forked from sergeych/crypto2
+hash salt derivation for any size. better naming
This commit is contained in:
1 parent
9f7babdf58
commit
10ec58ec08
9 files changed
+88
-19
No files matched your search
@@ -111,6 +111,34 @@ enum class Hash(
|
||||
for (block in source) sp.update(block)
|
||||
return sp.final()
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a salt of any size from a text. ___Salt could not be used as a password key___ source as it is not
|
||||
* strong to brute force, but it is good when you just need to get a deterministic salt of arbitrary size.
|
||||
*
|
||||
* _Note that deriving salt of sizes less than hash block will reduce hash strength and should not be allowed
|
||||
* in situation where strength is of concern while extending its length above hash block size does not improve
|
||||
* it_. The only reason to use this function is when the desired _salt size_ is not equal to block size, or
|
||||
* not known beforehand.1
|
||||
*
|
||||
* To get a cryptographically safe (more or less) key from password use [KDF] classes, or [KDF.deriveKey]
|
||||
* and [KDF.deriveMultipleKeys].
|
||||
*/
|
||||
fun deriveSalt(base: String, sizeInBytes: Int): UByteArray {
|
||||
require(sizeInBytes > 0)
|
||||
val result = mutableListOf<UByte>()
|
||||
var round = 0
|
||||
var src = base.encodeToUByteArray()
|
||||
do {
|
||||
src = "rnd_${round++}_".encodeToUByteArray() + src
|
||||
val hash = digest(src)
|
||||
if (result.size + hash.size <= sizeInBytes) result += hash
|
||||
else result += hash.slice(0..<(sizeInBytes - result.size))
|
||||
} while (result.size < sizeInBytes)
|
||||
return result.toUByteArray()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private val defaultSuffix1 = "All lay loads on a willing horse".encodeToUByteArray()
|
||||
|
||||
@@ -114,7 +114,7 @@ object PBKD {
|
||||
}
|
||||
return entry.op {
|
||||
if (entry.data == null) {
|
||||
entry.data = entry.kdf.derive(password)
|
||||
entry.data = entry.kdf.deriveKey(password)
|
||||
}
|
||||
entry.data!!
|
||||
}
|
||||
|
||||
@@ -62,28 +62,34 @@ class UniversalRing(
|
||||
* Get all keys for the specified id (normally it could be 0, 1 or 2). See [KeyId] about
|
||||
* matching id keys.
|
||||
*/
|
||||
fun keysById(id: KeyId): List<UniversalKey> = allKeys.filter { it.id == id }
|
||||
fun findById(id: KeyId): List<UniversalKey> = allKeys.filter { it.id == id }
|
||||
|
||||
@Deprecated("please replace", replaceWith = ReplaceWith("findById"))
|
||||
fun keysById(id: KeyId) = findById(id)
|
||||
|
||||
/**
|
||||
* Return sequence of keys that have at least one of the [tags]
|
||||
*/
|
||||
fun keysByTags(vararg tags: String) = sequence {
|
||||
fun findByTags(vararg tags: String) = sequence {
|
||||
for (e in keyWithTags.entries) {
|
||||
if (tags.any { it in e.value }) yield(e.key)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("please replace", replaceWith = ReplaceWith("findByTag"))
|
||||
fun keysByTag(vararg tags: String) = findByTags(*tags)
|
||||
|
||||
/**
|
||||
* Get the first key of the specified type having the [tag]
|
||||
*/
|
||||
inline fun <reified T> keyByTag(tag: String) = keysByTags(tag).first { it is T }
|
||||
inline fun <reified T> keyByTag(tag: String) = findByTags(tag).first { it is T }
|
||||
|
||||
/**
|
||||
* Get keys of the specified type having any of the specified tags associated.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
inline fun <reified T> keysByAnyTag(vararg tags: String): Sequence<UniversalKey> =
|
||||
keysByTags(*tags).filter { it is T }
|
||||
findByTags(*tags).filter { it is T }
|
||||
|
||||
/**
|
||||
* Get all keys with a given id. Note that _matching keys_ have the same id, see [KeyId] for more.
|
||||
@@ -134,6 +140,7 @@ class UniversalRing(
|
||||
/**
|
||||
* Add key and tags to the ring. If the key already exists, tags are merged.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
fun add(key: UniversalKey, tags: Collection<String>): UniversalRing =
|
||||
UniversalRing(keyWithTags + (key to tags.toSet()))
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ sealed class KDF {
|
||||
*
|
||||
* Random salt of proper size is used
|
||||
*/
|
||||
fun kdfForSize(numberOfKeys: Int): KDF = creteDefault(SymmetricKey.keyLength*numberOfKeys, this)
|
||||
fun kdfForSize(numberOfKeys: Int): KDF = creteDefault(SymmetricKey.keyLength * numberOfKeys, this)
|
||||
|
||||
/**
|
||||
* Derive multiple keys from the password. Derivation params will be included in the key ids, see
|
||||
@@ -49,20 +49,20 @@ sealed class KDF {
|
||||
*/
|
||||
@Suppress("unused")
|
||||
fun deriveMultiple(password: String, count: Int): List<SymmetricKey> =
|
||||
kdfForSize(count).deriveMultiple(password, count)
|
||||
kdfForSize(count).deriveMultipleKeys(password, count)
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive a single key from the password, same as [deriveMultiple] with `count==1`
|
||||
* Derive a single key from the password, same as [deriveMultipleKeys] with `count==1`
|
||||
*/
|
||||
abstract fun derive(password: String): UByteArray
|
||||
abstract fun deriveKey(password: String): UByteArray
|
||||
|
||||
/**
|
||||
* Derive keys from lower part of bytes derived from the password. E.g., if generated size is longer than
|
||||
* required to create [count] keys, the first bytes will be used. It let use rest bytes for other purposes.
|
||||
*/
|
||||
fun deriveMultiple(password: String, count: Int): List<SymmetricKey> {
|
||||
val bytes = derive(password)
|
||||
fun deriveMultipleKeys(password: String, count: Int): List<SymmetricKey> {
|
||||
val bytes = deriveKey(password)
|
||||
val ks = SymmetricKey.keyLength
|
||||
check(ks * count <= bytes.size) { "KDF is too short for $count keys: ${bytes.size} we need ${ks * count}" }
|
||||
return (0..<count).map {
|
||||
@@ -84,6 +84,7 @@ sealed class KDF {
|
||||
val keySize: Int,
|
||||
) : KDF(), Comparable<Argon> {
|
||||
|
||||
|
||||
/**
|
||||
* Very abstract strength comparison. If a1 > a2 it generally means that its complexity, and, hopefully,
|
||||
* strength is higher.
|
||||
@@ -111,7 +112,7 @@ sealed class KDF {
|
||||
}
|
||||
}
|
||||
|
||||
override fun derive(password: String): UByteArray =
|
||||
override fun deriveKey(password: String): UByteArray =
|
||||
PasswordHash.pwhash(keySize, password, salt, instructionsComplexity, memComplexity, algorithm.code)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -141,6 +142,17 @@ sealed class KDF {
|
||||
|
||||
fun randomSalt() = randomUBytes(saltSize)
|
||||
|
||||
/**
|
||||
* Create a deterministic salt suitable fot this KDF from a given text.
|
||||
*
|
||||
* We recommend to use random salts stored, and [KeyId] of password-generated keys already
|
||||
* do it for you. Use this method only if you can't store [KeyId] or salt; it is generally less secure:
|
||||
* knowing the base text it is possible to understand, for example, that the same derived password was used
|
||||
* more than once (random salt makes it impossible).
|
||||
*/
|
||||
@Suppress("unused")
|
||||
fun deriveSaltFromString(text: String) = Hash.Blake2b.deriveSalt(text, saltSize)
|
||||
|
||||
fun create(complexity: Complexity, salt: UByteArray, keySize: Int): Argon {
|
||||
require(salt.size == saltSize) { "The salt size should be $saltSize" }
|
||||
require(keySize > minKeySize) { "The key size should be at least $keySize bytes" }
|
||||
@@ -165,6 +177,7 @@ sealed class KDF {
|
||||
268435456,
|
||||
salt, keySize
|
||||
)
|
||||
|
||||
Moderate -> Argon(
|
||||
Alg.default,
|
||||
crypto_pwhash_OPSLIMIT_MODERATE,
|
||||
@@ -192,10 +205,10 @@ sealed class KDF {
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("unused")
|
||||
fun creteDefault(keySize: Int, complexity: Complexity, salt: UByteArray = Argon.randomSalt()): KDF {
|
||||
return Argon.create(complexity, salt, keySize)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
data class Instance(val kdf: KDF, val password: String)
|
||||
|
||||
Reference in new issue
Block a user