Dropping AES gcm from public api as it's not portable in libsodium and going the xchacha20poly1305 as only AEAD
This commit is contained in:
parent
5c10d3abf4
commit
e24f0a29f2
59
README.md
59
README.md
@ -71,7 +71,7 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be
|
||||
|
||||
### Authenticated symmetric encryption (AEAD)
|
||||
|
||||
* TODO
|
||||
* XChaCha20-Poly1305
|
||||
|
||||
|
||||
### Delegated flavor dependancy table
|
||||
@ -82,6 +82,7 @@ The following table describes which library is used for particular cryptographic
|
||||
| Blake2b | LazySodium | libsodium.js | libsodium |
|
||||
| SHA256 | LazySodium | libsodium.js | libsodium |
|
||||
| SHA512 | LazySodium | libsodium.js | libsodium |
|
||||
| XChaCha20-Poly1305 | LazySodium | libsodium.js | libsodium |
|
||||
|
||||
|
||||
|
||||
@ -190,7 +191,34 @@ sha512.update("abc".encodeToUByteArray())
|
||||
val result = sha512.digest()
|
||||
```
|
||||
|
||||
### Symmetric encryption
|
||||
### Key derivation
|
||||
|
||||
#### Argon2
|
||||
|
||||
NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow
|
||||
specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183
|
||||
|
||||
```kotlin
|
||||
val argon2Instance = Argon2(
|
||||
password = "Password",
|
||||
salt = "RandomSalt",
|
||||
parallelism = 8,
|
||||
tagLength = 64U,
|
||||
requestedMemorySize = 256U, //4GB
|
||||
numberOfIterations = 4U,
|
||||
key = "",
|
||||
associatedData = "",
|
||||
argonType = ArgonType.Argon2id
|
||||
)
|
||||
val tag = argon2Instance.derive()
|
||||
val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "")
|
||||
val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" +
|
||||
"0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37"
|
||||
println("Tag: ${tagString}")
|
||||
assertEquals(tagString, expectedTagString)
|
||||
```
|
||||
|
||||
### Symmetric encryption (OUTDATED, won't be exposed in next release, no counterpart in delegated flavor - 0.10.1)
|
||||
|
||||
#### AES
|
||||
|
||||
@ -236,33 +264,6 @@ plainText == decrypted.toHexString()
|
||||
|
||||
```
|
||||
|
||||
### Key derivation
|
||||
|
||||
#### Argon2
|
||||
|
||||
NOTE: This implementation is tested against KAT generated by reference Argon2 implementation, which does not follow
|
||||
specification completely. See this issue https://github.com/P-H-C/phc-winner-argon2/issues/183
|
||||
|
||||
```kotlin
|
||||
val argon2Instance = Argon2(
|
||||
password = "Password",
|
||||
salt = "RandomSalt",
|
||||
parallelism = 8,
|
||||
tagLength = 64U,
|
||||
requestedMemorySize = 256U, //4GB
|
||||
numberOfIterations = 4U,
|
||||
key = "",
|
||||
associatedData = "",
|
||||
argonType = ArgonType.Argon2id
|
||||
)
|
||||
val tag = argon2Instance.derive()
|
||||
val tagString = tag.map { it.toString(16).padStart(2, '0') }.joinToString(separator = "")
|
||||
val expectedTagString = "c255e3e94305817d5e09a7c771e574e3a81cc78fef5da4a9644b6df0" +
|
||||
"0ba1c9b424e3dd0ce7e600b1269b14c84430708186a8a60403e1bfbda935991592b9ff37"
|
||||
println("Tag: ${tagString}")
|
||||
assertEquals(tagString, expectedTagString)
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -6,10 +6,10 @@ package com.ionspin.kotlin.crypto.authenticated
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
interface Aes256GcmStateless {
|
||||
/**
|
||||
* Nonce autogenerated, key autogenerated
|
||||
*/
|
||||
fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray) : Aes256GcmEncryptionResult
|
||||
|
||||
fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray, key: Aes256GcmKey) : Aes256GcmEncryptionResult
|
||||
|
||||
fun decrypt(encryptedData: UByteArray, nonce: UByteArray, key : Aes256GcmKey) : UByteArray
|
||||
}
|
||||
|
||||
data class Aes256GcmEncryptionResult(val cyphertext : UByteArray, val additionalData: UByteArray, val nonce: UByteArray, val tag: UByteArray) {
|
||||
@ -35,3 +35,8 @@ data class Aes256GcmEncryptionResult(val cyphertext : UByteArray, val additional
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface Aes256GcmKey {
|
||||
val key : UByteArray
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
interface XChaCha20Poly1305
|
@ -0,0 +1,47 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
interface XChaCha20 {
|
||||
interface Nonce {
|
||||
val content: UByteArray
|
||||
}
|
||||
|
||||
interface Key {
|
||||
val content : UByteArray
|
||||
}
|
||||
fun encrypt(key: Key, inputMessage: UByteArray) : XChaCha20EncryptionResult
|
||||
|
||||
fun decrypt(key: Key, nonce: Nonce) : UByteArray
|
||||
}
|
||||
|
||||
data class XChaCha20EncryptionResult(val nonce: UByteArray, val encryptionData: UByteArray) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || this::class != other::class) return false
|
||||
|
||||
other as XChaCha20EncryptionResult
|
||||
|
||||
if (nonce != other.nonce) return false
|
||||
if (encryptionData != other.encryptionData) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = nonce.hashCode()
|
||||
result = 31 * result + encryptionData.hashCode()
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
interface XChaCha20KeyProvider {
|
||||
fun generateNewKey() : XChaCha20.Key
|
||||
|
||||
fun createFromUByteArray(uByteArray: UByteArray) : XChaCha20.Key
|
||||
}
|
@ -1,18 +1,24 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
import com.ionspin.kotlin.crypto.SRNG
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
class Aes256GcmStatelessPure : Aes256GcmStateless {
|
||||
internal class Aes256GcmStatelessPure : Aes256GcmStateless {
|
||||
/**
|
||||
* Nonce autogenerated
|
||||
*/
|
||||
override fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray, key:) : Aes256GcmEncryptionResult {
|
||||
override fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray, key: Aes256GcmKey) : Aes256GcmEncryptionResult {
|
||||
|
||||
TODO()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
override fun decrypt(encryptedData: UByteArray, nonce: UByteArray, key: Aes256GcmKey): UByteArray {
|
||||
TODO("not implemented yet")
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,7 @@
|
||||
package com.ionspin.kotlin.crypto.authenticated
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
@ -0,0 +1,31 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.*
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
class Salsa20 {
|
||||
companion object {
|
||||
fun coreHash(input: UByteArray) : UByteArray {
|
||||
val y0 = input.fromBigEndianArrayToUintWithPosition(0)
|
||||
val y1 = input.fromBigEndianArrayToUintWithPosition(4)
|
||||
val y2 = input.fromBigEndianArrayToUintWithPosition(8)
|
||||
val y3 = input.fromBigEndianArrayToUintWithPosition(12);
|
||||
|
||||
val z1 = y1 xor ((y0 + y3) rotateLeft 7)
|
||||
val z2 = y2 xor ((z1 + y0) rotateLeft 9)
|
||||
val z3 = y3 xor ((z2 + z1) rotateLeft 13)
|
||||
val z0 = y0 xor ((z3 + z2) rotateLeft 18)
|
||||
val result = UByteArray(16)
|
||||
result.insertUIntAtPositionAsBigEndian(0, z0)
|
||||
result.insertUIntAtPositionAsBigEndian(4, z1)
|
||||
result.insertUIntAtPositionAsBigEndian(8, z2)
|
||||
result.insertUIntAtPositionAsBigEndian(12, z3)
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
class XChaCha20Pure {
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
val chachaState = UByteArray(64)
|
||||
|
||||
fun quarterRound() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,8 @@
|
||||
|
||||
package com.ionspin.kotlin.crypto.util
|
||||
|
||||
import com.ionspin.kotlin.crypto.keyderivation.argon2.ArgonBlockPointer
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
@ -72,6 +74,14 @@ infix fun ULong.rotateRight(places: Int): ULong {
|
||||
return (this shr places) xor (this shl (64 - places))
|
||||
}
|
||||
|
||||
infix fun UInt.rotateLeft(places: Int): UInt {
|
||||
return (this shl places) xor (this shr (32 - places))
|
||||
}
|
||||
|
||||
|
||||
infix fun ULong.rotateLeft(places: Int): ULong {
|
||||
return (this shl places) xor (this shr (64 - places))
|
||||
}
|
||||
|
||||
infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
|
||||
if (this.size != other.size) {
|
||||
@ -231,9 +241,33 @@ fun UByteArray.fromLittleEndianArrayToUInt() : UInt {
|
||||
return uint
|
||||
}
|
||||
|
||||
fun UByteArray.fromLittleEndianArrayToUintWithPosition(position: Int) : UInt{
|
||||
var uint = 0U
|
||||
for (i in 0 until 4) {
|
||||
uint = uint or (this[position + i].toUInt() shl (i * 8))
|
||||
}
|
||||
return uint
|
||||
}
|
||||
|
||||
fun UByteArray.fromBigEndianArrayToUintWithPosition(position: Int) : UInt{
|
||||
var uint = 0U
|
||||
for (i in 0 until 4) {
|
||||
uint = uint shl 8 or (this[position + i].toUInt())
|
||||
}
|
||||
return uint
|
||||
}
|
||||
|
||||
fun UByteArray.insertUIntAtPositionAsLittleEndian(position: Int, value: UInt) {
|
||||
for (i in position until position + 4) {
|
||||
this[i] = ((value shr (i * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
fun UByteArray.insertUIntAtPositionAsBigEndian(position: Int, value: UInt) {
|
||||
for (i in position until position + 4) {
|
||||
this[i] = ((value shr (24 - i * 8)) and 0xFFU).toUByte()
|
||||
}
|
||||
}
|
||||
|
||||
fun Array<UByte>.fromBigEndianArrayToUInt() : UInt {
|
||||
if (this.size > 4) {
|
||||
|
@ -43,7 +43,7 @@ class AesCbcTest {
|
||||
println("Encrypted: ${encrypted.encryptedData.toHexString()}")
|
||||
|
||||
expectedCipherText == encrypted.encryptedData.toHexString() &&
|
||||
iv == encrypted.initilizationVector.toHexString()
|
||||
iv == encrypted.initializationVector.toHexString()
|
||||
}
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ class AesCbcTest {
|
||||
val decrypted = AesCbcPure.decrypt(
|
||||
key,
|
||||
encryptedDataAndInitializationVector.encryptedData,
|
||||
encryptedDataAndInitializationVector.initilizationVector
|
||||
encryptedDataAndInitializationVector.initializationVector
|
||||
)
|
||||
plainText == decrypted.toHexString()
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
package com.ionspin.kotlin.crypto.symmetric
|
||||
|
||||
import com.ionspin.kotlin.crypto.util.hexStringToUByteArray
|
||||
import com.ionspin.kotlin.crypto.util.rotateLeft
|
||||
import com.ionspin.kotlin.crypto.util.toHexString
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* Created by Ugljesa Jovanovic
|
||||
* ugljesa.jovanovic@ionspin.com
|
||||
* on 14-Jun-2020
|
||||
*/
|
||||
class Salsa20Test {
|
||||
|
||||
@Test
|
||||
fun testRotateLeft() {
|
||||
val a = 0xc0a8787eU
|
||||
val b = a rotateLeft 5
|
||||
val expected = 0x150f0fd8U
|
||||
assertEquals(b, expected)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCoreHash() {
|
||||
assertTrue {
|
||||
val input = "00000000000000000000000000000000".hexStringToUByteArray()
|
||||
val expected = "00000000000000000000000000000000".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val input = "00000001000000000000000000000000".hexStringToUByteArray()
|
||||
val expected = "08008145000000800001020020500000".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val input = "00000000000000010000000000000000".hexStringToUByteArray()
|
||||
val expected = "88000100000000010000020000402000".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val input = "00000000000000000000000100000000".hexStringToUByteArray()
|
||||
val expected = "80040000000000000000000100002000".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
|
||||
assertTrue {
|
||||
val input = "00000000000000000000000000000001".hexStringToUByteArray()
|
||||
val expected = "00048044000000800001000020100001".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
|
||||
|
||||
assertTrue {
|
||||
val input = "d3917c5b55f1c40752a58a7a8f887a3b".hexStringToUByteArray()
|
||||
val expected = "3e2f308cd90a8f366ab2a9232883524c".hexStringToUByteArray()
|
||||
val result = Salsa20.coreHash(input)
|
||||
println("Result ${result.toHexString()}")
|
||||
|
||||
expected.contentEquals(result)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user