Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ import platform.CoreCrypto.kCCHmacAlgSHA256
import platform.CoreCrypto.kCCHmacAlgSHA512

object IosHmacService : HmacService {
override suspend fun <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int?): HmacKey<A> {
require(keySize == null || keySize >= algorithm.minimumKeySize) {
"Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
override suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int?,
acceptsShortKeySize: Boolean
): HmacKey<A> {
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)
}
Expand All @@ -21,9 +25,13 @@ object IosHmacService : HmacService {
override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey.copyOf()

override suspend fun <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A> {
require(bytes.size >= algorithm.minimumKeySize) {
"Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
override suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean
): HmacKey<A> {
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)
}
Expand Down
18 changes: 14 additions & 4 deletions lib/src/commonMain/kotlin/com/icure/kryptom/crypto/HmacService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int? = null): HmacKey<A>
suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int? = null,
acceptsShortKeySize: Boolean = false
): HmacKey<A>

/**
* Exports a key to a byte array.
Expand All @@ -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 <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A>
suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean = false
): HmacKey<A>

/**
* Generates a signature for some data using the provided key and algorithm.
Expand Down
6 changes: 3 additions & 3 deletions lib/src/commonMain/kotlin/com/icure/kryptom/crypto/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
}
Expand All @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IllegalArgumentException> {
defaultCryptoService.hmac.generateKey(algorithm, algorithm.minimumKeySize - 1)
defaultCryptoService.hmac.generateKey(algorithm, algorithm.minimumRecommendedKeySize - 1)
}
}

Expand Down
20 changes: 14 additions & 6 deletions lib/src/jsMain/kotlin/com/icure/kryptom/crypto/JsHmacService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,13 @@ object JsHmacService : HmacService {
"length" to keySize * 8
)

override suspend fun <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int?): HmacKey<A> {
require(keySize == null || keySize >= algorithm.minimumKeySize) {
"Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
override suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int?,
acceptsShortKeySize: Boolean
): HmacKey<A> {
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(
Expand All @@ -44,9 +48,13 @@ object JsHmacService : HmacService {
private suspend fun exportRawKey(rawKey: dynamic) =
jsCrypto.subtle.exportKey(RAW, rawKey).await() as ArrayBuffer

override suspend fun <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A> {
require(bytes.size >= algorithm.minimumKeySize) {
"Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
override suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean
): HmacKey<A> {
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,13 @@ object JvmHmacService : HmacService {
HmacAlgorithm.HmacSha256 -> "HMac-SHA256"
}

override suspend fun <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int?): HmacKey<A> {
require(keySize == null || keySize >= algorithm.minimumKeySize) {
"Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
override suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int?,
acceptsShortKeySize: Boolean
): HmacKey<A> {
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)
Expand All @@ -24,9 +28,13 @@ object JvmHmacService : HmacService {
return key.key.encoded
}

override suspend fun <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A> {
require(bytes.size >= algorithm.minimumKeySize) {
"Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
override suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean
): HmacKey<A> {
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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -20,19 +18,27 @@ import libcrypto.HMAC_Update

@OptIn(ExperimentalForeignApi::class)
object OpensslHmacService : HmacService {
override suspend fun <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int?): HmacKey<A> {
require(keySize == null || keySize >= algorithm.minimumKeySize) {
"Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
override suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int?,
acceptsShortKeySize: Boolean
): HmacKey<A> {
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)
}

override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey

override suspend fun <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A> {
require(bytes.size >= algorithm.minimumKeySize) {
"Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
override suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean
) : HmacKey<A> {
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)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <A : HmacAlgorithm> generateKey(algorithm: A, keySize: Int?): HmacKey<A> {
require(keySize == null || keySize >= algorithm.minimumKeySize) {
"Invalid key size for $algorithm. A minimal length of ${algorithm.minimumKeySize} is required"
override suspend fun <A : HmacAlgorithm> generateKey(
algorithm: A,
keySize: Int?,
acceptsShortKeySize: Boolean
): HmacKey<A> {
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),
Expand All @@ -36,9 +40,13 @@ object BCryptHmacService : HmacService {
override suspend fun exportKey(key: HmacKey<*>): ByteArray =
key.rawKey.copyOf()

override suspend fun <A : HmacAlgorithm> loadKey(algorithm: A, bytes: ByteArray): HmacKey<A> {
require(bytes.size >= algorithm.minimumKeySize) {
"Invalid key length for algorithm $algorithm: got ${bytes.size} but at least ${algorithm.minimumKeySize} expected"
override suspend fun <A : HmacAlgorithm> loadKey(
algorithm: A,
bytes: ByteArray,
acceptsShortKeys: Boolean
): HmacKey<A> {
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(),
Expand Down