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
@@ -1,13 +1,23 @@
// META: title=WebCryptoAPI: ML-KEM encapsulateBits() and decapsulateBits() tests
// META: script=ml_kem_vectors.js
// META: title=WebCryptoAPI: KEM encapsulateBits() and decapsulateBits() tests
// META: script=../util/helpers.js
// META: script=ml_kem_vectors.js
// META: script=hybrid_kem_vectors.js
// META: timeout=long

function define_bits_tests() {
var subtle = self.crypto.subtle;
var variants = ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024'];
var variants = [
{ name: 'ML-KEM-512', ciphertextLength: 768 },
{ name: 'ML-KEM-768', ciphertextLength: 1088 },
{ name: 'ML-KEM-1024', ciphertextLength: 1568 },
{ name: 'MLKEM768-P256', ciphertextLength: 1153 },
{ name: 'MLKEM768-X25519', ciphertextLength: 1120 },
{ name: 'MLKEM1024-P384', ciphertextLength: 1665 },
];

variants.forEach(function (variant) {
var algorithmName = variant.name;

variants.forEach(function (algorithmName) {
// Test encapsulateBits operation
promise_test(async function (test) {
// Generate a key pair for testing
Expand Down Expand Up @@ -50,24 +60,11 @@ function define_bits_tests() {
'Shared key should be 32 bytes'
);

// Verify ciphertext length based on algorithm variant
var expectedCiphertextLength;
switch (algorithmName) {
case 'ML-KEM-512':
expectedCiphertextLength = 768;
break;
case 'ML-KEM-768':
expectedCiphertextLength = 1088;
break;
case 'ML-KEM-1024':
expectedCiphertextLength = 1568;
break;
}
assert_equals(
encapsulatedBits.ciphertext.byteLength,
expectedCiphertextLength,
variant.ciphertextLength,
'Ciphertext should be ' +
expectedCiphertextLength +
variant.ciphertextLength +
' bytes for ' +
algorithmName
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
// META: title=WebCryptoAPI: ML-KEM encapsulateKey() and decapsulateKey() tests
// META: script=ml_kem_vectors.js
// META: title=WebCryptoAPI: KEM encapsulateKey() and decapsulateKey() tests
// META: script=../util/helpers.js
// META: script=ml_kem_vectors.js
// META: script=hybrid_kem_vectors.js
// META: timeout=long

function define_key_tests() {
var subtle = self.crypto.subtle;
var variants = ['ML-KEM-512', 'ML-KEM-768', 'ML-KEM-1024'];
var variants = [
{ name: 'ML-KEM-512', ciphertextLength: 768 },
{ name: 'ML-KEM-768', ciphertextLength: 1088 },
{ name: 'ML-KEM-1024', ciphertextLength: 1568 },
{ name: 'MLKEM768-P256', ciphertextLength: 1153 },
{ name: 'MLKEM768-X25519', ciphertextLength: 1120 },
{ name: 'MLKEM1024-P384', ciphertextLength: 1665 },
];

// Test various 256-bit shared key algorithms
var sharedKeyConfigs = [
Expand Down Expand Up @@ -36,7 +44,9 @@ function define_key_tests() {
},
];

variants.forEach(function (algorithmName) {
variants.forEach(function (variant) {
var algorithmName = variant.name;

sharedKeyConfigs.forEach(function (config) {
[true, false].forEach(function (extractable) {
// Test encapsulateKey operation
Expand Down Expand Up @@ -115,24 +125,11 @@ function define_key_tests() {
);
}

// Verify ciphertext length based on algorithm variant
var expectedCiphertextLength;
switch (algorithmName) {
case 'ML-KEM-512':
expectedCiphertextLength = 768;
break;
case 'ML-KEM-768':
expectedCiphertextLength = 1088;
break;
case 'ML-KEM-1024':
expectedCiphertextLength = 1568;
break;
}
assert_equals(
encapsulatedKey.ciphertext.byteLength,
expectedCiphertextLength,
variant.ciphertextLength,
'Ciphertext should be ' +
expectedCiphertextLength +
variant.ciphertextLength +
' bytes for ' +
algorithmName
);
Expand Down
50 changes: 50 additions & 0 deletions test/fixtures/wpt/WebCryptoAPI/encap_decap/hybrid_kem_vectors.js

Large diffs are not rendered by default.

682 changes: 36 additions & 646 deletions test/fixtures/wpt/WebCryptoAPI/encap_decap/ml_kem_vectors.js

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// META: title=WebCryptoAPI: generateKey() for Failures
// META: timeout=long
// META: script=../util/helpers.js
// META: script=algorithm_registry.js
// META: script=failures.js
run_test(["MLKEM768-P256", "MLKEM768-X25519", "MLKEM1024-P384"]);
35 changes: 17 additions & 18 deletions test/fixtures/wpt/WebCryptoAPI/generateKey/successes.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,23 +47,22 @@ function run_test(algorithmNames, slowTest) {
assert_unreached("generateKey threw an unexpected error: " + err.toString());
})
.then(async function (result) {
// TODO: remove this block to enable ML-KEM JWK when its definition is done in IETF JOSE WG
if (result.publicKey?.algorithm.name.startsWith('ML-KEM')) {
const promises = [
subtle.exportKey('spki', result.publicKey),
extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined,
subtle.exportKey('raw-public', result.publicKey),
];
if (extractable)
promises.push(subtle.exportKey('raw-seed', result.privateKey));
await Promise.all(promises);
} else if (resultType === "CryptoKeyPair") {
const promises = [
subtle.exportKey('jwk', result.publicKey),
extractable ? subtle.exportKey('jwk', result.privateKey) : undefined,
subtle.exportKey('spki', result.publicKey),
extractable ? subtle.exportKey('pkcs8', result.privateKey) : undefined,
];
if (resultType === "CryptoKeyPair") {
// TODO: enable ML-KEM JWK when its definition is done in IETF JOSE WG.
const isMlKem = result.publicKey.algorithm.name.startsWith('ML-KEM');
const isHybridKem = result.publicKey.algorithm.name.startsWith('MLKEM');
const promises = [];

if (!isMlKem) {
promises.push(subtle.exportKey('jwk', result.publicKey));
promises.push(extractable ? subtle.exportKey('jwk', result.privateKey) : undefined);
}

if (!isHybridKem) {
promises.push(subtle.exportKey('spki', result.publicKey));
if (extractable)
promises.push(subtle.exportKey('pkcs8', result.privateKey));
}

switch (result.publicKey.algorithm.name.substring(0, 2)) {
case 'ML':
Expand All @@ -90,7 +89,7 @@ function run_test(algorithmNames, slowTest) {

const [jwkPub, jwkPriv] = await Promise.all(promises);

if (extractable) {
if (extractable && !isMlKem) {
// Test that the JWK public key is a superset of the JWK private key.
for (const [prop, value] of Object.entries(jwkPub)) {
if (prop !== 'key_ops') {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// META: title=WebCryptoAPI: generateKey() Successful Calls
// META: timeout=long
// META: script=../util/helpers.js
// META: script=/common/subset-tests.js
// META: script=algorithm_registry.js
// META: script=successes.js
run_test(["MLKEM768-P256", "MLKEM768-X25519", "MLKEM1024-P384"]);
23 changes: 21 additions & 2 deletions test/fixtures/wpt/WebCryptoAPI/getPublicKey.tentative.https.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,24 @@ const algorithms = [
generateKeyParams: { name: "ML-KEM-1024" },
usages: ["encapsulateBits", "encapsulateKey", "decapsulateBits", "decapsulateKey"],
publicKeyUsages: ["encapsulateBits", "encapsulateKey"]
},
{
name: "MLKEM768-P256",
generateKeyParams: { name: "MLKEM768-P256" },
usages: ["encapsulateBits", "encapsulateKey", "decapsulateBits", "decapsulateKey"],
publicKeyUsages: ["encapsulateBits", "encapsulateKey"]
},
{
name: "MLKEM768-X25519",
generateKeyParams: { name: "MLKEM768-X25519" },
usages: ["encapsulateBits", "encapsulateKey", "decapsulateBits", "decapsulateKey"],
publicKeyUsages: ["encapsulateBits", "encapsulateKey"]
},
{
name: "MLKEM1024-P384",
generateKeyParams: { name: "MLKEM1024-P384" },
usages: ["encapsulateBits", "encapsulateKey", "decapsulateBits", "decapsulateKey"],
publicKeyUsages: ["encapsulateBits", "encapsulateKey"]
}
];

Expand Down Expand Up @@ -145,8 +163,9 @@ algorithms.forEach(function(algorithm) {

// Verify that the derived public key matches the original public key
// by comparing their exported forms
const originalExported = await crypto.subtle.exportKey("spki", keyPair.publicKey);
const derivedExported = await crypto.subtle.exportKey("spki", publicKey);
const exportFormat = algorithm.name.startsWith("MLKEM") ? "raw-public" : "spki";
const originalExported = await crypto.subtle.exportKey(exportFormat, keyPair.publicKey);
const derivedExported = await crypto.subtle.exportKey(exportFormat, publicKey);

assert_array_equals(
new Uint8Array(originalExported),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// META: title=WebCryptoAPI: importKey() for Hybrid KEM keys
// META: timeout=long
// META: script=../util/helpers.js
// META: script=Hybrid-KEM_importKey_fixtures.js
// META: script=ml_importKey.js

var keyData = hybridKemKeyData;

runTests("MLKEM768-P256");
runTests("MLKEM768-X25519");
runTests("MLKEM1024-P384");
Loading
Loading