Added java implementation of _pwhash_

This commit is contained in:
Ugljesa Jovanovic 2020-09-21 22:13:36 +02:00
parent 2b4d8aa4b1
commit fe4134a65f
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
2 changed files with 134 additions and 30 deletions

View File

@ -1,5 +1,6 @@
package com.ionspin.kotlin.crypto.pwhash package com.ionspin.kotlin.crypto.pwhash
import com.ionspin.kotlin.crypto.LibsodiumInitializer
import com.ionspin.kotlin.crypto.util.toHexString import com.ionspin.kotlin.crypto.util.toHexString
import kotlin.random.Random import kotlin.random.Random
import kotlin.random.nextUBytes import kotlin.random.nextUBytes
@ -14,44 +15,53 @@ import kotlin.test.assertTrue
class PasswordHashTest { class PasswordHashTest {
@Test @Test
fun testPasswordHash() { fun testPasswordHash() {
val randomBytes = Random(0).nextUBytes(crypto_pwhash_SALTBYTES) LibsodiumInitializer.initializeWithCallback {
val password = "correct horse battery staple" val randomBytes = Random(0).nextUBytes(crypto_pwhash_SALTBYTES)
val hashedPassword = PasswordHash.pwhash( val password = "correct horse battery staple"
64, val hashedPassword = PasswordHash.pwhash(
password, 64,
randomBytes, password,
crypto_pwhash_OPSLIMIT_MIN, randomBytes,
crypto_pwhash_MEMLIMIT_MIN, crypto_pwhash_OPSLIMIT_MIN,
crypto_pwhash_ALG_DEFAULT crypto_pwhash_MEMLIMIT_MIN,
) crypto_pwhash_ALG_DEFAULT
println("Hashed password: ${hashedPassword.toHexString()}") )
println("Hashed password: ${hashedPassword.toHexString()}")
assertTrue {
hashedPassword.toHexString().equals("e762ee529e90e3bbc242c23e8e2f963ab9a17ed9e79f89a00c71261a979207b2213cc" +
"0330c53f410a9c8933c46e8642dc542efc0660c69e255b601c7244ef6b0")
}
}
} }
@Test @Test
fun testPasswordHashForStorage() { 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 { LibsodiumInitializer.initializeWithCallback {
PasswordHash.strVerify( val password = "correct horse battery staple"
hashedPassword, val hashedPassword = PasswordHash.str(
password password,
crypto_pwhash_OPSLIMIT_MIN,
crypto_pwhash_MEMLIMIT_MIN
) )
} println("Hashed password for storage: ${hashedPassword.toHexString()}")
assertTrue { assertTrue {
PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN) == 0 PasswordHash.strVerify(
} hashedPassword,
password
)
}
assertTrue { assertTrue {
PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_SENSITIVE) == 1 PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_MIN) == 0
} }
//TODO strNeedsRehash -1 case? assertTrue {
PasswordHash.strNeedsRehash(hashedPassword, crypto_pwhash_OPSLIMIT_MIN, crypto_pwhash_MEMLIMIT_SENSITIVE) == 1
}
//TODO strNeedsRehash -1 case?
}
} }
} }

View File

@ -0,0 +1,94 @@
package com.ionspin.kotlin.crypto.pwhash
import com.ionspin.kotlin.crypto.LibsodiumInitializer.sodium
import com.sun.jna.NativeLong
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)
sodium.crypto_pwhash(
hashedPassword.asByteArray(),
outputLength.toLong(),
password.encodeToByteArray(),
password.length.toLong(),
salt.asByteArray(),
opsLimit.toLong(),
NativeLong(memLimit.toLong()),
algorithm
)
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): UByteArray {
val output = ByteArray(crypto_pwhash_STRBYTES)
sodium.crypto_pwhash_str(
output,
password.encodeToByteArray(),
password.length.toLong(),
opslimit.toLong(),
NativeLong(memlimit.toLong())
)
return output.asUByteArray()
}
/**
* 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(
passwordHash: UByteArray,
opslimit: ULong,
memlimit: Int
): Int {
return sodium.crypto_pwhash_str_needs_rehash(
passwordHash.asByteArray(),
opslimit.toLong(),
NativeLong(memlimit.toLong())
)
}
/**
* 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 = sodium.crypto_pwhash_str_verify(
passwordHash.asByteArray(),
password.encodeToByteArray(),
password.length.toLong()
)
return result == 0
}
}