mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-26 01:35:08 +02:00
LibCrypto+LibWeb: Check RSA keys validity on SubtleCrypto import_key
Fix various TODO by checking the validity of RSA keys when they are imported. Also add some internal tests since WPT doesn't seem to provide them.
This commit is contained in:
Notes:
github-actions[bot]
2025-06-25 00:23:02 +00:00
Author: https://github.com/devgianlu Commit: https://github.com/LadybirdBrowser/ladybird/commit/4e747f525a1 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4950 Reviewed-by: https://github.com/ADKaster Reviewed-by: https://github.com/alimpfard Reviewed-by: https://github.com/konradekk Reviewed-by: https://github.com/shannonbooth
@@ -0,0 +1,80 @@
|
||||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
function b64ToBn(b64) {
|
||||
const bin = atob(b64.replace(/-/g, '+').replace(/_/g, '/'));
|
||||
const hex = bin.split('').map((ch) => ch.charCodeAt(0).toString(16).padStart(2, '0')).join('');
|
||||
return [BigInt('0x' + hex), bin.length];
|
||||
}
|
||||
|
||||
function bnToB64(bn, len) {
|
||||
const hex = bn.toString(16);
|
||||
const bin = hex.match(/.{1,2}/g).map((byte) => String.fromCharCode(parseInt(byte, 16))).join('');
|
||||
return btoa(bin.padStart(len, '\x00')).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||||
}
|
||||
|
||||
function corruptJwk(key) {
|
||||
const [bn, len] = b64ToBn(key.d);
|
||||
|
||||
// Corrupt the private key
|
||||
return {
|
||||
...key,
|
||||
d: bnToB64(bn ^ 1n, len),
|
||||
};
|
||||
}
|
||||
|
||||
function corruptPkcs8Pub(privateKey) {
|
||||
// Corrupt the public key, which is encoded between 33 and 165 for the PKCS8
|
||||
const corrupted = new Uint8Array(privateKey);
|
||||
corrupted[128] ^= 0x01;
|
||||
return corrupted.buffer;
|
||||
}
|
||||
|
||||
function corruptPkcs8Priv(privateKey) {
|
||||
// Corrupt a piece of the private key, which is encoded at the end of the PKCS8
|
||||
const corrupted = new Uint8Array(privateKey);
|
||||
corrupted[corrupted.length - 20] ^= 0x01;
|
||||
return corrupted.buffer;
|
||||
}
|
||||
|
||||
asyncTest(async done => {
|
||||
for (const [name, genUsages, privUsages] of [
|
||||
["RSASSA-PKCS1-v1_5", ["sign", "verify"], ["sign"]],
|
||||
["RSA-PSS", ["sign", "verify"], ["sign"]],
|
||||
["RSA-OAEP", ["encrypt", "decrypt"], ["decrypt"]],
|
||||
]) {
|
||||
for (const hash of ["SHA-1", "SHA-256", "SHA-384", "SHA-512"]) {
|
||||
const keyPair = await window.crypto.subtle.generateKey({
|
||||
name: name,
|
||||
hash: hash,
|
||||
modulusLength: 1024,
|
||||
publicExponent: new Uint8Array([1, 0, 1]),
|
||||
}, true, genUsages);
|
||||
|
||||
// NOTE: We don't check the public key for RSA since it's harder to corrupt.
|
||||
|
||||
for (const [format, corruptFn] of [["jwk", corruptJwk], ["pkcs8", corruptPkcs8Priv], ["pkcs8", corruptPkcs8Pub]]) {
|
||||
const privateKey = await window.crypto.subtle.exportKey(format, keyPair.privateKey);
|
||||
|
||||
// Prove that the original key can be imported successfully
|
||||
await window.crypto.subtle.importKey(format, privateKey, {name: name, hash: hash,}, false, privUsages);
|
||||
|
||||
// Corrupt the private key and try to import it
|
||||
const corruptedPrivateKey = corruptFn(privateKey);
|
||||
|
||||
try {
|
||||
await window.crypto.subtle.importKey(format, corruptedPrivateKey, {
|
||||
name: name,
|
||||
hash: hash,
|
||||
}, false, privUsages);
|
||||
println(`${name.padEnd(5, ' ')} ${hash} PRIV - ${format.padEnd(5, ' ')} FAILED`);
|
||||
} catch (e) {
|
||||
println(`${name.padEnd(5, ' ')} ${hash} PRIV - ${format.padEnd(5, ' ')} OK: ${e}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user