pto.0.3.0.source-code.KeyStretcher.kt Maven / Gradle / Ivy
package se.wollan.crypto
import org.slf4j.Logger
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import kotlin.system.measureTimeMillis
// https://en.wikipedia.org/wiki/Key_stretching
internal interface KeyStretcher {
fun stretch(weakKey: Pincode, salt: Salt, iterations: KeyStretchIterations): SecretKey
}
@JvmInline
value class KeyStretchIterations(val value: Int) {
init {
require(value >= minValue) { "too few iterations: $value" }
}
override fun toString() = value.toString()
companion object {
private const val minValue = 100_000
val min = KeyStretchIterations(minValue)
val default = KeyStretchIterations(1_000_000)
val medium = KeyStretchIterations(500_000)
}
}
// Same settings as https://mprimi.github.io/portable-secret/creator/
internal class PBKDF2KeyStretcher : KeyStretcher {
override fun stretch(weakKey: Pincode, salt: Salt, iterations: KeyStretchIterations): SecretKey {
val spec = PBEKeySpec(weakKey.value, salt.value, iterations.value, SecretKey.LEN_BYTES * 8)
val factory = SecretKeyFactory.getInstance(ALG)
val hash = factory.generateSecret(spec).encoded
return SecretKey(hash)
}
companion object {
private const val ALG = "PBKDF2WithHmacSHA1"
}
}
internal class LoggingKeyStretcherDecorator(
private val inner: KeyStretcher, private val logger: Logger
) : KeyStretcher {
override fun stretch(weakKey: Pincode, salt: Salt, iterations: KeyStretchIterations): SecretKey {
val result: SecretKey
val ms = measureTimeMillis {
result = inner.stretch(weakKey, salt, iterations)
}
logger.debug("key stretching with $iterations iterations took $ms ms")
return result
}
}