diff --git a/lib/src/appleMain/kotlin/com/icure/kryptom/crypto/IosHmacService.kt b/lib/src/appleMain/kotlin/com/icure/kryptom/crypto/IosHmacService.kt
index bac56f3..0d31d9e 100644
--- a/lib/src/appleMain/kotlin/com/icure/kryptom/crypto/IosHmacService.kt
+++ b/lib/src/appleMain/kotlin/com/icure/kryptom/crypto/IosHmacService.kt
@@ -10,9 +10,13 @@ import platform.CoreCrypto.kCCHmacAlgSHA256
import platform.CoreCrypto.kCCHmacAlgSHA512
object IosHmacService : HmacService {
- override suspend fun generateKey(algorithm: A, keySize: Int?): HmacKey {
- require(keySize == null || keySize >= algorithm.minimumKeySize) {
- "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
+ override suspend fun generateKey(
+ algorithm: A,
+ keySize: Int?,
+ acceptsShortKeySize: Boolean
+ ): HmacKey {
+ require(acceptsShortKeySize || keySize == null || keySize >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumRecommendedKeySize} is required"
}
return HmacKey(IosStrongRandom.randomBytes(keySize ?: algorithm.recommendedKeySize), algorithm)
}
@@ -21,9 +25,13 @@ object IosHmacService : HmacService {
override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey.copyOf()
- override suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey {
- require(bytes.size >= algorithm.minimumKeySize) {
- "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
+ override suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean
+ ): HmacKey {
+ require(acceptsShortKeys || bytes.size >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumRecommendedKeySize} expected"
}
return HmacKey(bytes.copyOf(), algorithm)
}
diff --git a/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/HmacService.kt b/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/HmacService.kt
index a2dcb06..19e131e 100644
--- a/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/HmacService.kt
+++ b/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/HmacService.kt
@@ -6,10 +6,15 @@ interface HmacService {
*
* @param algorithm the [HmacAlgorithm].
* @param keySize the key size. If null (default behaviour), [HmacAlgorithm.recommendedKeySize] will be used.
- * Note: for security reasons, the key size cannot be less than [HmacAlgorithm.minimumKeySize]
- * @throws IllegalArgumentException if [keySize] is less than [HmacAlgorithm.minimumKeySize]
+ * Note: for general usage the key size shouldn't be less than [HmacAlgorithm.minimumRecommendedKeySize], but in
+ * some applications (e.g. TOTP shorter lengths are acceptable)
+ * @param acceptsShortKeySize if false (default) key sizes shorter than the minimum recommended key size for the algorithm will be rejected
*/
- suspend fun generateKey(algorithm: A, keySize: Int? = null): HmacKey
+ suspend fun generateKey(
+ algorithm: A,
+ keySize: Int? = null,
+ acceptsShortKeySize: Boolean = false
+ ): HmacKey
/**
* Exports a key to a byte array.
@@ -18,8 +23,13 @@ interface HmacService {
/**
* Imports a key from a byte array.
+ * @param acceptsShortKey if false (default) keys shorter than the minimum recommended key size for the algorithm will be rejected
*/
- suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey
+ suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean = false
+ ): HmacKey
/**
* Generates a signature for some data using the provided key and algorithm.
diff --git a/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/Keys.kt b/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/Keys.kt
index b80e210..d9708a9 100644
--- a/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/Keys.kt
+++ b/lib/src/commonMain/kotlin/com/icure/kryptom/crypto/Keys.kt
@@ -202,7 +202,7 @@ sealed interface HmacAlgorithm {
* The minimum key size for this algorithm in bytes, as specified in the [HMAC RFC](https://www.rfc-editor.org/rfc/rfc2104#section-3).
* A key which size is under this length decrease the security strength of the function.
*/
- val minimumKeySize: Int
+ val minimumRecommendedKeySize: Int
/**
* The size of the digest produced by this algorithm in bytes.
@@ -219,7 +219,7 @@ sealed interface HmacAlgorithm {
*/
data object HmacSha512 : HmacAlgorithm {
override val recommendedKeySize: Int = 128
- override val minimumKeySize: Int = 64
+ override val minimumRecommendedKeySize: Int = 64
override val digestSize: Int = 64
override val identifier: String = Identifiers.HMAC_SHA_512
}
@@ -229,7 +229,7 @@ sealed interface HmacAlgorithm {
*/
data object HmacSha256 : HmacAlgorithm {
override val recommendedKeySize: Int = 64
- override val minimumKeySize: Int = 32
+ override val minimumRecommendedKeySize: Int = 32
override val digestSize: Int = 32
override val identifier: String = Identifiers.HMAC_SHA_256
}
diff --git a/lib/src/commonTest/kotlin/com/icure/kryptom/crypto/HmacServiceTest.kt b/lib/src/commonTest/kotlin/com/icure/kryptom/crypto/HmacServiceTest.kt
index 28a84c0..10469ce 100644
--- a/lib/src/commonTest/kotlin/com/icure/kryptom/crypto/HmacServiceTest.kt
+++ b/lib/src/commonTest/kotlin/com/icure/kryptom/crypto/HmacServiceTest.kt
@@ -62,14 +62,14 @@ class HmacServiceTest : StringSpec({
}
"$algorithm - can generate a key with a custom size" {
- val size = algorithm.minimumKeySize
+ val size = algorithm.minimumRecommendedKeySize
val key = defaultCryptoService.hmac.generateKey(algorithm, size)
defaultCryptoService.hmac.exportKey(key).size shouldBe size
}
"$algorithm - cannot specify a key size less than the minimum key size" {
shouldThrow {
- defaultCryptoService.hmac.generateKey(algorithm, algorithm.minimumKeySize - 1)
+ defaultCryptoService.hmac.generateKey(algorithm, algorithm.minimumRecommendedKeySize - 1)
}
}
diff --git a/lib/src/jsMain/kotlin/com/icure/kryptom/crypto/JsHmacService.kt b/lib/src/jsMain/kotlin/com/icure/kryptom/crypto/JsHmacService.kt
index 5c13fae..aa71e80 100644
--- a/lib/src/jsMain/kotlin/com/icure/kryptom/crypto/JsHmacService.kt
+++ b/lib/src/jsMain/kotlin/com/icure/kryptom/crypto/JsHmacService.kt
@@ -21,9 +21,13 @@ object JsHmacService : HmacService {
"length" to keySize * 8
)
- override suspend fun generateKey(algorithm: A, keySize: Int?): HmacKey {
- require(keySize == null || keySize >= algorithm.minimumKeySize) {
- "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
+ override suspend fun generateKey(
+ algorithm: A,
+ keySize: Int?,
+ acceptsShortKeySize: Boolean
+ ): HmacKey {
+ require(acceptsShortKeySize || keySize == null || keySize >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumRecommendedKeySize} is required"
}
val requestedKeySize = keySize ?: algorithm.recommendedKeySize
val generatedKey = jsCrypto.subtle.generateKey(
@@ -44,9 +48,13 @@ object JsHmacService : HmacService {
private suspend fun exportRawKey(rawKey: dynamic) =
jsCrypto.subtle.exportKey(RAW, rawKey).await() as ArrayBuffer
- override suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey {
- require(bytes.size >= algorithm.minimumKeySize) {
- "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
+ override suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean
+ ): HmacKey {
+ require(acceptsShortKeys || bytes.size >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumRecommendedKeySize} expected"
}
return HmacKey(
jsCrypto.subtle.importKey(
diff --git a/lib/src/jvmAndAndroidMain/kotlin/com/icure/kryptom/crypto/JvmHmacService.kt b/lib/src/jvmAndAndroidMain/kotlin/com/icure/kryptom/crypto/JvmHmacService.kt
index 4997203..aaf0c5d 100644
--- a/lib/src/jvmAndAndroidMain/kotlin/com/icure/kryptom/crypto/JvmHmacService.kt
+++ b/lib/src/jvmAndAndroidMain/kotlin/com/icure/kryptom/crypto/JvmHmacService.kt
@@ -11,9 +11,13 @@ object JvmHmacService : HmacService {
HmacAlgorithm.HmacSha256 -> "HMac-SHA256"
}
- override suspend fun generateKey(algorithm: A, keySize: Int?): HmacKey {
- require(keySize == null || keySize >= algorithm.minimumKeySize) {
- "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
+ override suspend fun generateKey(
+ algorithm: A,
+ keySize: Int?,
+ acceptsShortKeySize: Boolean
+ ): HmacKey {
+ require(acceptsShortKeySize || keySize == null || keySize >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumRecommendedKeySize} is required"
}
val keyGen: KeyGenerator = KeyGenerator.getInstance(algorithm.name)
keyGen.init((keySize ?: algorithm.recommendedKeySize) * 8)
@@ -24,9 +28,13 @@ object JvmHmacService : HmacService {
return key.key.encoded
}
- override suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey {
- require(bytes.size >= algorithm.minimumKeySize) {
- "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
+ override suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean
+ ): HmacKey {
+ require(acceptsShortKeys || bytes.size >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumRecommendedKeySize} expected"
}
return HmacKey(SecretKeySpec(bytes, algorithm.name), algorithm)
}
diff --git a/lib/src/linuxMain/kotlin/com/icure/kryptom/crypto/OpensslHmacService.kt b/lib/src/linuxMain/kotlin/com/icure/kryptom/crypto/OpensslHmacService.kt
index d4e37da..290235d 100644
--- a/lib/src/linuxMain/kotlin/com/icure/kryptom/crypto/OpensslHmacService.kt
+++ b/lib/src/linuxMain/kotlin/com/icure/kryptom/crypto/OpensslHmacService.kt
@@ -8,9 +8,7 @@ import kotlinx.cinterop.memScoped
import kotlinx.cinterop.pin
import kotlinx.cinterop.ptr
import kotlinx.cinterop.value
-import libcrypto.EVP_DigestSignInit
import libcrypto.EVP_MAX_MD_SIZE
-import libcrypto.EVP_sha256
import libcrypto.EVP_sha512
import libcrypto.HMAC_CTX_free
import libcrypto.HMAC_CTX_new
@@ -20,9 +18,13 @@ import libcrypto.HMAC_Update
@OptIn(ExperimentalForeignApi::class)
object OpensslHmacService : HmacService {
- override suspend fun generateKey(algorithm: A, keySize: Int?): HmacKey {
- require(keySize == null || keySize >= algorithm.minimumKeySize) {
- "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
+ override suspend fun generateKey(
+ algorithm: A,
+ keySize: Int?,
+ acceptsShortKeySize: Boolean
+ ): HmacKey {
+ require(acceptsShortKeySize || keySize == null || keySize >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumRecommendedKeySize} is required"
}
return HmacKey(OpensslStrongRandom.randomBytes(keySize ?: algorithm.recommendedKeySize), algorithm)
}
@@ -30,9 +32,13 @@ object OpensslHmacService : HmacService {
override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey
- override suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey {
- require(bytes.size >= algorithm.minimumKeySize) {
- "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
+ override suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean
+ ) : HmacKey {
+ require(acceptsShortKeys || bytes.size >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumRecommendedKeySize} expected"
}
return HmacKey(bytes.copyOf(), algorithm)
}
diff --git a/lib/src/mingwMain/kotlin/com/icure/kryptom/crypto/BCryptHmacService.kt b/lib/src/mingwMain/kotlin/com/icure/kryptom/crypto/BCryptHmacService.kt
index 3e36e28..98e9054 100644
--- a/lib/src/mingwMain/kotlin/com/icure/kryptom/crypto/BCryptHmacService.kt
+++ b/lib/src/mingwMain/kotlin/com/icure/kryptom/crypto/BCryptHmacService.kt
@@ -23,9 +23,13 @@ object BCryptHmacService : HmacService {
// https://github.com/tpn/winsdk-10/blob/9b69fd26ac0c7d0b83d378dba01080e93349c2ed/Include/10.0.14393.0/shared/bcrypt.h#L884C8-L884C59
private const val BCRYPT_ALG_HANDLE_HMAC_FLAG = 0x08
- override suspend fun generateKey(algorithm: A, keySize: Int?): HmacKey {
- require(keySize == null || keySize >= algorithm.minimumKeySize) {
- "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
+ override suspend fun generateKey(
+ algorithm: A,
+ keySize: Int?,
+ acceptsShortKeySize: Boolean
+ ): HmacKey {
+ require(acceptsShortKeySize || keySize == null || keySize >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key size for $algorithm. A minimal length of ${algorithm.minimumRecommendedKeySize} is required"
}
return HmacKey(
BCryptStrongRandom.randomBytes(algorithm.recommendedKeySize),
@@ -36,9 +40,13 @@ object BCryptHmacService : HmacService {
override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey.copyOf()
- override suspend fun loadKey(algorithm: A, bytes: ByteArray): HmacKey {
- require(bytes.size >= algorithm.minimumKeySize) {
- "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
+ override suspend fun loadKey(
+ algorithm: A,
+ bytes: ByteArray,
+ acceptsShortKeys: Boolean
+ ): HmacKey {
+ require(acceptsShortKeys || bytes.size >= algorithm.minimumRecommendedKeySize) {
+ "Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumRecommendedKeySize} expected"
}
return HmacKey(
bytes.copyOf(),