LibWeb: Implement CryptoAlgorithms for big-endian

This commit is contained in:
Dennis Camera 2024-07-03 20:00:57 +02:00 committed by Andrew Kaster
commit 81a0aa5725
Notes: sideshowbarker 2024-07-17 22:01:16 +09:00

View file

@ -44,8 +44,6 @@ AlgorithmMethods::~AlgorithmMethods() = default;
// https://w3c.github.io/webcrypto/#big-integer // https://w3c.github.io/webcrypto/#big-integer
static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::GCPtr<JS::Uint8Array> const& big_integer) static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::GCPtr<JS::Uint8Array> const& big_integer)
{ {
static_assert(AK::HostIsLittleEndian, "This method needs special treatment for BE");
// The BigInteger typedef is a Uint8Array that holds an arbitrary magnitude unsigned integer // The BigInteger typedef is a Uint8Array that holds an arbitrary magnitude unsigned integer
// **in big-endian order**. Values read from the API SHALL have minimal typed array length // **in big-endian order**. Values read from the API SHALL have minimal typed array length
// (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits). // (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits).
@ -55,24 +53,25 @@ static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::GCPtr<J
::Crypto::UnsignedBigInteger result(0); ::Crypto::UnsignedBigInteger result(0);
if (buffer.size() > 0) { if (buffer.size() > 0) {
if constexpr (AK::HostIsLittleEndian) {
// We need to reverse the buffer to get it into little-endian order
Vector<u8, 32> reversed_buffer;
reversed_buffer.resize(buffer.size());
for (size_t i = 0; i < buffer.size(); ++i) {
reversed_buffer[buffer.size() - i - 1] = buffer[i];
}
// We need to reverse the buffer to get it into little-endian order return ::Crypto::UnsignedBigInteger::import_data(reversed_buffer.data(), reversed_buffer.size());
Vector<u8, 32> reversed_buffer; } else {
reversed_buffer.resize(buffer.size()); return ::Crypto::UnsignedBigInteger::import_data(buffer.data(), buffer.size());
for (size_t i = 0; i < buffer.size(); ++i) {
reversed_buffer[buffer.size() - i - 1] = buffer[i];
} }
result = ::Crypto::UnsignedBigInteger::import_data(reversed_buffer.data(), reversed_buffer.size());
} }
return result; return ::Crypto::UnsignedBigInteger(0);
} }
// https://www.rfc-editor.org/rfc/rfc7518#section-2 // https://www.rfc-editor.org/rfc/rfc7518#section-2
ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger integer) ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger integer)
{ {
static_assert(AK::HostIsLittleEndian, "This code assumes little-endian");
// The representation of a positive or zero integer value as the // The representation of a positive or zero integer value as the
// base64url encoding of the value's unsigned big-endian // base64url encoding of the value's unsigned big-endian
// representation as an octet sequence. The octet sequence MUST // representation as an octet sequence. The octet sequence MUST
@ -85,15 +84,20 @@ ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger integer)
bool const remove_leading_zeroes = true; bool const remove_leading_zeroes = true;
auto data_size = integer.export_data(bytes.span(), remove_leading_zeroes); auto data_size = integer.export_data(bytes.span(), remove_leading_zeroes);
auto data_slice = bytes.bytes().slice(bytes.size() - data_size, data_size); auto data_slice_be = bytes.bytes().slice(bytes.size() - data_size, data_size);
// We need to encode the integer's big endian representation as a base64 string String encoded;
Vector<u8, 32> byte_swapped_data; if constexpr (AK::HostIsLittleEndian) {
byte_swapped_data.ensure_capacity(data_size); // We need to encode the integer's big endian representation as a base64 string
for (size_t i = 0; i < data_size; ++i) Vector<u8, 32> data_slice_cpu;
byte_swapped_data.append(data_slice[data_size - i - 1]); data_slice_cpu.ensure_capacity(data_size);
for (size_t i = 0; i < data_size; ++i) {
auto encoded = TRY(encode_base64url(byte_swapped_data)); data_slice_cpu.append(data_slice_be[data_size - i - 1]);
}
encoded = TRY(encode_base64url(data_slice_cpu));
} else {
encoded = TRY(encode_base64url(data_slice_be));
}
// FIXME: create a version of encode_base64url that omits padding bytes // FIXME: create a version of encode_base64url that omits padding bytes
if (auto first_padding_byte = encoded.find_byte_offset('='); first_padding_byte.has_value()) if (auto first_padding_byte = encoded.find_byte_offset('='); first_padding_byte.has_value())
@ -104,7 +108,6 @@ ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger integer)
WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm& realm, String const& base64_url_string) WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm& realm, String const& base64_url_string)
{ {
auto& vm = realm.vm(); auto& vm = realm.vm();
static_assert(AK::HostIsLittleEndian, "This code assumes little-endian");
// FIXME: Create a version of decode_base64url that ignores padding inconsistencies // FIXME: Create a version of decode_base64url that ignores padding inconsistencies
auto padded_string = base64_url_string; auto padded_string = base64_url_string;
@ -118,15 +121,19 @@ WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Rea
return vm.throw_completion<JS::InternalError>(vm.error_message(::JS::VM::ErrorMessage::OutOfMemory)); return vm.throw_completion<JS::InternalError>(vm.error_message(::JS::VM::ErrorMessage::OutOfMemory));
return WebIDL::DataError::create(realm, MUST(String::formatted("base64 decode: {}", base64_bytes_or_error.release_error()))); return WebIDL::DataError::create(realm, MUST(String::formatted("base64 decode: {}", base64_bytes_or_error.release_error())));
} }
auto base64_bytes = base64_bytes_or_error.release_value(); auto base64_bytes_be = base64_bytes_or_error.release_value();
// We need to swap the integer's big-endian representation to little endian in order to import it if constexpr (AK::HostIsLittleEndian) {
Vector<u8, 32> byte_swapped_data; // We need to swap the integer's big-endian representation to little endian in order to import it
byte_swapped_data.ensure_capacity(base64_bytes.size()); Vector<u8, 32> base64_bytes_cpu;
for (size_t i = 0; i < base64_bytes.size(); ++i) base64_bytes_cpu.ensure_capacity(base64_bytes_be.size());
byte_swapped_data.append(base64_bytes[base64_bytes.size() - i - 1]); for (size_t i = 0; i < base64_bytes_be.size(); ++i) {
base64_bytes_cpu.append(base64_bytes_be[base64_bytes_be.size() - i - 1]);
return ::Crypto::UnsignedBigInteger::import_data(byte_swapped_data.data(), byte_swapped_data.size()); }
return ::Crypto::UnsignedBigInteger::import_data(base64_bytes_cpu.data(), base64_bytes_cpu.size());
} else {
return ::Crypto::UnsignedBigInteger::import_data(base64_bytes_be.data(), base64_bytes_be.size());
}
} }
// https://w3c.github.io/webcrypto/#concept-parse-an-asn1-structure // https://w3c.github.io/webcrypto/#concept-parse-an-asn1-structure