Adding pwhash native and tests

This commit is contained in:
Ugljesa Jovanovic 2020-09-21 19:07:06 +02:00
parent 65bf54765f
commit 2b4d8aa4b1
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
4 changed files with 110 additions and 29 deletions

View File

@ -7,25 +7,25 @@ package com.ionspin.kotlin.crypto.pwhash
const val crypto_pwhash_BYTES_MIN = 16U const val crypto_pwhash_BYTES_MIN = 16U
const val crypto_pwhash_MEMLIMIT_INTERACTIVE = 67108864U const val crypto_pwhash_MEMLIMIT_INTERACTIVE = 67108864U
const val crypto_pwhash_MEMLIMIT_MIN = 8192U const val crypto_pwhash_MEMLIMIT_MIN = 8192
const val crypto_pwhash_MEMLIMIT_MODERATE = 268435456U const val crypto_pwhash_MEMLIMIT_MODERATE = 268435456
const val crypto_pwhash_MEMLIMIT_SENSITIVE = 1073741824U const val crypto_pwhash_MEMLIMIT_SENSITIVE = 1073741824
const val crypto_pwhash_OPSLIMIT_INTERACTIVE = 2U const val crypto_pwhash_OPSLIMIT_INTERACTIVE = 2
const val crypto_pwhash_OPSLIMIT_MAX = 4294967295U const val crypto_pwhash_OPSLIMIT_MAX = 4294967295UL
const val crypto_pwhash_OPSLIMIT_MIN = 1U const val crypto_pwhash_OPSLIMIT_MIN = 1UL
const val crypto_pwhash_OPSLIMIT_MODERATE = 3U const val crypto_pwhash_OPSLIMIT_MODERATE = 3UL
const val crypto_pwhash_OPSLIMIT_SENSITIVE = 4U const val crypto_pwhash_OPSLIMIT_SENSITIVE = 4UL
const val crypto_pwhash_PASSWD_MAX = 4294967295U const val crypto_pwhash_PASSWD_MAX = 4294967295
const val crypto_pwhash_PASSWD_MIN = 0U const val crypto_pwhash_PASSWD_MIN = 0
const val crypto_pwhash_SALTBYTES = 16U const val crypto_pwhash_SALTBYTES = 16
const val crypto_pwhash_STRBYTES = 128U const val crypto_pwhash_STRBYTES = 128
const val crypto_pwhash_STRPREFIX = "\$argon2id$" const val crypto_pwhash_STRPREFIX = "\$argon2id$"
val crypto_pwhash_argon2id_ALG_ARGON2ID13 = 2 val crypto_pwhash_argon2id_ALG_ARGON2ID13 = 2
val crypto_pwhash_argon2i_ALG_ARGON2I13 = 1 val crypto_pwhash_argon2i_ALG_ARGON2I13 = 1
val crypto_pwhash_ALG_DEFAULT = crypto_pwhash_argon2id_ALG_ARGON2ID13 val crypto_pwhash_ALG_DEFAULT = crypto_pwhash_argon2id_ALG_ARGON2ID13
class PasswordHashFailed() : RuntimeException("Password hashing failed") class PasswordHashingFailed() : RuntimeException("Password hashing failed")
expect object PasswordHash { expect object PasswordHash {
/** /**
@ -55,7 +55,7 @@ expect object PasswordHash {
* and other data stores. No extra information has to be stored in order to verify the password. * 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. * The function returns 0 on success and -1 if it didn't complete successfully.
*/ */
fun str(password: String, opslimit: ULong, memlimit: Int): String fun str(password: String, opslimit: ULong, memlimit: Int): UByteArray
/** /**
* Check if a password verification string str matches the parameters opslimit and memlimit, and the current default algorithm. * Check if a password verification string str matches the parameters opslimit and memlimit, and the current default algorithm.
@ -63,6 +63,13 @@ expect object PasswordHash {
* The function returns 0 if the parameters already match the given ones. * 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. * 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 strNeedsRehash(passwordHash: UByteArray, opslimit: ULong, memlimit: Int): Int
fun strVerify(passwordHash: String, password: UByteArray): Boolean
} /**
* his function verifies that str is a valid password verification string (as generated by crypto_pwhash_str()) for passwd whose length is passwdlen.
* str has to be zero-terminated.
* It returns 0 if the verification succeeds, and -1 on error.
*/
fun strVerify(passwordHash: UByteArray, password: String): Boolean
}

View File

@ -0,0 +1,57 @@
package com.ionspin.kotlin.crypto.pwhash
import com.ionspin.kotlin.crypto.util.toHexString
import kotlin.random.Random
import kotlin.random.nextUBytes
import kotlin.test.Test
import kotlin.test.assertTrue
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Sep-2020
*/
class PasswordHashTest {
@Test
fun testPasswordHash() {
val randomBytes = Random(0).nextUBytes(crypto_pwhash_SALTBYTES)
val password = "correct horse battery staple"
val hashedPassword = PasswordHash.pwhash(
64,
password,
randomBytes,
crypto_pwhash_OPSLIMIT_MIN,
crypto_pwhash_MEMLIMIT_MIN,
crypto_pwhash_ALG_DEFAULT
)
println("Hashed password: ${hashedPassword.toHexString()}")
}
@Test
fun testPasswordHashForStorage() {
val password = "correct horse battery staple"
val hashedPassword = PasswordHash.str(
password,
crypto_pwhash_OPSLIMIT_MIN,
crypto_pwhash_MEMLIMIT_MIN
)
println("Hashed password for storage: ${hashedPassword.toHexString()}")
assertTrue {
PasswordHash.strVerify(
hashedPassword,
password
)
}
assertTrue {
PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN) == 0
}
assertTrue {
PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_SENSITIVE) == 1
}
//TODO strNeedsRehash -1 case?
}
}

View File

@ -4,9 +4,11 @@ import com.ionspin.kotlin.crypto.util.toPtr
import kotlinx.cinterop.addressOf import kotlinx.cinterop.addressOf
import kotlinx.cinterop.convert import kotlinx.cinterop.convert
import kotlinx.cinterop.pin import kotlinx.cinterop.pin
import kotlinx.cinterop.toCValues
import libsodium.crypto_pwhash import libsodium.crypto_pwhash
import libsodium.crypto_pwhash_str import libsodium.crypto_pwhash_str
import libsodium.crypto_pwhash_str_needs_rehash import libsodium.crypto_pwhash_str_needs_rehash
import libsodium.crypto_pwhash_str_verify
actual object PasswordHash { actual object PasswordHash {
/** /**
@ -30,7 +32,7 @@ actual object PasswordHash {
val hashedPasswordPinned = hashedPassword.pin() val hashedPasswordPinned = hashedPassword.pin()
val saltPinned = salt.pin() val saltPinned = salt.pin()
crypto_pwhash( val hashingResult = crypto_pwhash(
hashedPasswordPinned.toPtr(), hashedPasswordPinned.toPtr(),
outputLength.convert(), outputLength.convert(),
password, password,
@ -42,6 +44,9 @@ actual object PasswordHash {
) )
saltPinned.unpin() saltPinned.unpin()
hashedPasswordPinned.unpin() hashedPasswordPinned.unpin()
if (hashingResult != 0) {
throw PasswordHashingFailed()
}
return hashedPassword return hashedPassword
} }
@ -56,8 +61,8 @@ actual object PasswordHash {
* and other data stores. No extra information has to be stored in order to verify the password. * 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. * The function returns 0 on success and -1 if it didn't complete successfully.
*/ */
actual fun str(password: String, opslimit: ULong, memlimit: Int): String { actual fun str(password: String, opslimit: ULong, memlimit: Int): UByteArray {
val output = ByteArray(crypto_pwhash_STRBYTES.toInt()) val output = ByteArray(crypto_pwhash_STRBYTES)
val outputPinned = output.pin() val outputPinned = output.pin()
crypto_pwhash_str( crypto_pwhash_str(
outputPinned.addressOf(0), outputPinned.addressOf(0),
@ -68,7 +73,7 @@ actual object PasswordHash {
) )
outputPinned.unpin() outputPinned.unpin()
return output.decodeToString() return output.asUByteArray()
} }
/** /**
@ -78,20 +83,32 @@ actual object PasswordHash {
* It returns -1 on error. If it happens, applications may want to compute a correct hash the next time the user logs in. * 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( actual fun strNeedsRehash(
password: String, passwordHash: UByteArray,
opslimit: ULong, opslimit: ULong,
memlimit: Int memlimit: Int
): Boolean { ): Int {
val password = password.encodeToByteArray() val result = crypto_pwhash_str_needs_rehash(
crypto_pwhash_str_needs_rehash( passwordHash.asByteArray().toCValues(),
,
opslimit, opslimit,
memlimit.convert() memlimit.convert()
) )
return result
} }
actual fun strVerify(passwordHash: String, password: UByteArray): Boolean { /**
TODO("Not yet implemented") * his function verifies that str is a valid password verification string (as generated by crypto_pwhash_str()) for passwd whose length is passwdlen.
* str has to be zero-terminated.
* It returns 0 if the verification succeeds, and -1 on error.
*/
actual fun strVerify(passwordHash: UByteArray, password: String): Boolean {
val result = crypto_pwhash_str_verify(
passwordHash.asByteArray().toCValues(),
password,
password.length.convert()
)
return result == 0
} }
} }

0
test Normal file
View File