diff --git a/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/pwhash/PasswordHash.kt b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/pwhash/PasswordHash.kt new file mode 100644 index 0000000..8fe6e9d --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/commonMain/kotlin/com.ionspin.kotlin.crypto/pwhash/PasswordHash.kt @@ -0,0 +1,68 @@ +package com.ionspin.kotlin.crypto.pwhash + +/** + * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 17/Sep/2020 + */ + + +const val crypto_pwhash_BYTES_MIN = 16U +const val crypto_pwhash_MEMLIMIT_INTERACTIVE = 67108864U +const val crypto_pwhash_MEMLIMIT_MIN = 8192U +const val crypto_pwhash_MEMLIMIT_MODERATE = 268435456U +const val crypto_pwhash_MEMLIMIT_SENSITIVE = 1073741824U +const val crypto_pwhash_OPSLIMIT_INTERACTIVE = 2U +const val crypto_pwhash_OPSLIMIT_MAX = 4294967295U +const val crypto_pwhash_OPSLIMIT_MIN = 1U +const val crypto_pwhash_OPSLIMIT_MODERATE = 3U +const val crypto_pwhash_OPSLIMIT_SENSITIVE = 4U +const val crypto_pwhash_PASSWD_MAX = 4294967295U +const val crypto_pwhash_PASSWD_MIN = 0U +const val crypto_pwhash_SALTBYTES = 16U +const val crypto_pwhash_STRBYTES = 128U +const val crypto_pwhash_STRPREFIX = "\$argon2id$" + +val crypto_pwhash_argon2id_ALG_ARGON2ID13 = 2 +val crypto_pwhash_argon2i_ALG_ARGON2I13 = 1 +val crypto_pwhash_ALG_DEFAULT = crypto_pwhash_argon2id_ALG_ARGON2ID13 + +class PasswordHashFailed() : RuntimeException("Password hashing failed") + +expect object PasswordHash { + /** + * The crypto_pwhash() function derives an outlen bytes long key from a password passwd whose length is passwdlen + * and a salt salt whose fixed length is crypto_pwhash_SALTBYTES bytes. passwdlen should be at least crypto_pwhash_ + * PASSWD_MIN and crypto_pwhash_PASSWD_MAX. outlen should be at least crypto_pwhash_BYTES_MIN = 16 (128 bits) and + * at most crypto_pwhash_BYTES_MAX. + * + * See https://libsodium.gitbook.io/doc/password_hashing/default_phf for more details + */ + fun pwhash( + outputLength: Int, + password: String, + salt: UByteArray, + opsLimit: ULong, + memLimit: Int, + algorithm: Int + ): UByteArray + + /** + * The crypto_pwhash_str() function puts an ASCII encoded string into out, which includes: + * the result of a memory-hard, CPU-intensive hash function applied to the password passwd of length passwdlen + * the automatically generated salt used for the previous computation + * the other parameters required to verify the password, including the algorithm identifier, its version, opslimit and memlimit. + * out must be large enough to hold crypto_pwhash_STRBYTES bytes, but the actual output string may be shorter. + * The output string is zero-terminated, includes only ASCII characters and can be safely stored into SQL databases + * and other data stores. No extra information has to be stored in order to verify the password. + * The function returns 0 on success and -1 if it didn't complete successfully. + */ + fun str(password: String, opslimit: ULong, memlimit: Int): String + + /** + * Check if a password verification string str matches the parameters opslimit and memlimit, and the current default algorithm. + * The function returns 1 if the string appears to be correct, but doesn't match the given parameters. In that situation, applications may want to compute a new hash using the current parameters the next time the user logs in. + * The function returns 0 if the parameters already match the given ones. + * It returns -1 on error. If it happens, applications may want to compute a correct hash the next time the user logs in. + */ + fun strNeedsRehash(password: String, opslimit: ULong, memlimit: Int): Boolean + fun strVerify(passwordHash: String, password: UByteArray): Boolean +} \ No newline at end of file diff --git a/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/pwhash/PasswordHash.kt b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/pwhash/PasswordHash.kt new file mode 100644 index 0000000..1e5bf37 --- /dev/null +++ b/multiplatform-crypto-libsodium-bindings/src/nativeMain/kotlin/com/ionspin/kotlin/crypto/pwhash/PasswordHash.kt @@ -0,0 +1,97 @@ +package com.ionspin.kotlin.crypto.pwhash + +import com.ionspin.kotlin.crypto.util.toPtr +import kotlinx.cinterop.addressOf +import kotlinx.cinterop.convert +import kotlinx.cinterop.pin +import libsodium.crypto_pwhash +import libsodium.crypto_pwhash_str +import libsodium.crypto_pwhash_str_needs_rehash + +actual object PasswordHash { + /** + * The crypto_pwhash() function derives an outlen bytes long key from a password passwd whose length is passwdlen + * and a salt salt whose fixed length is crypto_pwhash_SALTBYTES bytes. passwdlen should be at least crypto_pwhash_ + * PASSWD_MIN and crypto_pwhash_PASSWD_MAX. outlen should be at least crypto_pwhash_BYTES_MIN = 16 (128 bits) and + * at most crypto_pwhash_BYTES_MAX. + * + * See https://libsodium.gitbook.io/doc/password_hashing/default_phf for more details + */ + actual fun pwhash( + outputLength: Int, + password: String, + salt: UByteArray, + opsLimit: ULong, + memLimit: Int, + algorithm: Int + ) : UByteArray { + val hashedPassword = UByteArray(outputLength) + + val hashedPasswordPinned = hashedPassword.pin() + val saltPinned = salt.pin() + + crypto_pwhash( + hashedPasswordPinned.toPtr(), + outputLength.convert(), + password, + password.length.convert(), + saltPinned.toPtr(), + opsLimit, + memLimit.convert(), + algorithm + ) + saltPinned.unpin() + hashedPasswordPinned.unpin() + + return hashedPassword + } + + /** + * The crypto_pwhash_str() function puts an ASCII encoded string into out, which includes: + * the result of a memory-hard, CPU-intensive hash function applied to the password passwd of length passwdlen + * the automatically generated salt used for the previous computation + * the other parameters required to verify the password, including the algorithm identifier, its version, opslimit and memlimit. + * out must be large enough to hold crypto_pwhash_STRBYTES bytes, but the actual output string may be shorter. + * The output string is zero-terminated, includes only ASCII characters and can be safely stored into SQL databases + * and other data stores. No extra information has to be stored in order to verify the password. + * The function returns 0 on success and -1 if it didn't complete successfully. + */ + actual fun str(password: String, opslimit: ULong, memlimit: Int): String { + val output = ByteArray(crypto_pwhash_STRBYTES.toInt()) + val outputPinned = output.pin() + crypto_pwhash_str( + outputPinned.addressOf(0), + password, + password.length.convert(), + opslimit, + memlimit.convert() + ) + outputPinned.unpin() + + return output.decodeToString() + } + + /** + * Check if a password verification string str matches the parameters opslimit and memlimit, and the current default algorithm. + * The function returns 1 if the string appears to be correct, but doesn't match the given parameters. In that situation, applications may want to compute a new hash using the current parameters the next time the user logs in. + * The function returns 0 if the parameters already match the given ones. + * It returns -1 on error. If it happens, applications may want to compute a correct hash the next time the user logs in. + */ + actual fun strNeedsRehash( + password: String, + opslimit: ULong, + memlimit: Int + ): Boolean { + val password = password.encodeToByteArray() + crypto_pwhash_str_needs_rehash( + , + opslimit, + memlimit.convert() + ) + } + + actual fun strVerify(passwordHash: String, password: UByteArray): Boolean { + TODO("Not yet implemented") + } + +} \ No newline at end of file