mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-28 10:37:17 +02:00
LibWeb: Add ML-DSA test
This represents 1600+ subtests, including the worker tests.
This commit is contained in:
committed by
Jelle Raaijmakers
parent
006428db7a
commit
4868a118a5
Notes:
github-actions[bot]
2025-12-10 20:27:51 +00:00
Author: https://github.com/tete17 Commit: https://github.com/LadybirdBrowser/ladybird/commit/4868a118a5f Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6935 Reviewed-by: https://github.com/R-Goc Reviewed-by: https://github.com/gmta ✅
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>WebCryptoAPI: generateKey() for Failures</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../util/helpers.js"></script>
|
||||
<script src="failures.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../../WebCryptoAPI/generateKey/failures_ML-DSA.tentative.https.any.js"></script>
|
||||
@@ -0,0 +1,5 @@
|
||||
// META: title=WebCryptoAPI: generateKey() for Failures
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
// META: script=failures.js
|
||||
run_test(["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]);
|
||||
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>WebCryptoAPI: generateKey() Successful Calls</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../util/helpers.js"></script>
|
||||
<script src="../../common/subset-tests.js"></script>
|
||||
<script src="successes.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../../WebCryptoAPI/generateKey/successes_ML-DSA.tentative.https.any.js"></script>
|
||||
@@ -0,0 +1,6 @@
|
||||
// META: title=WebCryptoAPI: generateKey() Successful Calls
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
// META: script=/common/subset-tests.js
|
||||
// META: script=successes.js
|
||||
run_test(["ML-DSA-44", "ML-DSA-65", "ML-DSA-87"]);
|
||||
@@ -0,0 +1,222 @@
|
||||
var subtle = crypto.subtle;
|
||||
|
||||
function runTests(algorithmName) {
|
||||
var algorithm = { name: algorithmName };
|
||||
var data = keyData[algorithmName];
|
||||
var jwkData = {
|
||||
jwk: { kty: data.jwk.kty, alg: data.jwk.alg, pub: data.jwk.pub },
|
||||
};
|
||||
|
||||
[true, false].forEach(function (extractable) {
|
||||
// Test public keys first
|
||||
allValidUsages(data.publicUsages, true).forEach(function (usages) {
|
||||
['spki', 'jwk', 'raw-public'].forEach(function (format) {
|
||||
if (format === 'jwk') {
|
||||
// Not all fields used for public keys
|
||||
testFormat(
|
||||
format,
|
||||
algorithm,
|
||||
jwkData,
|
||||
algorithmName,
|
||||
usages,
|
||||
extractable
|
||||
);
|
||||
} else {
|
||||
testFormat(
|
||||
format,
|
||||
algorithm,
|
||||
data,
|
||||
algorithmName,
|
||||
usages,
|
||||
extractable
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Next, test private keys
|
||||
allValidUsages(data.privateUsages).forEach(function (usages) {
|
||||
['pkcs8', 'jwk', 'raw-seed'].forEach(function (format) {
|
||||
testFormat(format, algorithm, data, algorithmName, usages, extractable);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Test importKey with a given key format and other parameters. If
|
||||
// extrable is true, export the key and verify that it matches the input.
|
||||
function testFormat(format, algorithm, keyData, keySize, usages, extractable) {
|
||||
[algorithm, algorithm.name].forEach((alg) => {
|
||||
promise_test(function (test) {
|
||||
return subtle
|
||||
.importKey(format, keyData[format], alg, extractable, usages)
|
||||
.then(
|
||||
function (key) {
|
||||
assert_equals(
|
||||
key.constructor,
|
||||
CryptoKey,
|
||||
'Imported a CryptoKey object'
|
||||
);
|
||||
assert_goodCryptoKey(
|
||||
key,
|
||||
algorithm,
|
||||
extractable,
|
||||
usages,
|
||||
format === 'pkcs8' ||
|
||||
format === 'raw-seed' ||
|
||||
(format === 'jwk' && keyData[format].priv)
|
||||
? 'private'
|
||||
: 'public'
|
||||
);
|
||||
if (!extractable) {
|
||||
return;
|
||||
}
|
||||
|
||||
return subtle.exportKey(format, key).then(
|
||||
function (result) {
|
||||
if (format !== 'jwk') {
|
||||
assert_true(
|
||||
equalBuffers(keyData[format], result),
|
||||
'Round trip works'
|
||||
);
|
||||
} else {
|
||||
assert_true(
|
||||
equalJwk(keyData[format], result),
|
||||
'Round trip works'
|
||||
);
|
||||
}
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Threw an unexpected error: ' + err.toString()
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached('Threw an unexpected error: ' + err.toString());
|
||||
}
|
||||
);
|
||||
}, 'Good parameters: ' +
|
||||
keySize.toString() +
|
||||
' bits ' +
|
||||
parameterString(format, keyData[format], alg, extractable, usages));
|
||||
});
|
||||
}
|
||||
|
||||
// Helper methods follow:
|
||||
|
||||
// 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 (expected[fieldName] !== got[fieldName]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Convert method parameters to a string to uniquely name each test
|
||||
function parameterString(format, data, algorithm, extractable, usages) {
|
||||
if ('byteLength' in data) {
|
||||
data = 'buffer(' + data.byteLength.toString() + ')';
|
||||
} else {
|
||||
data = 'object(' + Object.keys(data).join(', ') + ')';
|
||||
}
|
||||
var result =
|
||||
'(' +
|
||||
objectToString(format) +
|
||||
', ' +
|
||||
objectToString(data) +
|
||||
', ' +
|
||||
objectToString(algorithm) +
|
||||
', ' +
|
||||
objectToString(extractable) +
|
||||
', ' +
|
||||
objectToString(usages) +
|
||||
')';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// 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(', ') + '}';
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>WebCryptoAPI: importKey() for ML-DSA keys</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="../util/helpers.js"></script>
|
||||
<script src="ML-DSA_importKey_fixtures.js"></script>
|
||||
<script src="ML-DSA_importKey.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../../WebCryptoAPI/import_export/ML-DSA_importKey.tentative.https.any.js"></script>
|
||||
@@ -0,0 +1,9 @@
|
||||
// META: title=WebCryptoAPI: importKey() for ML-DSA keys
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
// META: script=ML-DSA_importKey_fixtures.js
|
||||
// META: script=ML-DSA_importKey.js
|
||||
|
||||
runTests("ML-DSA-44");
|
||||
runTests("ML-DSA-65");
|
||||
runTests("ML-DSA-87");
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,757 @@
|
||||
function run_test() {
|
||||
setup({ explicit_done: true });
|
||||
|
||||
var subtle = self.crypto.subtle; // Change to test prefixed implementations
|
||||
|
||||
// When are all these tests really done? When all the promises they use have resolved.
|
||||
var all_promises = [];
|
||||
|
||||
// Source file [algorithm_name]_vectors.js provides the getTestVectors method
|
||||
// for the algorithm that drives these tests.
|
||||
var testVectors = getTestVectors();
|
||||
var invalidTestVectors = getInvalidTestVectors();
|
||||
|
||||
// Test verification first, because signing tests rely on that working
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_true(is_verified, 'Signature verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verification');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' + vector.name + ' verification');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test verification with an altered buffer after call
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
var signature = copyBuffer(vector.signature);
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_true(is_verified, 'Signature verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
signature[0] = 255 - signature[0];
|
||||
return operation;
|
||||
}, vector.name + ' verification with altered signature after call');
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' verification with altered signature after call');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Check for successful verification even if plaintext is altered after call.
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
var plaintext = copyBuffer(vector.data);
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, vector.signature, plaintext)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_true(is_verified, 'Signature verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
plaintext[0] = 255 - plaintext[0];
|
||||
return operation;
|
||||
}, vector.name + ' with altered plaintext after call');
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' with altered plaintext after call');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Check for failures due to using privateKey to verify.
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
return subtle
|
||||
.verify(algorithm, vector.privateKey, vector.signature, vector.data)
|
||||
.then(
|
||||
function (plaintext) {
|
||||
assert_unreached(
|
||||
'Should have thrown error for using privateKey to verify in ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_equals(
|
||||
err.name,
|
||||
'InvalidAccessError',
|
||||
"Should throw InvalidAccessError instead of '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
}, vector.name + ' using privateKey to verify');
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' using privateKey to verify');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Check for failures due to using publicKey to sign.
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
return subtle.sign(algorithm, vector.publicKey, vector.data).then(
|
||||
function (signature) {
|
||||
assert_unreached(
|
||||
'Should have thrown error for using publicKey to sign in ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_equals(
|
||||
err.name,
|
||||
'InvalidAccessError',
|
||||
"Should throw InvalidAccessError instead of '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
}, vector.name + ' using publicKey to sign');
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' using publicKey to sign');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Check for failures due to no "verify" usage.
|
||||
testVectors.forEach(function (originalVector) {
|
||||
var vector = Object.assign({}, originalVector);
|
||||
|
||||
var promise = importVectorKeys(vector, [], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
return subtle
|
||||
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
|
||||
.then(
|
||||
function (plaintext) {
|
||||
assert_unreached(
|
||||
'Should have thrown error for no verify usage in ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_equals(
|
||||
err.name,
|
||||
'InvalidAccessError',
|
||||
"Should throw InvalidAccessError instead of '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
}, vector.name + ' no verify usage');
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' + vector.name + ' no verify usage');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Check for successful signing and verification.
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
return subtle.sign(algorithm, vector.privateKey, vector.data).then(
|
||||
function (signature) {
|
||||
// Can we verify the signature?
|
||||
return subtle
|
||||
.verify(algorithm, vector.publicKey, signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_true(is_verified, 'Round trip verification works');
|
||||
return signature;
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'verify error for test ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'sign error for test ' + vector.name + ": '" + err.message + "'"
|
||||
);
|
||||
}
|
||||
);
|
||||
}, vector.name + ' round trip');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested signing or verifying
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' + vector.name + ' round trip');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test signing with the wrong algorithm
|
||||
testVectors.forEach(function (vector) {
|
||||
// Want to get the key for the wrong algorithm
|
||||
var promise = subtle
|
||||
.generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
|
||||
.then(
|
||||
function (wrongKey) {
|
||||
var algorithm = vector.algorithmName;
|
||||
return importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.sign(algorithm, wrongKey, vector.data)
|
||||
.then(
|
||||
function (signature) {
|
||||
assert_unreached(
|
||||
'Signing should not have succeeded for ' + vector.name
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_equals(
|
||||
err.name,
|
||||
'InvalidAccessError',
|
||||
"Should have thrown InvalidAccessError instead of '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' signing with wrong algorithm name');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' signing with wrong algorithm name');
|
||||
}
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'Generate wrong key for test ' +
|
||||
vector.name +
|
||||
" failed: '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}, 'generate wrong key step: ' +
|
||||
vector.name +
|
||||
' signing with wrong algorithm name');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test verification with the wrong algorithm
|
||||
testVectors.forEach(function (vector) {
|
||||
// Want to get the key for the wrong algorithm
|
||||
var promise = subtle
|
||||
.generateKey({ name: 'HMAC', hash: 'SHA-1' }, false, ['sign', 'verify'])
|
||||
.then(
|
||||
function (wrongKey) {
|
||||
return importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, wrongKey, vector.signature, vector.data)
|
||||
.then(
|
||||
function (signature) {
|
||||
assert_unreached(
|
||||
'Verifying should not have succeeded for ' + vector.name
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
assert_equals(
|
||||
err.name,
|
||||
'InvalidAccessError',
|
||||
"Should have thrown InvalidAccessError instead of '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verifying with wrong algorithm name');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' verifying with wrong algorithm name');
|
||||
}
|
||||
);
|
||||
},
|
||||
function (err) {
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'Generate wrong key for test ' +
|
||||
vector.name +
|
||||
" failed: '" +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}, 'generate wrong key step: ' +
|
||||
vector.name +
|
||||
' verifying with wrong algorithm name');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test verification fails with wrong signature
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
var signature = copyBuffer(vector.signature);
|
||||
signature[0] = 255 - signature[0];
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_false(is_verified, 'Signature NOT verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verification failure due to altered signature');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' verification failure due to altered signature');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test verification fails with short (odd length) signature
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
var signature = vector.signature.slice(1); // Skip the first byte
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_false(is_verified, 'Signature NOT verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verification failure due to shortened signature');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' verification failure due to shortened signature');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test verification fails with wrong plaintext
|
||||
testVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
var plaintext = copyBuffer(vector.data);
|
||||
plaintext[0] = 255 - plaintext[0];
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, vector.signature, plaintext)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_false(is_verified, 'Signature NOT verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verification failure due to altered plaintext');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' +
|
||||
vector.name +
|
||||
' verification failure due to altered plaintext');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
// Test invalid signatures
|
||||
invalidTestVectors.forEach(function (vector) {
|
||||
var promise = importVectorKeys(vector, ['verify'], ['sign']).then(
|
||||
function (vectors) {
|
||||
var algorithm = vector.algorithmName;
|
||||
promise_test(function (test) {
|
||||
var operation = subtle
|
||||
.verify(algorithm, vector.publicKey, vector.signature, vector.data)
|
||||
.then(
|
||||
function (is_verified) {
|
||||
assert_false(is_verified, 'Signature unexpectedly verified');
|
||||
},
|
||||
function (err) {
|
||||
assert_unreached(
|
||||
'Verification should not throw error ' +
|
||||
vector.name +
|
||||
': ' +
|
||||
err.message +
|
||||
"'"
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return operation;
|
||||
}, vector.name + ' verification');
|
||||
},
|
||||
function (err) {
|
||||
// We need a failed test if the importVectorKey operation fails, so
|
||||
// we know we never tested verification.
|
||||
promise_test(function (test) {
|
||||
assert_unreached(
|
||||
'importVectorKeys failed for ' +
|
||||
vector.name +
|
||||
". Message: ''" +
|
||||
err.message +
|
||||
"''"
|
||||
);
|
||||
}, 'importVectorKeys step: ' + vector.name + ' verification');
|
||||
}
|
||||
);
|
||||
|
||||
all_promises.push(promise);
|
||||
});
|
||||
|
||||
promise_test(function () {
|
||||
return Promise.all(all_promises)
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(function () {
|
||||
done();
|
||||
});
|
||||
}, 'setup');
|
||||
|
||||
// A test vector has all needed fields for signing and verifying, EXCEPT that the
|
||||
// key field may be null. This function replaces that null with the Correct
|
||||
// CryptoKey object.
|
||||
//
|
||||
// Returns a Promise that yields an updated vector on success.
|
||||
function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) {
|
||||
var publicPromise, privatePromise;
|
||||
|
||||
if (vector.publicKey !== null) {
|
||||
publicPromise = new Promise(function (resolve, reject) {
|
||||
resolve(vector);
|
||||
});
|
||||
} else {
|
||||
publicPromise = subtle
|
||||
.importKey(
|
||||
vector.publicKeyFormat,
|
||||
vector.publicKeyBuffer,
|
||||
{ name: vector.algorithmName },
|
||||
false,
|
||||
publicKeyUsages
|
||||
)
|
||||
.then(function (key) {
|
||||
vector.publicKey = key;
|
||||
return vector;
|
||||
}); // Returns a copy of the sourceBuffer it is sent.
|
||||
}
|
||||
|
||||
if (vector.privateKey !== null) {
|
||||
privatePromise = new Promise(function (resolve, reject) {
|
||||
resolve(vector);
|
||||
});
|
||||
} else {
|
||||
privatePromise = subtle
|
||||
.importKey(
|
||||
vector.privateKeyFormat,
|
||||
vector.privateKeyBuffer,
|
||||
{ name: vector.algorithmName },
|
||||
false,
|
||||
privateKeyUsages
|
||||
)
|
||||
.then(function (key) {
|
||||
vector.privateKey = key;
|
||||
return vector;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.all([publicPromise, privatePromise]);
|
||||
}
|
||||
|
||||
// Returns a copy of the sourceBuffer it is sent.
|
||||
function copyBuffer(sourceBuffer) {
|
||||
var source = new Uint8Array(sourceBuffer);
|
||||
var copy = new Uint8Array(sourceBuffer.byteLength);
|
||||
|
||||
for (var i = 0; i < source.byteLength; i++) {
|
||||
copy[i] = source[i];
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<title>WebCryptoAPI: sign() and verify() Using ML-DSA</title>
|
||||
<meta name="timeout" content="long">
|
||||
<script>
|
||||
self.GLOBAL = {
|
||||
isWindow: function() { return true; },
|
||||
isWorker: function() { return false; },
|
||||
isShadowRealm: function() { return false; },
|
||||
};
|
||||
</script>
|
||||
<script src="../../resources/testharness.js"></script>
|
||||
<script src="../../resources/testharnessreport.js"></script>
|
||||
<script src="mldsa_vectors.js"></script>
|
||||
<script src="mldsa.js"></script>
|
||||
<div id=log></div>
|
||||
<script src="../../WebCryptoAPI/sign_verify/mldsa.tentative.https.any.js"></script>
|
||||
@@ -0,0 +1,6 @@
|
||||
// META: title=WebCryptoAPI: sign() and verify() Using ML-DSA
|
||||
// META: script=mldsa_vectors.js
|
||||
// META: script=mldsa.js
|
||||
// META: timeout=long
|
||||
|
||||
run_test();
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user