LibWeb: Don’t crash on a detached publicExponent in generateKey
Problem: Crash when generating an RSA key — or serializing one — whose publicExponent is a typed array whose backing ArrayBuffer has been detached; for example, by calling transfer() on it. Cause: Two places with big_integer_from_api_big_integer() reading the bytes of the backing ArrayBuffer directly. But reading the bytes of a detached buffer aborts. Fix: Read the bytes with WebIDL get_buffer_source_copy() — which yields an empty copy for a detached, or OOB resizable, buffer. The empty array is already mapped to zero — so generation rejects the zero exponent with an error, rather than crashing. Fixes https://github.com/LadybirdBrowser/ladybird/issues/9991
This commit is contained in:
parent
9183c2a029
commit
9ffd3e48c3
6 changed files with 52 additions and 3 deletions
|
|
@ -91,8 +91,7 @@ static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(GC::Ptr<JS:
|
|||
// (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits).
|
||||
// The API SHALL accept values with any number of leading zero bits, including the empty array, which represents zero.
|
||||
|
||||
auto buffer = big_integer->viewed_array_buffer()->bytes();
|
||||
|
||||
auto buffer = MUST(WebIDL::get_buffer_source_copy(*big_integer));
|
||||
if (!buffer.is_empty())
|
||||
return ::Crypto::UnsignedBigInteger::import_data(buffer);
|
||||
return ::Crypto::UnsignedBigInteger(0);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibWeb/Crypto/CryptoKey.h>
|
||||
#include <LibWeb/Crypto/KeyAlgorithms.h>
|
||||
#include <LibWeb/HTML/StructuredSerialize.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
|
||||
namespace Web::Crypto {
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ enum class KeyAlgorithmTag : u8 {
|
|||
|
||||
::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::Uint8Array const& big_integer)
|
||||
{
|
||||
auto buffer = big_integer.viewed_array_buffer()->bytes().slice(big_integer.byte_offset(), big_integer.byte_length().length());
|
||||
auto buffer = MUST(WebIDL::get_buffer_source_copy(big_integer));
|
||||
if (!buffer.is_empty())
|
||||
return ::Crypto::UnsignedBigInteger::import_data(buffer);
|
||||
return ::Crypto::UnsignedBigInteger(0);
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
rejected: OperationError
|
||||
|
|
@ -0,0 +1 @@
|
|||
PASS (didn't crash): cloned
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest(async done => {
|
||||
const exponentBuffer = new ArrayBuffer(3);
|
||||
const publicExponent = new Uint8Array(exponentBuffer);
|
||||
publicExponent.set([0x01, 0x00, 0x01]);
|
||||
|
||||
// transfer() detaches exponentBuffer, so publicExponent views a detached buffer (byteLength 0).
|
||||
exponentBuffer.transfer();
|
||||
|
||||
try {
|
||||
await crypto.subtle.generateKey({
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
modulusLength: 1024,
|
||||
publicExponent,
|
||||
hash: "SHA-256",
|
||||
}, true, ["sign", "verify"]);
|
||||
println("resolved");
|
||||
} catch (e) {
|
||||
println("rejected: " + e.name);
|
||||
}
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="../include.js"></script>
|
||||
<script>
|
||||
asyncTest(async done => {
|
||||
const keyPair = await crypto.subtle.generateKey({
|
||||
name: "RSASSA-PKCS1-v1_5",
|
||||
modulusLength: 1024,
|
||||
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
|
||||
hash: "SHA-256",
|
||||
}, true, ["sign", "verify"]);
|
||||
|
||||
// Detach the buffer backing the stored publicExponent, then serialize the key by cloning it.
|
||||
keyPair.publicKey.algorithm.publicExponent.buffer.transfer();
|
||||
try {
|
||||
structuredClone(keyPair.publicKey);
|
||||
println("PASS (didn't crash): cloned");
|
||||
} catch (e) {
|
||||
println("PASS (didn't crash): " + e.name);
|
||||
}
|
||||
done();
|
||||
});
|
||||
</script>
|
||||
Loading…
Reference in a new issue