LibWeb: Handle raw-secret key formats in wrap_key

Refresh the imported wrapKey_unwrapKey WPT to the current upstream
version and treat the raw-secret and other raw* formats like raw
when wrap_key() and unwrap_key() serialize wrapped key bytes.

The updated test covers ChaCha20-Poly1305 wrapping with the
raw-secret format. Accepting the full set of byte-oriented raw
formats lets those keys round-trip through wrapKey and unwrapKey.
This commit is contained in:
Andreas Kling
2026-03-29 11:38:28 +02:00
committed by Andreas Kling
parent 3726df5a9e
commit 52c46483d1
Notes: github-actions[bot] 2026-03-29 11:23:14 +00:00
4 changed files with 162 additions and 84 deletions

View File

@@ -40,6 +40,11 @@
name: "AES-KW",
importParameters: {name: "AES-KW", length: 128},
wrapParameters: {name: "AES-KW"}
},
{
name: 'ChaCha20-Poly1305',
importParameters: {name: "ChaCha20-Poly1305"},
wrapParameters: {name: "ChaCha20-Poly1305", iv: new Uint8Array(12), additionalData: new Uint8Array(16)}
}
];
@@ -57,7 +62,8 @@
{algorithm: {name: "AES-CBC", length: 128}, usages: ["encrypt", "decrypt"]},
{algorithm: {name: "AES-GCM", length: 128}, usages: ["encrypt", "decrypt"]},
{algorithm: {name: "AES-KW", length: 128}, usages: ["wrapKey", "unwrapKey"]},
{algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]}
{algorithm: {name: "HMAC", length: 128, hash: "SHA-256"}, usages: ["sign", "verify"]},
{algorithm: {name: "ChaCha20-Poly1305"}, usages: ['encrypt', 'decrypt']}
];
// Import all the keys needed, then iterate over all combinations
@@ -69,7 +75,7 @@
var wrapper = wrappers[wrapperParam.name];
keysToWrapParameters.filter((param) => Object.keys(keys).includes(param.algorithm.name)).forEach(function(toWrapParam) {
var keyData = keys[toWrapParam.algorithm.name];
["raw", "spki", "pkcs8"].filter((fmt) => Object.keys(keyData).includes(fmt)).forEach(function(keyDataFormat) {
["raw", "raw-secret", "spki", "pkcs8"].filter((fmt) => Object.keys(keyData).includes(fmt)).forEach(function(keyDataFormat) {
var toWrap = keyData[keyDataFormat];
[keyDataFormat, "jwk"].forEach(function(format) {
if (wrappingIsPossible(toWrap.originalExport[format], wrapper.parameters.name)) {
@@ -106,9 +112,15 @@
.then(function(key) {
wrappers["RSA-OAEP"].unwrappingKey = key;
}));
} else if (params.name === "ChaCha20-Poly1305") {
var algorithm = {name: params.name};
promises.push(subtle.importKey("raw-secret", wrappingKeyData["SYMMETRIC256"].raw, algorithm, true, ["wrapKey", "unwrapKey"])
.then(function(key) {
wrappers[params.name] = {wrappingKey: key, unwrappingKey: key, parameters: params};
}));
} else {
var algorithm = {name: params.name};
promises.push(subtle.importKey("raw", wrappingKeyData["SYMMETRIC"].raw, algorithm, true, ["wrapKey", "unwrapKey"])
promises.push(subtle.importKey("raw", wrappingKeyData["SYMMETRIC128"].raw, algorithm, true, ["wrapKey", "unwrapKey"])
.then(function(key) {
wrappers[params.name] = {wrappingKey: key, unwrappingKey: key, parameters: params};
}));
@@ -151,9 +163,12 @@
var keyData = toWrapKeyDataFromAlg(params.algorithm.name);
promises.push(importAndExport("spki", keyData.spki, params.algorithm, params.publicUsages, "public key "));
promises.push(importAndExport("pkcs8", keyData.pkcs8, params.algorithm, params.privateUsages, "private key "));
} else if (params.algorithm.name === "ChaCha20-Poly1305") {
keys[params.algorithm.name] = {};
promises.push(importAndExport("raw-secret", toWrapKeyData["SYMMETRIC256"].raw, params.algorithm, params.usages, ""));
} else {
keys[params.algorithm.name] = {};
promises.push(importAndExport("raw", toWrapKeyData["SYMMETRIC"].raw, params.algorithm, params.usages, ""));
promises.push(importAndExport("raw", toWrapKeyData["SYMMETRIC128"].raw, params.algorithm, params.usages, ""));
}
});
// Using allSettled to skip unsupported test cases.
@@ -292,79 +307,6 @@
}
}
// Are two array buffers the same?
function equalBuffers(a, b) {
if (a.byteLength !== b.byteLength) {
return false;
}
var aBytes = new Uint8Array(a);
var bBytes = new Uint8Array(b);
for (var i=0; i<a.byteLength; i++) {
if (aBytes[i] !== bBytes[i]) {
return false;
}
}
return true;
}
// Are two Jwk objects "the same"? That is, does the object returned include
// matching values for each property that was expected? It's okay if the
// returned object has extra methods; they aren't checked.
function equalJwk(expected, got) {
var fields = Object.keys(expected);
var fieldName;
for(var i=0; i<fields.length; i++) {
fieldName = fields[i];
if (!(fieldName in got)) {
return false;
}
if (objectToString(expected[fieldName]) !== objectToString(got[fieldName])) {
return false;
}
}
return true;
}
// Character representation of any object we may use as a parameter.
function objectToString(obj) {
var keyValuePairs = [];
if (Array.isArray(obj)) {
return "[" + obj.map(function(elem){return objectToString(elem);}).join(", ") + "]";
} else if (typeof obj === "object") {
Object.keys(obj).sort().forEach(function(keyName) {
keyValuePairs.push(keyName + ": " + objectToString(obj[keyName]));
});
return "{" + keyValuePairs.join(", ") + "}";
} else if (typeof obj === "undefined") {
return "undefined";
} else {
return obj.toString();
}
var keyValuePairs = [];
Object.keys(obj).sort().forEach(function(keyName) {
var value = obj[keyName];
if (typeof value === "object") {
value = objectToString(value);
} else if (typeof value === "array") {
value = "[" + value.map(function(elem){return objectToString(elem);}).join(", ") + "]";
} else {
value = value.toString();
}
keyValuePairs.push(keyName + ": " + value);
});
return "{" + keyValuePairs.join(", ") + "}";
}
// Can we compare key values by using them
function canCompareNonExtractableKeys(key){
if (key.usages.indexOf("decrypt") !== -1) {
@@ -432,6 +374,9 @@
case "ECDH" :
deriveParams = {name: "ECDH"};
break;
case "ChaCha20-Poly1305":
cryptParams = {name: "ChaCha20-Poly1305", iv: new Uint8Array(12)};
break;
default:
throw new Error("Unsupported algorithm for key comparison");
}

View File

@@ -14,8 +14,11 @@ let wrappingKeyData = {
qi: "JHmVKb1zwW5iRR6RCeexYnh2fmY-3DrPSdM8Dxhr0F8dayi-tlRqEdnG0hvp45n8gLUskWWcB9EXlUJObZGKDfGuxgMa3g_xeLA2vmFQ12MxPsyH4iCNZvsgmGxx7TuOHrnDh5EBVnM4_de63crEJON2sYI8Ozi-xp2OEmAr2seWKq4sxkFni6exLhqb-NE4m9HMKlng1EtQh2rLBFG1VYD3SYYpMLc5fxzqGvSxn3Fa-Xgg-IZPY3ubrcm52KYgmLUGmnYStfVqGSWSdhDXHlNgI5pdAA0FzpyBk3ZX-JsxhwcnneKrYBBweq06kRMGWgvdbdAQ-7wSeGqqj5VPwA"
},
},
"SYMMETRIC": {
"SYMMETRIC128": {
raw: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
},
"SYMMETRIC256": {
raw: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]),
}
}
@@ -86,8 +89,11 @@ let toWrapKeyData = {
kty: "OKP"
}
},
"SYMMETRIC": {
"SYMMETRIC128": {
raw: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
},
"SYMMETRIC256": {
raw: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]),
}
}