diff --git a/mod.ts b/mod.ts index c427db92e..ed666d8fd 100644 --- a/mod.ts +++ b/mod.ts @@ -24,6 +24,8 @@ export { DhkemP521HkdfSha512 } from "./src/kems/dhkemP521.ts"; export { DhkemX25519HkdfSha256 } from "./src/kems/dhkemX25519.ts"; export { DhkemX448HkdfSha512 } from "./src/kems/dhkemX448.ts"; -export { HkdfSha256, HkdfSha384, HkdfSha512 } from "./src/kdfs/hkdf.ts"; +export { HkdfSha256 } from "./src/kdfs/hkdfSha256.ts"; +export { HkdfSha384 } from "./src/kdfs/hkdfSha384.ts"; +export { HkdfSha512 } from "./src/kdfs/hkdfSha512.ts"; export { Aes128Gcm, Aes256Gcm } from "./src/aeads/aesGcm.ts"; export { Chacha20Poly1305 } from "./src/aeads/chacha20Poly1305.ts"; diff --git a/src/cipherSuite.ts b/src/cipherSuite.ts index 84b72ea3d..b92f8bfe7 100644 --- a/src/cipherSuite.ts +++ b/src/cipherSuite.ts @@ -21,7 +21,9 @@ import { AeadId, KdfId, KemId, Mode } from "./identifiers.ts"; import { Aes128Gcm, Aes256Gcm } from "./aeads/aesGcm.ts"; import { ExportOnly } from "./aeads/exportOnly.ts"; import { Chacha20Poly1305 } from "./aeads/chacha20Poly1305.ts"; -import { HkdfSha256, HkdfSha384, HkdfSha512 } from "./kdfs/hkdf.ts"; +import { HkdfSha256 } from "./kdfs/hkdfSha256.ts"; +import { HkdfSha384 } from "./kdfs/hkdfSha384.ts"; +import { HkdfSha512 } from "./kdfs/hkdfSha512.ts"; import { RecipientContext } from "./recipientContext.ts"; import { SenderContext } from "./senderContext.ts"; import { loadSubtleCrypto } from "./webCrypto.ts"; diff --git a/src/kdfs/hkdf.ts b/src/kdfs/hkdf.ts index 6e432c7ae..78c120d30 100644 --- a/src/kdfs/hkdf.ts +++ b/src/kdfs/hkdf.ts @@ -1,7 +1,3 @@ -import { hmac } from "npm:@noble/hashes@1.3.1/hmac"; -import { sha256 } from "npm:@noble/hashes@1.3.1/sha256"; -import { sha384, sha512 } from "npm:@noble/hashes@1.3.1/sha512"; - import type { KdfInterface } from "../interfaces/kdfInterface.ts"; import { KdfId } from "../identifiers.ts"; @@ -9,7 +5,7 @@ import { KdfAlgorithm } from "../algorithm.ts"; import * as consts from "../consts.ts"; -export class Hkdf extends KdfAlgorithm implements KdfInterface { +export class HkdfNative extends KdfAlgorithm implements KdfInterface { public readonly id: KdfId = KdfId.HkdfSha256; public readonly hashSize: number = 0; protected readonly algHash: HmacKeyGenParams = { @@ -58,29 +54,7 @@ export class Hkdf extends KdfAlgorithm implements KdfInterface { salt = new ArrayBuffer(this.hashSize); } if (salt.byteLength !== this.hashSize) { - // Web Cryptography API supports only Nh(hashSize) length key. - // In this case, fallback to the upper-layer hmac library. - switch (this.algHash.hash) { - case "SHA-256": - return hmac( - sha256, - new Uint8Array(salt), - new Uint8Array(ikm), - ); - case "SHA-384": - return hmac( - sha384, - new Uint8Array(salt), - new Uint8Array(ikm), - ); - default: - // case "SHA-512": - return hmac( - sha512, - new Uint8Array(salt), - new Uint8Array(ikm), - ); - } + throw new Error("The salt length must be the same as the hashSize"); } const key = await (this._api as SubtleCrypto).importKey( "raw", @@ -188,7 +162,7 @@ export class Hkdf extends KdfAlgorithm implements KdfInterface { } } -export class HkdfSha256 extends Hkdf implements KdfInterface { +export class HkdfSha256Native extends HkdfNative { public readonly id: KdfId = KdfId.HkdfSha256; public readonly hashSize: number = 32; protected readonly algHash: HmacKeyGenParams = { @@ -198,7 +172,7 @@ export class HkdfSha256 extends Hkdf implements KdfInterface { }; } -export class HkdfSha384 extends Hkdf implements KdfInterface { +export class HkdfSha384Native extends HkdfNative { public readonly id: KdfId = KdfId.HkdfSha384; public readonly hashSize: number = 48; protected readonly algHash: HmacKeyGenParams = { @@ -208,7 +182,7 @@ export class HkdfSha384 extends Hkdf implements KdfInterface { }; } -export class HkdfSha512 extends Hkdf implements KdfInterface { +export class HkdfSha512Native extends HkdfNative { public readonly id: KdfId = KdfId.HkdfSha512; public readonly hashSize: number = 64; protected readonly algHash: HmacKeyGenParams = { diff --git a/src/kdfs/hkdfSha256.ts b/src/kdfs/hkdfSha256.ts new file mode 100644 index 000000000..d1e84c9aa --- /dev/null +++ b/src/kdfs/hkdfSha256.ts @@ -0,0 +1,29 @@ +import { hmac } from "npm:@noble/hashes@1.3.1/hmac"; +import { sha256 } from "npm:@noble/hashes@1.3.1/sha256"; + +import { HkdfSha256Native } from "./hkdf.ts"; + +export class HkdfSha256 extends HkdfSha256Native { + public override async extract( + salt: ArrayBuffer, + ikm: ArrayBuffer, + ): Promise { + this.checkInit(); + if (salt.byteLength === 0) { + salt = new ArrayBuffer(this.hashSize); + } + if (salt.byteLength !== this.hashSize) { + return hmac(sha256, new Uint8Array(salt), new Uint8Array(ikm)); + } + const key = await (this._api as SubtleCrypto).importKey( + "raw", + salt, + this.algHash, + false, + [ + "sign", + ], + ); + return await (this._api as SubtleCrypto).sign("HMAC", key, ikm); + } +} diff --git a/src/kdfs/hkdfSha384.ts b/src/kdfs/hkdfSha384.ts new file mode 100644 index 000000000..06573d5cf --- /dev/null +++ b/src/kdfs/hkdfSha384.ts @@ -0,0 +1,29 @@ +import { hmac } from "npm:@noble/hashes@1.3.1/hmac"; +import { sha384 } from "npm:@noble/hashes@1.3.1/sha512"; + +import { HkdfSha384Native } from "./hkdf.ts"; + +export class HkdfSha384 extends HkdfSha384Native { + public override async extract( + salt: ArrayBuffer, + ikm: ArrayBuffer, + ): Promise { + this.checkInit(); + if (salt.byteLength === 0) { + salt = new ArrayBuffer(this.hashSize); + } + if (salt.byteLength !== this.hashSize) { + return hmac(sha384, new Uint8Array(salt), new Uint8Array(ikm)); + } + const key = await (this._api as SubtleCrypto).importKey( + "raw", + salt, + this.algHash, + false, + [ + "sign", + ], + ); + return await (this._api as SubtleCrypto).sign("HMAC", key, ikm); + } +} diff --git a/src/kdfs/hkdfSha512.ts b/src/kdfs/hkdfSha512.ts new file mode 100644 index 000000000..e16547ac9 --- /dev/null +++ b/src/kdfs/hkdfSha512.ts @@ -0,0 +1,29 @@ +import { hmac } from "npm:@noble/hashes@1.3.1/hmac"; +import { sha512 } from "npm:@noble/hashes@1.3.1/sha512"; + +import { HkdfSha512Native } from "./hkdf.ts"; + +export class HkdfSha512 extends HkdfSha512Native { + public override async extract( + salt: ArrayBuffer, + ikm: ArrayBuffer, + ): Promise { + this.checkInit(); + if (salt.byteLength === 0) { + salt = new ArrayBuffer(this.hashSize); + } + if (salt.byteLength !== this.hashSize) { + return hmac(sha512, new Uint8Array(salt), new Uint8Array(ikm)); + } + const key = await (this._api as SubtleCrypto).importKey( + "raw", + salt, + this.algHash, + false, + [ + "sign", + ], + ); + return await (this._api as SubtleCrypto).sign("HMAC", key, ikm); + } +} diff --git a/src/kems/dhkemP256.ts b/src/kems/dhkemP256.ts index 9ab3aa486..6b700d8b0 100644 --- a/src/kems/dhkemP256.ts +++ b/src/kems/dhkemP256.ts @@ -1,5 +1,5 @@ import { KemId } from "../identifiers.ts"; -import { HkdfSha256 } from "../kdfs/hkdf.ts"; +import { HkdfSha256 } from "../kdfs/hkdfSha256.ts"; import { Dhkem } from "./dhkem.ts"; import { Ec } from "./dhkemPrimitives/ec.ts"; diff --git a/src/kems/dhkemP384.ts b/src/kems/dhkemP384.ts index 39e595561..d7833c824 100644 --- a/src/kems/dhkemP384.ts +++ b/src/kems/dhkemP384.ts @@ -1,5 +1,5 @@ import { KemId } from "../identifiers.ts"; -import { HkdfSha384 } from "../kdfs/hkdf.ts"; +import { HkdfSha384 } from "../kdfs/hkdfSha384.ts"; import { Dhkem } from "./dhkem.ts"; import { Ec } from "./dhkemPrimitives/ec.ts"; diff --git a/src/kems/dhkemP521.ts b/src/kems/dhkemP521.ts index b4444a8c8..a5b338f96 100644 --- a/src/kems/dhkemP521.ts +++ b/src/kems/dhkemP521.ts @@ -1,5 +1,5 @@ import { KemId } from "../identifiers.ts"; -import { HkdfSha512 } from "../kdfs/hkdf.ts"; +import { HkdfSha512 } from "../kdfs/hkdfSha512.ts"; import { Dhkem } from "./dhkem.ts"; import { Ec } from "./dhkemPrimitives/ec.ts"; diff --git a/src/kems/dhkemX25519.ts b/src/kems/dhkemX25519.ts index 51ea8f6fd..dda39b26f 100644 --- a/src/kems/dhkemX25519.ts +++ b/src/kems/dhkemX25519.ts @@ -1,5 +1,5 @@ import { KemId } from "../identifiers.ts"; -import { HkdfSha256 } from "../kdfs/hkdf.ts"; +import { HkdfSha256 } from "../kdfs/hkdfSha256.ts"; import { Dhkem } from "./dhkem.ts"; import { X25519 } from "./dhkemPrimitives/x25519.ts"; diff --git a/src/kems/dhkemX448.ts b/src/kems/dhkemX448.ts index 86072a121..03fa7dcf3 100644 --- a/src/kems/dhkemX448.ts +++ b/src/kems/dhkemX448.ts @@ -1,5 +1,5 @@ import { KemId } from "../identifiers.ts"; -import { HkdfSha512 } from "../kdfs/hkdf.ts"; +import { HkdfSha512 } from "../kdfs/hkdfSha512.ts"; import { Dhkem } from "./dhkem.ts"; import { X448 } from "./dhkemPrimitives/x448.ts"; diff --git a/test/cipherSuite.test.ts b/test/cipherSuite.test.ts index 741f4780d..5b2ac0f3f 100644 --- a/test/cipherSuite.test.ts +++ b/test/cipherSuite.test.ts @@ -12,7 +12,9 @@ import { DhkemP384HkdfSha384 } from "../src/kems/dhkemP384.ts"; import { DhkemP521HkdfSha512 } from "../src/kems/dhkemP521.ts"; import { DhkemX25519HkdfSha256 } from "../src/kems/dhkemX25519.ts"; import { DhkemX448HkdfSha512 } from "../src/kems/dhkemX448.ts"; -import { HkdfSha256, HkdfSha384, HkdfSha512 } from "../src/kdfs/hkdf.ts"; +import { HkdfSha256 } from "../src/kdfs/hkdfSha256.ts"; +import { HkdfSha384 } from "../src/kdfs/hkdfSha384.ts"; +import { HkdfSha512 } from "../src/kdfs/hkdfSha512.ts"; import { Aes128Gcm } from "../src/aeads/aesGcm.ts"; import * as errors from "../src/errors.ts"; diff --git a/test/encryptionContext.test.ts b/test/encryptionContext.test.ts index fb0f08e4c..82953db8a 100644 --- a/test/encryptionContext.test.ts +++ b/test/encryptionContext.test.ts @@ -5,7 +5,7 @@ import { describe, it } from "testing/bdd.ts"; import { CipherSuite } from "../src/cipherSuite.ts"; import { EncryptionContext } from "../src/encryptionContext.ts"; import { AeadId, KdfId, KemId } from "../src/identifiers.ts"; -import { HkdfSha256 } from "../src/kdfs/hkdf.ts"; +import { HkdfSha256 } from "../src/kdfs/hkdfSha256.ts"; import { loadSubtleCrypto } from "../src/webCrypto.ts"; import { i2Osp } from "../src/utils/misc.ts"; import { ExportOnly } from "../src/aeads/exportOnly.ts"; diff --git a/test/kdfContext.test.ts b/test/kdfContext.test.ts index bc68ecf5e..aef00adb0 100644 --- a/test/kdfContext.test.ts +++ b/test/kdfContext.test.ts @@ -2,7 +2,9 @@ import { assertEquals } from "testing/asserts.ts"; import { describe, it } from "testing/bdd.ts"; -import { HkdfSha256, HkdfSha384, HkdfSha512 } from "../src/kdfs/hkdf.ts"; +import { HkdfSha256 } from "../src/kdfs/hkdfSha256.ts"; +import { HkdfSha384 } from "../src/kdfs/hkdfSha384.ts"; +import { HkdfSha512 } from "../src/kdfs/hkdfSha512.ts"; import { AeadId, KdfId, KemId } from "../src/identifiers.ts"; import { loadCrypto, loadSubtleCrypto } from "../src/webCrypto.ts"; import { i2Osp } from "../src/utils/misc.ts"; diff --git a/x/dhkem-secp256k1/src/dhkem-secp256k1.ts b/x/dhkem-secp256k1/src/dhkem-secp256k1.ts index 6863aa7a2..d18f15438 100644 --- a/x/dhkem-secp256k1/src/dhkem-secp256k1.ts +++ b/x/dhkem-secp256k1/src/dhkem-secp256k1.ts @@ -7,7 +7,7 @@ import type { KemInterface } from "../../../src/interfaces/kemInterface.ts"; import { Algorithm } from "../../../src/algorithm.ts"; import { KemId } from "../../../src/identifiers.ts"; import { XCryptoKey } from "../../../src/xCryptoKey.ts"; -import { HkdfSha256 } from "../../../src/kdfs/hkdf.ts"; +import { HkdfSha256 } from "../../../src/kdfs/hkdfSha256.ts"; import { Dhkem } from "../../../src/kems/dhkem.ts"; import * as consts from "../../../src/consts.ts";