diff --git a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/kdf/Kdf.kt b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/kdf/Kdf.kt new file mode 100644 index 0000000..4c64812 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/kdf/Kdf.kt @@ -0,0 +1,33 @@ +package com.ionspin.kotlin.crypto.kdf + +/** + * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 16/Sep/2020 + */ +const val crypto_kdf_PRIMITIVE = "blake2b" +const val crypto_kdf_BYTES_MIN = 16 +const val crypto_kdf_BYTES_MAX = 64 +const val crypto_kdf_CONTEXTBYTES = 8 +const val crypto_kdf_KEYBYTES = 32 + +expect object Kdf { + /** + * The crypto_kdf_derive_from_key() function derives a subkey_id-th subkey subkey of length subkey_len bytes using + * the master key key and the context ctx. + * subkey_id can be any value up to (2^64)-1. + * subkey_len has to be between crypto_kdf_BYTES_MIN (inclusive) and crypto_kdf_BYTES_MAX (inclusive). + * Similar to a type, the context ctx is a 8 characters string describing what the key is going to be used for. + * Its purpose is to mitigate accidental bugs by separating domains. The same function used with the same key but + * in two distinct contexts is likely to generate two different outputs. + * Contexts don't have to be secret and can have a low entropy. + * Examples of contexts include UserName, __auth__, pictures and userdata. + * They must be crypto_kdf_CONTEXTBYTES bytes long. + * If more convenient, it is also fine to use a single global context for a whole application. This will still + * prevent the same keys from being mistakenly used by another application. + */ + fun deriveFromKey(subkeyId: Int, subkeyLength: Int, context: String, masterKey: UByteArray) : UByteArray + + /** + * The crypto_kdf_keygen() function creates a master key. + */ + fun keygen() : UByteArray +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/kdf/KdfTest.kt b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/kdf/KdfTest.kt new file mode 100644 index 0000000..06c4f99 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonTest/kotlin/com/ionspin/kotlin/crypto/kdf/KdfTest.kt @@ -0,0 +1,34 @@ +package com.ionspin.kotlin.crypto.kdf + +import com.ionspin.kotlin.crypto.LibsodiumInitializer +import kotlin.test.Test +import kotlin.test.assertFails +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 16/Sep/2020 + */ +class KdfTest { + @Test + fun testKdf() { + LibsodiumInitializer.initializeWithCallback { + val masterKey = Kdf.keygen() + val subkey1 = Kdf.deriveFromKey(1, crypto_kdf_BYTES_MAX, "test1234", masterKey) + val subkey2 = Kdf.deriveFromKey(2, crypto_kdf_BYTES_MAX, "test1234", masterKey) + + assertTrue { + subkey1.size == crypto_kdf_BYTES_MAX && + subkey2.size == crypto_kdf_BYTES_MAX + } + + val repeatSubkey1 = Kdf.deriveFromKey(1, crypto_kdf_BYTES_MAX, "test1234", masterKey) + assertTrue { + subkey1.contentEquals(repeatSubkey1) + } + assertFalse { + subkey1.contentEquals(subkey2) + } + } + } +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt index d7caf7d..d97fe22 100644 --- a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/JsSodiumInterface.kt @@ -174,7 +174,15 @@ interface JsSodiumInterface { fun crypto_sign_verify_detached(signature: Uint8Array, message: Uint8Array, publicKey: Uint8Array) : Boolean - // ---- Sign end + // ---- Sign end ---- + + + // ---- KDF ---- + + fun crypto_kdf_derive_from_key(subkey_len: UInt, subkeyId : UInt, ctx: String, key: Uint8Array) : Uint8Array + fun crypto_kdf_keygen() : Uint8Array + + // ---- KDF end ----- //util fun memzero(array: Uint8Array) diff --git a/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt new file mode 100644 index 0000000..1866909 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jsMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt @@ -0,0 +1,43 @@ +package com.ionspin.kotlin.crypto.kdf + +import com.ionspin.kotlin.crypto.getSodium +import ext.libsodium.com.ionspin.kotlin.crypto.toUByteArray +import ext.libsodium.com.ionspin.kotlin.crypto.toUInt8Array + +actual object Kdf { + /** + * The crypto_kdf_derive_from_key() function derives a subkey_id-th subkey subkey of length subkey_len bytes using + * the master key key and the context ctx. + * subkey_id can be any value up to (2^64)-1. + * subkey_len has to be between crypto_kdf_BYTES_MIN (inclusive) and crypto_kdf_BYTES_MAX (inclusive). + * Similar to a type, the context ctx is a 8 characters string describing what the key is going to be used for. + * Its purpose is to mitigate accidental bugs by separating domains. The same function used with the same key but + * in two distinct contexts is likely to generate two different outputs. + * Contexts don't have to be secret and can have a low entropy. + * Examples of contexts include UserName, __auth__, pictures and userdata. + * They must be crypto_kdf_CONTEXTBYTES bytes long. + * If more convenient, it is also fine to use a single global context for a whole application. This will still + * prevent the same keys from being mistakenly used by another application. + */ + actual fun deriveFromKey( + subkeyId: Int, + subkeyLength: Int, + context: String, + masterKey: UByteArray + ): UByteArray { + return getSodium().crypto_kdf_derive_from_key( + subkeyLength.toUInt(), + subkeyId.toUInt(), + context, + masterKey.toUInt8Array() + ).toUByteArray() + } + + /** + * The crypto_kdf_keygen() function creates a master key. + */ + actual fun keygen(): UByteArray { + return getSodium().crypto_kdf_keygen().toUByteArray() + } + +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt new file mode 100644 index 0000000..7df70a6 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/jvmMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt @@ -0,0 +1,49 @@ +package com.ionspin.kotlin.crypto.kdf + +import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodium + +actual object Kdf { + /** + * The crypto_kdf_derive_from_key() function derives a subkey_id-th subkey subkey of length subkey_len bytes using + * the master key key and the context ctx. + * subkey_id can be any value up to (2^64)-1. + * subkey_len has to be between crypto_kdf_BYTES_MIN (inclusive) and crypto_kdf_BYTES_MAX (inclusive). + * Similar to a type, the context ctx is a 8 characters string describing what the key is going to be used for. + * Its purpose is to mitigate accidental bugs by separating domains. The same function used with the same key but + * in two distinct contexts is likely to generate two different outputs. + * Contexts don't have to be secret and can have a low entropy. + * Examples of contexts include UserName, __auth__, pictures and userdata. + * They must be crypto_kdf_CONTEXTBYTES bytes long. + * If more convenient, it is also fine to use a single global context for a whole application. This will still + * prevent the same keys from being mistakenly used by another application. + */ + actual fun deriveFromKey( + subkeyId: Int, + subkeyLength: Int, + context: String, + masterKey: UByteArray + ): UByteArray { + val subkey = UByteArray(subkeyLength) + val contextEncoded = context.encodeToByteArray() + + sodium.crypto_kdf_derive_from_key( + subkey.asByteArray(), + subkeyLength, + subkeyId.toLong(), + contextEncoded, + masterKey.asByteArray() + ) + + return subkey + } + + /** + * The crypto_kdf_keygen() function creates a master key. + */ + actual fun keygen(): UByteArray { + val masterKey = UByteArray(crypto_kdf_KEYBYTES) + sodium.crypto_kdf_keygen(masterKey.asByteArray()) + return masterKey + } + +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt new file mode 100644 index 0000000..972ce15 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/kdf/Kdf.kt @@ -0,0 +1,66 @@ +package com.ionspin.kotlin.crypto.kdf + +import com.ionspin.kotlin.crypto.util.encodeToUByteArray +import com.ionspin.kotlin.crypto.util.toPtr +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.convert +import kotlinx.cinterop.pin +import libsodium.crypto_kdf_derive_from_key +import libsodium.crypto_kdf_keygen + +actual object Kdf { + /** + * The crypto_kdf_derive_from_key() function derives a subkey_id-th subkey subkey of length subkey_len bytes using + * the master key key and the context ctx. + * subkey_id can be any value up to (2^64)-1. + * subkey_len has to be between crypto_kdf_BYTES_MIN (inclusive) and crypto_kdf_BYTES_MAX (inclusive). + * Similar to a type, the context ctx is a 8 characters string describing what the key is going to be used for. + * Its purpose is to mitigate accidental bugs by separating domains. The same function used with the same key but + * in two distinct contexts is likely to generate two different outputs. + * Contexts don't have to be secret and can have a low entropy. + * Examples of contexts include UserName, __auth__, pictures and userdata. + * They must be crypto_kdf_CONTEXTBYTES bytes long. + * If more convenient, it is also fine to use a single global context for a whole application. This will still + * prevent the same keys from being mistakenly used by another application. + */ + actual fun deriveFromKey( + subkeyId: Int, + subkeyLength: Int, + context: String, + masterKey: UByteArray + ): UByteArray { + val subkey = UByteArray(subkeyLength) + val contextEncoded = context.encodeToByteArray() + + val contextEncodedAndPinned = contextEncoded.pin() + val masterKeyPinned = masterKey.pin() + val subkeyPinned = subkey.pin() + crypto_kdf_derive_from_key( + subkeyPinned.toPtr(), + subkeyLength.convert(), + subkeyId.convert(), + contextEncodedAndPinned.addressOf(0), + masterKeyPinned.toPtr() + ) + + contextEncodedAndPinned.unpin() + masterKeyPinned.unpin() + subkeyPinned.unpin() + + return subkey + + + } + + /** + * The crypto_kdf_keygen() function creates a master key. + */ + actual fun keygen(): UByteArray { + val masterKey = UByteArray(crypto_kdf_KEYBYTES) + val masterKeyPinned = masterKey.pin() + crypto_kdf_keygen(masterKeyPinned.toPtr()) + masterKeyPinned.unpin() + return masterKey + } + +} \ No newline at end of file diff --git a/supported_bindings_list.md b/supported_bindings_list.md index 7125753..5f15aa3 100644 --- a/supported_bindings_list.md +++ b/supported_bindings_list.md @@ -48,19 +48,19 @@ | crypto_box_seal | :heavy_check_mark: | | crypto_box_seal_open | :heavy_check_mark: | | crypto_box_seed_keypair | :heavy_check_mark: | -| crypto_core_ristretto255_add | | -| crypto_core_ristretto255_from_hash | | -| crypto_core_ristretto255_is_valid_point | | -| crypto_core_ristretto255_random | | -| crypto_core_ristretto255_scalar_add | | -| crypto_core_ristretto255_scalar_complement | | -| crypto_core_ristretto255_scalar_invert | | -| crypto_core_ristretto255_scalar_mul | | -| crypto_core_ristretto255_scalar_negate | | -| crypto_core_ristretto255_scalar_random | | -| crypto_core_ristretto255_scalar_reduce | | -| crypto_core_ristretto255_scalar_sub | | -| crypto_core_ristretto255_sub | | +| crypto_core_ristretto255_add | not present in LazySodium | +| crypto_core_ristretto255_from_hash | not present in LazySodium | +| crypto_core_ristretto255_is_valid_point | not present in LazySodium | +| crypto_core_ristretto255_random | not present in LazySodium | +| crypto_core_ristretto255_scalar_add | not present in LazySodium | +| crypto_core_ristretto255_scalar_complement | not present in LazySodium | +| crypto_core_ristretto255_scalar_invert | not present in LazySodium | +| crypto_core_ristretto255_scalar_mul | not present in LazySodium | +| crypto_core_ristretto255_scalar_negate | not present in LazySodium | +| crypto_core_ristretto255_scalar_random | not present in LazySodium | +| crypto_core_ristretto255_scalar_reduce | not present in LazySodium | +| crypto_core_ristretto255_scalar_sub | not present in LazySodium | +| crypto_core_ristretto255_sub | not present in LazySodium | | crypto_generichash | :heavy_check_mark: | | crypto_generichash_blake2b_salt_personal | | | crypto_generichash_final | :heavy_check_mark: | @@ -76,23 +76,23 @@ | crypto_hash_sha512_final | :heavy_check_mark: | | crypto_hash_sha512_init | :heavy_check_mark: | | crypto_hash_sha512_update | :heavy_check_mark: | -| crypto_kdf_derive_from_key | | -| crypto_kdf_keygen | | +| crypto_kdf_derive_from_key | :heavy_check_mark: | +| crypto_kdf_keygen | :heavy_check_mark: | | crypto_kx_client_session_keys | | | crypto_kx_keypair | | | crypto_kx_seed_keypair | | | crypto_kx_server_session_keys | | -| crypto_onetimeauth | | -| crypto_onetimeauth_final | | -| crypto_onetimeauth_init | | -| crypto_onetimeauth_keygen | | -| crypto_onetimeauth_update | | -| crypto_onetimeauth_verify | | +| crypto_onetimeauth | not present in LazySodium | +| crypto_onetimeauth_final | not present in LazySodium | +| crypto_onetimeauth_init | not present in LazySodium | +| crypto_onetimeauth_keygen | not present in LazySodium | +| crypto_onetimeauth_update | not present in LazySodium | +| crypto_onetimeauth_verify | not present in LazySodium | | crypto_pwhash | | -| crypto_pwhash_scryptsalsa208sha256 | | -| crypto_pwhash_scryptsalsa208sha256_ll | | -| crypto_pwhash_scryptsalsa208sha256_str | | -| crypto_pwhash_scryptsalsa208sha256_str_verify | | +| crypto_pwhash_scryptsalsa208sha256 | not present in LazySodium for Android | +| crypto_pwhash_scryptsalsa208sha256_ll | not present in LazySodium for Android | +| crypto_pwhash_scryptsalsa208sha256_str | not present in LazySodium for Android | +| crypto_pwhash_scryptsalsa208sha256_str_verify | not present in LazySodium for Android | | crypto_pwhash_str | | | crypto_pwhash_str_needs_rehash | | | crypto_pwhash_str_verify | | @@ -135,9 +135,9 @@ | crypto_stream_chacha20_xor | | | crypto_stream_chacha20_xor_ic | | | crypto_stream_keygen | | -| crypto_stream_xchacha20_keygen | | -| crypto_stream_xchacha20_xor | | -| crypto_stream_xchacha20_xor_ic | | +| crypto_stream_xchacha20_keygen | not present in LazySodium Android | +| crypto_stream_xchacha20_xor | not present in LazySodium Android| +| crypto_stream_xchacha20_xor_ic | not present in LazySodium Android | | randombytes_buf | | | randombytes_buf_deterministic | | | randombytes_close | |