/src/serenity/Userland/Libraries/LibWeb/Crypto/CryptoAlgorithms.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2024, Andrew Kaster <akaster@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/Base64.h> |
8 | | #include <AK/QuickSort.h> |
9 | | #include <LibCrypto/ASN1/DER.h> |
10 | | #include <LibCrypto/Authentication/HMAC.h> |
11 | | #include <LibCrypto/Curves/Ed25519.h> |
12 | | #include <LibCrypto/Curves/SECPxxxr1.h> |
13 | | #include <LibCrypto/Hash/HKDF.h> |
14 | | #include <LibCrypto/Hash/HashManager.h> |
15 | | #include <LibCrypto/Hash/MGF.h> |
16 | | #include <LibCrypto/Hash/PBKDF2.h> |
17 | | #include <LibCrypto/Hash/SHA1.h> |
18 | | #include <LibCrypto/Hash/SHA2.h> |
19 | | #include <LibCrypto/PK/RSA.h> |
20 | | #include <LibCrypto/Padding/OAEP.h> |
21 | | #include <LibJS/Runtime/ArrayBuffer.h> |
22 | | #include <LibJS/Runtime/DataView.h> |
23 | | #include <LibJS/Runtime/TypedArray.h> |
24 | | #include <LibTLS/Certificate.h> |
25 | | #include <LibWeb/Crypto/CryptoAlgorithms.h> |
26 | | #include <LibWeb/Crypto/KeyAlgorithms.h> |
27 | | #include <LibWeb/Crypto/SubtleCrypto.h> |
28 | | #include <LibWeb/WebIDL/AbstractOperations.h> |
29 | | |
30 | | namespace Web::Crypto { |
31 | | |
32 | | // https://w3c.github.io/webcrypto/#concept-usage-intersection |
33 | | static Vector<Bindings::KeyUsage> usage_intersection(ReadonlySpan<Bindings::KeyUsage> a, ReadonlySpan<Bindings::KeyUsage> b) |
34 | 0 | { |
35 | 0 | Vector<Bindings::KeyUsage> result; |
36 | 0 | for (auto const& usage : a) { |
37 | 0 | if (b.contains_slow(usage)) |
38 | 0 | result.append(usage); |
39 | 0 | } |
40 | 0 | quick_sort(result); |
41 | 0 | return result; |
42 | 0 | } |
43 | | |
44 | | // Out of line to ensure this class has a key function |
45 | 0 | AlgorithmMethods::~AlgorithmMethods() = default; |
46 | | |
47 | | // https://w3c.github.io/webcrypto/#big-integer |
48 | | static ::Crypto::UnsignedBigInteger big_integer_from_api_big_integer(JS::GCPtr<JS::Uint8Array> const& big_integer) |
49 | 0 | { |
50 | 0 | static_assert(AK::HostIsLittleEndian, "This method needs special treatment for BE"); |
51 | | |
52 | | // The BigInteger typedef is a Uint8Array that holds an arbitrary magnitude unsigned integer |
53 | | // **in big-endian order**. Values read from the API SHALL have minimal typed array length |
54 | | // (that is, at most 7 leading zero bits, except the value 0 which shall have length 8 bits). |
55 | | // The API SHALL accept values with any number of leading zero bits, including the empty array, which represents zero. |
56 | |
|
57 | 0 | auto const& buffer = big_integer->viewed_array_buffer()->buffer(); |
58 | |
|
59 | 0 | ::Crypto::UnsignedBigInteger result(0); |
60 | 0 | if (buffer.size() > 0) { |
61 | | |
62 | | // We need to reverse the buffer to get it into little-endian order |
63 | 0 | Vector<u8, 32> reversed_buffer; |
64 | 0 | reversed_buffer.resize(buffer.size()); |
65 | 0 | for (size_t i = 0; i < buffer.size(); ++i) { |
66 | 0 | reversed_buffer[buffer.size() - i - 1] = buffer[i]; |
67 | 0 | } |
68 | |
|
69 | 0 | result = ::Crypto::UnsignedBigInteger::import_data(reversed_buffer.data(), reversed_buffer.size()); |
70 | 0 | } |
71 | 0 | return result; |
72 | 0 | } |
73 | | |
74 | | // https://www.rfc-editor.org/rfc/rfc7518#section-2 |
75 | | ErrorOr<String> base64_url_uint_encode(::Crypto::UnsignedBigInteger integer) |
76 | 0 | { |
77 | 0 | static_assert(AK::HostIsLittleEndian, "This code assumes little-endian"); |
78 | | |
79 | | // The representation of a positive or zero integer value as the |
80 | | // base64url encoding of the value's unsigned big-endian |
81 | | // representation as an octet sequence. The octet sequence MUST |
82 | | // utilize the minimum number of octets needed to represent the |
83 | | // value. Zero is represented as BASE64URL(single zero-valued |
84 | | // octet), which is "AA". |
85 | |
|
86 | 0 | auto bytes = TRY(ByteBuffer::create_uninitialized(integer.trimmed_byte_length())); |
87 | |
|
88 | 0 | bool const remove_leading_zeroes = true; |
89 | 0 | auto data_size = integer.export_data(bytes.span(), remove_leading_zeroes); |
90 | |
|
91 | 0 | auto data_slice = bytes.bytes().slice(bytes.size() - data_size, data_size); |
92 | | |
93 | | // We need to encode the integer's big endian representation as a base64 string |
94 | 0 | Vector<u8, 32> byte_swapped_data; |
95 | 0 | byte_swapped_data.ensure_capacity(data_size); |
96 | 0 | for (size_t i = 0; i < data_size; ++i) |
97 | 0 | byte_swapped_data.append(data_slice[data_size - i - 1]); |
98 | |
|
99 | 0 | auto encoded = TRY(encode_base64url(byte_swapped_data)); |
100 | | |
101 | | // FIXME: create a version of encode_base64url that omits padding bytes |
102 | 0 | if (auto first_padding_byte = encoded.find_byte_offset('='); first_padding_byte.has_value()) |
103 | 0 | return encoded.substring_from_byte_offset(0, first_padding_byte.value()); |
104 | 0 | return encoded; |
105 | 0 | } |
106 | | |
107 | | WebIDL::ExceptionOr<::Crypto::UnsignedBigInteger> base64_url_uint_decode(JS::Realm& realm, String const& base64_url_string) |
108 | 0 | { |
109 | 0 | auto& vm = realm.vm(); |
110 | 0 | static_assert(AK::HostIsLittleEndian, "This code assumes little-endian"); |
111 | | |
112 | | // FIXME: Create a version of decode_base64url that ignores padding inconsistencies |
113 | 0 | auto padded_string = base64_url_string; |
114 | 0 | if (padded_string.byte_count() % 4 != 0) { |
115 | 0 | padded_string = TRY_OR_THROW_OOM(vm, String::formatted("{}{}", padded_string, TRY_OR_THROW_OOM(vm, String::repeated('=', 4 - (padded_string.byte_count() % 4))))); |
116 | 0 | } |
117 | |
|
118 | 0 | auto base64_bytes_or_error = decode_base64url(padded_string); |
119 | 0 | if (base64_bytes_or_error.is_error()) { |
120 | 0 | if (base64_bytes_or_error.error().code() == ENOMEM) |
121 | 0 | return vm.throw_completion<JS::InternalError>(vm.error_message(::JS::VM::ErrorMessage::OutOfMemory)); |
122 | 0 | return WebIDL::DataError::create(realm, MUST(String::formatted("base64 decode: {}", base64_bytes_or_error.release_error()))); |
123 | 0 | } |
124 | 0 | auto base64_bytes = base64_bytes_or_error.release_value(); |
125 | | |
126 | | // We need to swap the integer's big-endian representation to little endian in order to import it |
127 | 0 | Vector<u8, 32> byte_swapped_data; |
128 | 0 | byte_swapped_data.ensure_capacity(base64_bytes.size()); |
129 | 0 | for (size_t i = 0; i < base64_bytes.size(); ++i) |
130 | 0 | byte_swapped_data.append(base64_bytes[base64_bytes.size() - i - 1]); |
131 | |
|
132 | 0 | return ::Crypto::UnsignedBigInteger::import_data(byte_swapped_data.data(), byte_swapped_data.size()); |
133 | 0 | } |
134 | | |
135 | | // https://w3c.github.io/webcrypto/#concept-parse-an-asn1-structure |
136 | | template<typename Structure> |
137 | | static WebIDL::ExceptionOr<Structure> parse_an_ASN1_structure(JS::Realm& realm, ReadonlyBytes data, bool exact_data = true) |
138 | 0 | { |
139 | | // 1. Let data be a sequence of bytes to be parsed. |
140 | | // 2. Let structure be the ASN.1 structure to be parsed. |
141 | | // 3. Let exactData be an optional boolean value. If it is not supplied, let it be initialized to true. |
142 | | |
143 | | // 4. Parse data according to the Distinguished Encoding Rules of [X690], using structure as the ASN.1 structure to be decoded. |
144 | 0 | ::Crypto::ASN1::Decoder decoder(data); |
145 | 0 | Structure structure; |
146 | 0 | if constexpr (IsSame<Structure, TLS::SubjectPublicKey>) { |
147 | 0 | auto maybe_subject_public_key = TLS::parse_subject_public_key_info(decoder); |
148 | 0 | if (maybe_subject_public_key.is_error()) |
149 | 0 | return WebIDL::DataError::create(realm, MUST(String::formatted("Error parsing subjectPublicKeyInfo: {}", maybe_subject_public_key.release_error()))); |
150 | 0 | structure = maybe_subject_public_key.release_value(); |
151 | 0 | } else if constexpr (IsSame<Structure, TLS::PrivateKey>) { |
152 | 0 | auto maybe_private_key = TLS::parse_private_key_info(decoder); |
153 | 0 | if (maybe_private_key.is_error()) |
154 | 0 | return WebIDL::DataError::create(realm, MUST(String::formatted("Error parsing privateKeyInfo: {}", maybe_private_key.release_error()))); |
155 | 0 | structure = maybe_private_key.release_value(); |
156 | | } else { |
157 | | static_assert(DependentFalse<Structure>, "Don't know how to parse ASN.1 structure type"); |
158 | | } |
159 | | |
160 | | // 5. If exactData was specified, and all of the bytes of data were not consumed during the parsing phase, then throw a DataError. |
161 | 0 | if (exact_data && !decoder.eof()) |
162 | 0 | return WebIDL::DataError::create(realm, "Not all bytes were consumed during the parsing phase"_string); |
163 | | |
164 | | // 6. Return the parsed ASN.1 structure. |
165 | 0 | return structure; |
166 | 0 | } Unexecuted instantiation: CryptoAlgorithms.cpp:Web::WebIDL::ExceptionOr<TLS::SubjectPublicKey> Web::Crypto::parse_an_ASN1_structure<TLS::SubjectPublicKey>(JS::Realm&, AK::Span<unsigned char const>, bool) Unexecuted instantiation: CryptoAlgorithms.cpp:Web::WebIDL::ExceptionOr<TLS::PrivateKey> Web::Crypto::parse_an_ASN1_structure<TLS::PrivateKey>(JS::Realm&, AK::Span<unsigned char const>, bool) |
167 | | |
168 | | // https://w3c.github.io/webcrypto/#concept-parse-a-spki |
169 | | static WebIDL::ExceptionOr<TLS::SubjectPublicKey> parse_a_subject_public_key_info(JS::Realm& realm, ReadonlyBytes bytes) |
170 | 0 | { |
171 | | // When this specification says to parse a subjectPublicKeyInfo, the user agent must parse an ASN.1 structure, |
172 | | // with data set to the sequence of bytes to be parsed, structure as the ASN.1 structure of subjectPublicKeyInfo, |
173 | | // as specified in [RFC5280], and exactData set to true. |
174 | 0 | return parse_an_ASN1_structure<TLS::SubjectPublicKey>(realm, bytes, true); |
175 | 0 | } |
176 | | |
177 | | // https://w3c.github.io/webcrypto/#concept-parse-a-privateKeyInfo |
178 | | static WebIDL::ExceptionOr<TLS::PrivateKey> parse_a_private_key_info(JS::Realm& realm, ReadonlyBytes bytes) |
179 | 0 | { |
180 | | // When this specification says to parse a PrivateKeyInfo, the user agent must parse an ASN.1 structure |
181 | | // with data set to the sequence of bytes to be parsed, structure as the ASN.1 structure of PrivateKeyInfo, |
182 | | // as specified in [RFC5208], and exactData set to true. |
183 | 0 | return parse_an_ASN1_structure<TLS::PrivateKey>(realm, bytes, true); |
184 | 0 | } |
185 | | |
186 | | static WebIDL::ExceptionOr<::Crypto::PK::RSAPrivateKey<>> parse_jwk_rsa_private_key(JS::Realm& realm, Bindings::JsonWebKey const& jwk) |
187 | 0 | { |
188 | 0 | auto n = TRY(base64_url_uint_decode(realm, *jwk.n)); |
189 | 0 | auto d = TRY(base64_url_uint_decode(realm, *jwk.d)); |
190 | 0 | auto e = TRY(base64_url_uint_decode(realm, *jwk.e)); |
191 | | |
192 | | // We know that if any of the extra parameters are provided, all of them must be |
193 | 0 | if (!jwk.p.has_value()) |
194 | 0 | return ::Crypto::PK::RSAPrivateKey<>(move(n), move(d), move(e), 0, 0); |
195 | | |
196 | 0 | auto p = TRY(base64_url_uint_decode(realm, *jwk.p)); |
197 | 0 | auto q = TRY(base64_url_uint_decode(realm, *jwk.q)); |
198 | 0 | auto dp = TRY(base64_url_uint_decode(realm, *jwk.dp)); |
199 | 0 | auto dq = TRY(base64_url_uint_decode(realm, *jwk.dq)); |
200 | 0 | auto qi = TRY(base64_url_uint_decode(realm, *jwk.qi)); |
201 | |
|
202 | 0 | return ::Crypto::PK::RSAPrivateKey<>(move(n), move(d), move(e), move(p), move(q), move(dp), move(dq), move(qi)); |
203 | 0 | } |
204 | | |
205 | | static WebIDL::ExceptionOr<::Crypto::PK::RSAPublicKey<>> parse_jwk_rsa_public_key(JS::Realm& realm, Bindings::JsonWebKey const& jwk) |
206 | 0 | { |
207 | 0 | auto e = TRY(base64_url_uint_decode(realm, *jwk.e)); |
208 | 0 | auto n = TRY(base64_url_uint_decode(realm, *jwk.n)); |
209 | |
|
210 | 0 | return ::Crypto::PK::RSAPublicKey<>(move(n), move(e)); |
211 | 0 | } |
212 | | |
213 | 0 | AlgorithmParams::~AlgorithmParams() = default; |
214 | | |
215 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> AlgorithmParams::from_value(JS::VM& vm, JS::Value value) |
216 | 0 | { |
217 | 0 | auto& object = value.as_object(); |
218 | |
|
219 | 0 | auto name = TRY(object.get("name")); |
220 | 0 | auto name_string = TRY(name.to_string(vm)); |
221 | |
|
222 | 0 | return adopt_own(*new AlgorithmParams { name_string }); |
223 | 0 | } |
224 | | |
225 | 0 | HKDFParams::~HKDFParams() = default; |
226 | | |
227 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> HKDFParams::from_value(JS::VM& vm, JS::Value value) |
228 | 0 | { |
229 | 0 | auto& object = value.as_object(); |
230 | |
|
231 | 0 | auto name_value = TRY(object.get("name")); |
232 | 0 | auto name = TRY(name_value.to_string(vm)); |
233 | |
|
234 | 0 | auto hash_value = TRY(object.get("hash")); |
235 | 0 | auto hash = TRY(hash_value.to_string(vm)); |
236 | |
|
237 | 0 | auto salt_value = TRY(object.get("salt")); |
238 | 0 | if (!salt_value.is_object() || !(is<JS::TypedArrayBase>(salt_value.as_object()) || is<JS::ArrayBuffer>(salt_value.as_object()) || is<JS::DataView>(salt_value.as_object()))) |
239 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource"); |
240 | 0 | auto salt = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(salt_value.as_object())); |
241 | |
|
242 | 0 | auto info_value = TRY(object.get("info")); |
243 | 0 | if (!info_value.is_object() || !(is<JS::TypedArrayBase>(info_value.as_object()) || is<JS::ArrayBuffer>(info_value.as_object()) || is<JS::DataView>(info_value.as_object()))) |
244 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource"); |
245 | 0 | auto info = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(info_value.as_object())); |
246 | |
|
247 | 0 | return adopt_own<AlgorithmParams>(*new HKDFParams { name, hash, salt, info }); |
248 | 0 | } |
249 | | |
250 | 0 | PBKDF2Params::~PBKDF2Params() = default; |
251 | | |
252 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> PBKDF2Params::from_value(JS::VM& vm, JS::Value value) |
253 | 0 | { |
254 | 0 | auto& object = value.as_object(); |
255 | |
|
256 | 0 | auto name_value = TRY(object.get("name")); |
257 | 0 | auto name = TRY(name_value.to_string(vm)); |
258 | |
|
259 | 0 | auto salt_value = TRY(object.get("salt")); |
260 | |
|
261 | 0 | if (!salt_value.is_object() || !(is<JS::TypedArrayBase>(salt_value.as_object()) || is<JS::ArrayBuffer>(salt_value.as_object()) || is<JS::DataView>(salt_value.as_object()))) |
262 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource"); |
263 | | |
264 | 0 | auto salt = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(salt_value.as_object())); |
265 | |
|
266 | 0 | auto iterations_value = TRY(object.get("iterations")); |
267 | 0 | auto iterations = TRY(iterations_value.to_u32(vm)); |
268 | |
|
269 | 0 | auto hash_value = TRY(object.get("hash")); |
270 | 0 | auto hash = TRY(hash_value.to_string(vm)); |
271 | |
|
272 | 0 | return adopt_own<AlgorithmParams>(*new PBKDF2Params { name, salt, iterations, hash }); |
273 | 0 | } |
274 | | |
275 | 0 | RsaKeyGenParams::~RsaKeyGenParams() = default; |
276 | | |
277 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> RsaKeyGenParams::from_value(JS::VM& vm, JS::Value value) |
278 | 0 | { |
279 | 0 | auto& object = value.as_object(); |
280 | |
|
281 | 0 | auto name_value = TRY(object.get("name")); |
282 | 0 | auto name = TRY(name_value.to_string(vm)); |
283 | |
|
284 | 0 | auto modulus_length_value = TRY(object.get("modulusLength")); |
285 | 0 | auto modulus_length = TRY(modulus_length_value.to_u32(vm)); |
286 | |
|
287 | 0 | auto public_exponent_value = TRY(object.get("publicExponent")); |
288 | 0 | JS::GCPtr<JS::Uint8Array> public_exponent; |
289 | |
|
290 | 0 | if (!public_exponent_value.is_object() || !is<JS::Uint8Array>(public_exponent_value.as_object())) |
291 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8Array"); |
292 | | |
293 | 0 | public_exponent = static_cast<JS::Uint8Array&>(public_exponent_value.as_object()); |
294 | |
|
295 | 0 | return adopt_own<AlgorithmParams>(*new RsaKeyGenParams { name, modulus_length, big_integer_from_api_big_integer(public_exponent) }); |
296 | 0 | } |
297 | | |
298 | 0 | RsaHashedKeyGenParams::~RsaHashedKeyGenParams() = default; |
299 | | |
300 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> RsaHashedKeyGenParams::from_value(JS::VM& vm, JS::Value value) |
301 | 0 | { |
302 | 0 | auto& object = value.as_object(); |
303 | |
|
304 | 0 | auto name_value = TRY(object.get("name")); |
305 | 0 | auto name = TRY(name_value.to_string(vm)); |
306 | |
|
307 | 0 | auto modulus_length_value = TRY(object.get("modulusLength")); |
308 | 0 | auto modulus_length = TRY(modulus_length_value.to_u32(vm)); |
309 | |
|
310 | 0 | auto public_exponent_value = TRY(object.get("publicExponent")); |
311 | 0 | JS::GCPtr<JS::Uint8Array> public_exponent; |
312 | |
|
313 | 0 | if (!public_exponent_value.is_object() || !is<JS::Uint8Array>(public_exponent_value.as_object())) |
314 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Uint8Array"); |
315 | | |
316 | 0 | public_exponent = static_cast<JS::Uint8Array&>(public_exponent_value.as_object()); |
317 | |
|
318 | 0 | auto hash_value = TRY(object.get("hash")); |
319 | 0 | auto hash = Variant<Empty, HashAlgorithmIdentifier> { Empty {} }; |
320 | 0 | if (hash_value.is_string()) { |
321 | 0 | auto hash_string = TRY(hash_value.to_string(vm)); |
322 | 0 | hash = HashAlgorithmIdentifier { hash_string }; |
323 | 0 | } else { |
324 | 0 | auto hash_object = TRY(hash_value.to_object(vm)); |
325 | 0 | hash = HashAlgorithmIdentifier { hash_object }; |
326 | 0 | } |
327 | |
|
328 | 0 | return adopt_own<AlgorithmParams>(*new RsaHashedKeyGenParams { name, modulus_length, big_integer_from_api_big_integer(public_exponent), hash.get<HashAlgorithmIdentifier>() }); |
329 | 0 | } |
330 | | |
331 | 0 | RsaHashedImportParams::~RsaHashedImportParams() = default; |
332 | | |
333 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> RsaHashedImportParams::from_value(JS::VM& vm, JS::Value value) |
334 | 0 | { |
335 | 0 | auto& object = value.as_object(); |
336 | |
|
337 | 0 | auto name_value = TRY(object.get("name")); |
338 | 0 | auto name = TRY(name_value.to_string(vm)); |
339 | |
|
340 | 0 | auto hash_value = TRY(object.get("hash")); |
341 | 0 | auto hash = Variant<Empty, HashAlgorithmIdentifier> { Empty {} }; |
342 | 0 | if (hash_value.is_string()) { |
343 | 0 | auto hash_string = TRY(hash_value.to_string(vm)); |
344 | 0 | hash = HashAlgorithmIdentifier { hash_string }; |
345 | 0 | } else { |
346 | 0 | auto hash_object = TRY(hash_value.to_object(vm)); |
347 | 0 | hash = HashAlgorithmIdentifier { hash_object }; |
348 | 0 | } |
349 | |
|
350 | 0 | return adopt_own<AlgorithmParams>(*new RsaHashedImportParams { name, hash.get<HashAlgorithmIdentifier>() }); |
351 | 0 | } |
352 | | |
353 | 0 | RsaOaepParams::~RsaOaepParams() = default; |
354 | | |
355 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> RsaOaepParams::from_value(JS::VM& vm, JS::Value value) |
356 | 0 | { |
357 | 0 | auto& object = value.as_object(); |
358 | |
|
359 | 0 | auto name_value = TRY(object.get("name")); |
360 | 0 | auto name = TRY(name_value.to_string(vm)); |
361 | |
|
362 | 0 | auto label_value = TRY(object.get("label")); |
363 | |
|
364 | 0 | ByteBuffer label; |
365 | 0 | if (!label_value.is_nullish()) { |
366 | 0 | if (!label_value.is_object() || !(is<JS::TypedArrayBase>(label_value.as_object()) || is<JS::ArrayBuffer>(label_value.as_object()) || is<JS::DataView>(label_value.as_object()))) |
367 | 0 | return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "BufferSource"); |
368 | | |
369 | 0 | label = TRY_OR_THROW_OOM(vm, WebIDL::get_buffer_source_copy(label_value.as_object())); |
370 | 0 | } |
371 | | |
372 | 0 | return adopt_own<AlgorithmParams>(*new RsaOaepParams { name, move(label) }); |
373 | 0 | } |
374 | | |
375 | 0 | EcdsaParams::~EcdsaParams() = default; |
376 | | |
377 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> EcdsaParams::from_value(JS::VM& vm, JS::Value value) |
378 | 0 | { |
379 | 0 | auto& object = value.as_object(); |
380 | |
|
381 | 0 | auto name_value = TRY(object.get("name")); |
382 | 0 | auto name = TRY(name_value.to_string(vm)); |
383 | |
|
384 | 0 | auto hash_value = TRY(object.get("hash")); |
385 | 0 | auto hash = Variant<Empty, HashAlgorithmIdentifier> { Empty {} }; |
386 | 0 | if (hash_value.is_string()) { |
387 | 0 | auto hash_string = TRY(hash_value.to_string(vm)); |
388 | 0 | hash = HashAlgorithmIdentifier { hash_string }; |
389 | 0 | } else { |
390 | 0 | auto hash_object = TRY(hash_value.to_object(vm)); |
391 | 0 | hash = HashAlgorithmIdentifier { hash_object }; |
392 | 0 | } |
393 | |
|
394 | 0 | return adopt_own<AlgorithmParams>(*new EcdsaParams { name, hash.get<HashAlgorithmIdentifier>() }); |
395 | 0 | } |
396 | | |
397 | 0 | EcKeyGenParams::~EcKeyGenParams() = default; |
398 | | |
399 | | JS::ThrowCompletionOr<NonnullOwnPtr<AlgorithmParams>> EcKeyGenParams::from_value(JS::VM& vm, JS::Value value) |
400 | 0 | { |
401 | 0 | auto& object = value.as_object(); |
402 | |
|
403 | 0 | auto name_value = TRY(object.get("name")); |
404 | 0 | auto name = TRY(name_value.to_string(vm)); |
405 | |
|
406 | 0 | auto curve_value = TRY(object.get("namedCurve")); |
407 | 0 | auto curve = TRY(curve_value.to_string(vm)); |
408 | |
|
409 | 0 | return adopt_own<AlgorithmParams>(*new EcKeyGenParams { name, curve }); |
410 | 0 | } |
411 | | |
412 | | // https://w3c.github.io/webcrypto/#rsa-oaep-operations |
413 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> RSAOAEP::encrypt(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& plaintext) |
414 | 0 | { |
415 | 0 | auto& realm = *m_realm; |
416 | 0 | auto& vm = realm.vm(); |
417 | 0 | auto const& normalized_algorithm = static_cast<RsaOaepParams const&>(params); |
418 | | |
419 | | // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. |
420 | 0 | if (key->type() != Bindings::KeyType::Public) |
421 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a public key"_string); |
422 | | |
423 | | // 2. Let label be the contents of the label member of normalizedAlgorithm or the empty octet string if the label member of normalizedAlgorithm is not present. |
424 | 0 | auto const& label = normalized_algorithm.label; |
425 | |
|
426 | 0 | auto const& handle = key->handle(); |
427 | 0 | auto public_key = handle.get<::Crypto::PK::RSAPublicKey<>>(); |
428 | 0 | auto hash = TRY(verify_cast<RsaHashedKeyAlgorithm>(*key->algorithm()).hash().name(vm)); |
429 | | |
430 | | // 3. Perform the encryption operation defined in Section 7.1 of [RFC3447] with the key represented by key as the recipient's RSA public key, |
431 | | // the contents of plaintext as the message to be encrypted, M and label as the label, L, and with the hash function specified by the hash attribute |
432 | | // of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in Section B.2.1 of [RFC3447]) as the MGF option. |
433 | |
|
434 | 0 | auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash)); |
435 | 0 | ErrorOr<ByteBuffer> maybe_padding = Error::from_string_view(error_message.bytes_as_string_view()); |
436 | 0 | if (hash.equals_ignoring_ascii_case("SHA-1"sv)) { |
437 | 0 | maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA1, ::Crypto::Hash::MGF>(plaintext, label, public_key.length()); |
438 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-256"sv)) { |
439 | 0 | maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA256, ::Crypto::Hash::MGF>(plaintext, label, public_key.length()); |
440 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-384"sv)) { |
441 | 0 | maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA384, ::Crypto::Hash::MGF>(plaintext, label, public_key.length()); |
442 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-512"sv)) { |
443 | 0 | maybe_padding = ::Crypto::Padding::OAEP::eme_encode<::Crypto::Hash::SHA512, ::Crypto::Hash::MGF>(plaintext, label, public_key.length()); |
444 | 0 | } |
445 | | |
446 | | // 4. If performing the operation results in an error, then throw an OperationError. |
447 | 0 | if (maybe_padding.is_error()) { |
448 | 0 | auto error_message = MUST(String::from_utf8(maybe_padding.error().string_literal())); |
449 | 0 | return WebIDL::OperationError::create(realm, error_message); |
450 | 0 | } |
451 | | |
452 | 0 | auto padding = maybe_padding.release_value(); |
453 | | |
454 | | // 5. Let ciphertext be the value C that results from performing the operation. |
455 | 0 | auto ciphertext = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(public_key.length())); |
456 | 0 | auto ciphertext_bytes = ciphertext.bytes(); |
457 | |
|
458 | 0 | auto rsa = ::Crypto::PK::RSA {}; |
459 | 0 | rsa.set_public_key(public_key); |
460 | 0 | rsa.encrypt(padding, ciphertext_bytes); |
461 | | |
462 | | // 6. Return the result of creating an ArrayBuffer containing ciphertext. |
463 | 0 | return JS::ArrayBuffer::create(realm, move(ciphertext)); |
464 | 0 | } |
465 | | |
466 | | // https://w3c.github.io/webcrypto/#rsa-oaep-operations |
467 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> RSAOAEP::decrypt(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, AK::ByteBuffer const& ciphertext) |
468 | 0 | { |
469 | 0 | auto& realm = *m_realm; |
470 | 0 | auto& vm = realm.vm(); |
471 | 0 | auto const& normalized_algorithm = static_cast<RsaOaepParams const&>(params); |
472 | | |
473 | | // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. |
474 | 0 | if (key->type() != Bindings::KeyType::Private) |
475 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a private key"_string); |
476 | | |
477 | | // 2. Let label be the contents of the label member of normalizedAlgorithm or the empty octet string if the label member of normalizedAlgorithm is not present. |
478 | 0 | auto const& label = normalized_algorithm.label; |
479 | |
|
480 | 0 | auto const& handle = key->handle(); |
481 | 0 | auto private_key = handle.get<::Crypto::PK::RSAPrivateKey<>>(); |
482 | 0 | auto hash = TRY(verify_cast<RsaHashedKeyAlgorithm>(*key->algorithm()).hash().name(vm)); |
483 | | |
484 | | // 3. Perform the decryption operation defined in Section 7.1 of [RFC3447] with the key represented by key as the recipient's RSA private key, |
485 | | // the contents of ciphertext as the ciphertext to be decrypted, C, and label as the label, L, and with the hash function specified by the hash attribute |
486 | | // of the [[algorithm]] internal slot of key as the Hash option and MGF1 (defined in Section B.2.1 of [RFC3447]) as the MGF option. |
487 | 0 | auto rsa = ::Crypto::PK::RSA {}; |
488 | 0 | rsa.set_private_key(private_key); |
489 | 0 | u32 private_key_length = private_key.length(); |
490 | |
|
491 | 0 | auto padding = TRY_OR_THROW_OOM(vm, ByteBuffer::create_uninitialized(private_key_length)); |
492 | 0 | auto padding_bytes = padding.bytes(); |
493 | 0 | rsa.decrypt(ciphertext, padding_bytes); |
494 | |
|
495 | 0 | auto error_message = MUST(String::formatted("Invalid hash function '{}'", hash)); |
496 | 0 | ErrorOr<ByteBuffer> maybe_plaintext = Error::from_string_view(error_message.bytes_as_string_view()); |
497 | 0 | if (hash.equals_ignoring_ascii_case("SHA-1"sv)) { |
498 | 0 | maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA1, ::Crypto::Hash::MGF>(padding, label, private_key_length); |
499 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-256"sv)) { |
500 | 0 | maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA256, ::Crypto::Hash::MGF>(padding, label, private_key_length); |
501 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-384"sv)) { |
502 | 0 | maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA384, ::Crypto::Hash::MGF>(padding, label, private_key_length); |
503 | 0 | } else if (hash.equals_ignoring_ascii_case("SHA-512"sv)) { |
504 | 0 | maybe_plaintext = ::Crypto::Padding::OAEP::eme_decode<::Crypto::Hash::SHA512, ::Crypto::Hash::MGF>(padding, label, private_key_length); |
505 | 0 | } |
506 | | |
507 | | // 4. If performing the operation results in an error, then throw an OperationError. |
508 | 0 | if (maybe_plaintext.is_error()) { |
509 | 0 | auto error_message = MUST(String::from_utf8(maybe_plaintext.error().string_literal())); |
510 | 0 | return WebIDL::OperationError::create(realm, error_message); |
511 | 0 | } |
512 | | |
513 | | // 5. Let plaintext the value M that results from performing the operation. |
514 | 0 | auto plaintext = maybe_plaintext.release_value(); |
515 | | |
516 | | // 6. Return the result of creating an ArrayBuffer containing plaintext. |
517 | 0 | return JS::ArrayBuffer::create(realm, move(plaintext)); |
518 | 0 | } |
519 | | |
520 | | // https://w3c.github.io/webcrypto/#rsa-oaep-operations |
521 | | WebIDL::ExceptionOr<Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>>> RSAOAEP::generate_key(AlgorithmParams const& params, bool extractable, Vector<Bindings::KeyUsage> const& key_usages) |
522 | 0 | { |
523 | | // 1. If usages contains an entry which is not "encrypt", "decrypt", "wrapKey" or "unwrapKey", then throw a SyntaxError. |
524 | 0 | for (auto const& usage : key_usages) { |
525 | 0 | if (usage != Bindings::KeyUsage::Encrypt && usage != Bindings::KeyUsage::Decrypt && usage != Bindings::KeyUsage::Wrapkey && usage != Bindings::KeyUsage::Unwrapkey) { |
526 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
527 | 0 | } |
528 | 0 | } |
529 | | |
530 | | // 2. Generate an RSA key pair, as defined in [RFC3447], with RSA modulus length equal to the modulusLength member of normalizedAlgorithm |
531 | | // and RSA public exponent equal to the publicExponent member of normalizedAlgorithm. |
532 | | // 3. If performing the operation results in an error, then throw an OperationError. |
533 | 0 | auto const& normalized_algorithm = static_cast<RsaHashedKeyGenParams const&>(params); |
534 | 0 | auto key_pair = ::Crypto::PK::RSA::generate_key_pair(normalized_algorithm.modulus_length, normalized_algorithm.public_exponent); |
535 | | |
536 | | // 4. Let algorithm be a new RsaHashedKeyAlgorithm object. |
537 | 0 | auto algorithm = RsaHashedKeyAlgorithm::create(m_realm); |
538 | | |
539 | | // 5. Set the name attribute of algorithm to "RSA-OAEP". |
540 | 0 | algorithm->set_name("RSA-OAEP"_string); |
541 | | |
542 | | // 6. Set the modulusLength attribute of algorithm to equal the modulusLength member of normalizedAlgorithm. |
543 | 0 | algorithm->set_modulus_length(normalized_algorithm.modulus_length); |
544 | | |
545 | | // 7. Set the publicExponent attribute of algorithm to equal the publicExponent member of normalizedAlgorithm. |
546 | 0 | TRY(algorithm->set_public_exponent(normalized_algorithm.public_exponent)); |
547 | | |
548 | | // 8. Set the hash attribute of algorithm to equal the hash member of normalizedAlgorithm. |
549 | 0 | algorithm->set_hash(normalized_algorithm.hash); |
550 | | |
551 | | // 9. Let publicKey be a new CryptoKey representing the public key of the generated key pair. |
552 | 0 | auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { key_pair.public_key }); |
553 | | |
554 | | // 10. Set the [[type]] internal slot of publicKey to "public" |
555 | 0 | public_key->set_type(Bindings::KeyType::Public); |
556 | | |
557 | | // 11. Set the [[algorithm]] internal slot of publicKey to algorithm. |
558 | 0 | public_key->set_algorithm(algorithm); |
559 | | |
560 | | // 12. Set the [[extractable]] internal slot of publicKey to true. |
561 | 0 | public_key->set_extractable(true); |
562 | | |
563 | | // 13. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "encrypt", "wrapKey" ]. |
564 | 0 | public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Encrypt, Bindings::KeyUsage::Wrapkey } })); |
565 | | |
566 | | // 14. Let privateKey be a new CryptoKey representing the private key of the generated key pair. |
567 | 0 | auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { key_pair.private_key }); |
568 | | |
569 | | // 15. Set the [[type]] internal slot of privateKey to "private" |
570 | 0 | private_key->set_type(Bindings::KeyType::Private); |
571 | | |
572 | | // 16. Set the [[algorithm]] internal slot of privateKey to algorithm. |
573 | 0 | private_key->set_algorithm(algorithm); |
574 | | |
575 | | // 17. Set the [[extractable]] internal slot of privateKey to extractable. |
576 | 0 | private_key->set_extractable(extractable); |
577 | | |
578 | | // 18. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "decrypt", "unwrapKey" ]. |
579 | 0 | private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Decrypt, Bindings::KeyUsage::Unwrapkey } })); |
580 | | |
581 | | // 19. Let result be a new CryptoKeyPair dictionary. |
582 | | // 20. Set the publicKey attribute of result to be publicKey. |
583 | | // 21. Set the privateKey attribute of result to be privateKey. |
584 | | // 22. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. |
585 | 0 | return Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>> { CryptoKeyPair::create(m_realm, public_key, private_key) }; |
586 | 0 | } |
587 | | |
588 | | // https://w3c.github.io/webcrypto/#rsa-oaep-operations |
589 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> RSAOAEP::import_key(Web::Crypto::AlgorithmParams const& params, Bindings::KeyFormat key_format, CryptoKey::InternalKeyData key_data, bool extractable, Vector<Bindings::KeyUsage> const& usages) |
590 | 0 | { |
591 | 0 | auto& realm = *m_realm; |
592 | | |
593 | | // 1. Let keyData be the key data to be imported. |
594 | |
|
595 | 0 | JS::GCPtr<CryptoKey> key = nullptr; |
596 | 0 | auto const& normalized_algorithm = static_cast<RsaHashedImportParams const&>(params); |
597 | | |
598 | | // 2. -> If format is "spki": |
599 | 0 | if (key_format == Bindings::KeyFormat::Spki) { |
600 | | // 1. If usages contains an entry which is not "encrypt" or "wrapKey", then throw a SyntaxError. |
601 | 0 | for (auto const& usage : usages) { |
602 | 0 | if (usage != Bindings::KeyUsage::Encrypt && usage != Bindings::KeyUsage::Wrapkey) { |
603 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | 0 | VERIFY(key_data.has<ByteBuffer>()); |
608 | | |
609 | | // 2. Let spki be the result of running the parse a subjectPublicKeyInfo algorithm over keyData. |
610 | | // 3. If an error occurred while parsing, then throw a DataError. |
611 | 0 | auto spki = TRY(parse_a_subject_public_key_info(m_realm, key_data.get<ByteBuffer>())); |
612 | | |
613 | | // 4. If the algorithm object identifier field of the algorithm AlgorithmIdentifier field of spki |
614 | | // is not equal to the rsaEncryption object identifier defined in [RFC3447], then throw a DataError. |
615 | 0 | if (spki.algorithm.identifier != TLS::rsa_encryption_oid) |
616 | 0 | return WebIDL::DataError::create(m_realm, "Algorithm object identifier is not the rsaEncryption object identifier"_string); |
617 | | |
618 | | // 5. Let publicKey be the result of performing the parse an ASN.1 structure algorithm, |
619 | | // with data as the subjectPublicKeyInfo field of spki, structure as the RSAPublicKey structure |
620 | | // specified in Section A.1.1 of [RFC3447], and exactData set to true. |
621 | | // NOTE: We already did this in parse_a_subject_public_key_info |
622 | 0 | auto& public_key = spki.rsa; |
623 | | |
624 | | // 6. If an error occurred while parsing, or it can be determined that publicKey is not |
625 | | // a valid public key according to [RFC3447], then throw a DataError. |
626 | | // FIXME: Validate the public key |
627 | | |
628 | | // 7. Let key be a new CryptoKey that represents the RSA public key identified by publicKey. |
629 | 0 | key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key }); |
630 | | |
631 | | // 8. Set the [[type]] internal slot of key to "public" |
632 | 0 | key->set_type(Bindings::KeyType::Public); |
633 | 0 | } |
634 | | |
635 | | // -> If format is "pkcs8": |
636 | 0 | else if (key_format == Bindings::KeyFormat::Pkcs8) { |
637 | | // 1. If usages contains an entry which is not "decrypt" or "unwrapKey", then throw a SyntaxError. |
638 | 0 | for (auto const& usage : usages) { |
639 | 0 | if (usage != Bindings::KeyUsage::Decrypt && usage != Bindings::KeyUsage::Unwrapkey) { |
640 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
641 | 0 | } |
642 | 0 | } |
643 | | |
644 | 0 | VERIFY(key_data.has<ByteBuffer>()); |
645 | | |
646 | | // 2. Let privateKeyInfo be the result of running the parse a privateKeyInfo algorithm over keyData. |
647 | | // 3. If an error occurred while parsing, then throw a DataError. |
648 | 0 | auto private_key_info = TRY(parse_a_private_key_info(m_realm, key_data.get<ByteBuffer>())); |
649 | | |
650 | | // 4. If the algorithm object identifier field of the privateKeyAlgorithm PrivateKeyAlgorithm field of privateKeyInfo |
651 | | // is not equal to the rsaEncryption object identifier defined in [RFC3447], then throw a DataError. |
652 | 0 | if (private_key_info.algorithm.identifier != TLS::rsa_encryption_oid) |
653 | 0 | return WebIDL::DataError::create(m_realm, "Algorithm object identifier is not the rsaEncryption object identifier"_string); |
654 | | |
655 | | // 5. Let rsaPrivateKey be the result of performing the parse an ASN.1 structure algorithm, |
656 | | // with data as the privateKey field of privateKeyInfo, structure as the RSAPrivateKey structure |
657 | | // specified in Section A.1.2 of [RFC3447], and exactData set to true. |
658 | | // NOTE: We already did this in parse_a_private_key_info |
659 | 0 | auto& rsa_private_key = private_key_info.rsa; |
660 | | |
661 | | // 6. If an error occurred while parsing, or if rsaPrivateKey is not |
662 | | // a valid RSA private key according to [RFC3447], then throw a DataError. |
663 | | // FIXME: Validate the private key |
664 | | |
665 | | // 7. Let key be a new CryptoKey that represents the RSA private key identified by rsaPrivateKey. |
666 | 0 | key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { rsa_private_key }); |
667 | | |
668 | | // 8. Set the [[type]] internal slot of key to "private" |
669 | 0 | key->set_type(Bindings::KeyType::Private); |
670 | 0 | } |
671 | | |
672 | | // -> If format is "jwk": |
673 | 0 | else if (key_format == Bindings::KeyFormat::Jwk) { |
674 | | // 1. -> If keyData is a JsonWebKey dictionary: |
675 | | // Let jwk equal keyData. |
676 | | // -> Otherwise: |
677 | | // Throw a DataError. |
678 | 0 | if (!key_data.has<Bindings::JsonWebKey>()) |
679 | 0 | return WebIDL::DataError::create(m_realm, "keyData is not a JsonWebKey dictionary"_string); |
680 | 0 | auto& jwk = key_data.get<Bindings::JsonWebKey>(); |
681 | | |
682 | | // 2. If the d field of jwk is present and usages contains an entry which is not "decrypt" or "unwrapKey", then throw a SyntaxError. |
683 | 0 | if (jwk.d.has_value()) { |
684 | 0 | for (auto const& usage : usages) { |
685 | 0 | if (usage != Bindings::KeyUsage::Decrypt && usage != Bindings::KeyUsage::Unwrapkey) { |
686 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", Bindings::idl_enum_to_string(usage)))); |
687 | 0 | } |
688 | 0 | } |
689 | 0 | } |
690 | | |
691 | | // 3. If the d field of jwk is not present and usages contains an entry which is not "encrypt" or "wrapKey", then throw a SyntaxError. |
692 | 0 | if (!jwk.d.has_value()) { |
693 | 0 | for (auto const& usage : usages) { |
694 | 0 | if (usage != Bindings::KeyUsage::Encrypt && usage != Bindings::KeyUsage::Wrapkey) { |
695 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", Bindings::idl_enum_to_string(usage)))); |
696 | 0 | } |
697 | 0 | } |
698 | 0 | } |
699 | | |
700 | | // 4. If the kty field of jwk is not a case-sensitive string match to "RSA", then throw a DataError. |
701 | 0 | if (jwk.kty != "RSA"_string) |
702 | 0 | return WebIDL::DataError::create(m_realm, "Invalid key type"_string); |
703 | | |
704 | | // 5. If usages is non-empty and the use field of jwk is present and is not a case-sensitive string match to "enc", then throw a DataError. |
705 | 0 | if (!usages.is_empty() && jwk.use.has_value() && *jwk.use != "enc"_string) |
706 | 0 | return WebIDL::DataError::create(m_realm, "Invalid use field"_string); |
707 | | |
708 | | // 6. If the key_ops field of jwk is present, and is invalid according to the requirements of JSON Web Key [JWK] |
709 | | // or does not contain all of the specified usages values, then throw a DataError. |
710 | 0 | if (jwk.key_ops.has_value()) { |
711 | 0 | for (auto const& usage : usages) { |
712 | 0 | if (!jwk.key_ops->contains_slow(Bindings::idl_enum_to_string(usage))) |
713 | 0 | return WebIDL::DataError::create(m_realm, MUST(String::formatted("Missing key_ops field: {}", Bindings::idl_enum_to_string(usage)))); |
714 | 0 | } |
715 | 0 | } |
716 | | // FIXME: Validate jwk.key_ops against requirements in https://www.rfc-editor.org/rfc/rfc7517#section-4.3 |
717 | | |
718 | | // 7. If the ext field of jwk is present and has the value false and extractable is true, then throw a DataError. |
719 | 0 | if (jwk.ext.has_value() && !*jwk.ext && extractable) |
720 | 0 | return WebIDL::DataError::create(m_realm, "Invalid ext field"_string); |
721 | | |
722 | 0 | Optional<String> hash = {}; |
723 | | // 8. -> If the alg field of jwk is not present: |
724 | 0 | if (!jwk.alg.has_value()) { |
725 | | // Let hash be undefined. |
726 | 0 | } |
727 | | // -> If the alg field of jwk is equal to "RSA-OAEP": |
728 | 0 | else if (jwk.alg == "RSA-OAEP"sv) { |
729 | | // Let hash be the string "SHA-1". |
730 | 0 | hash = "SHA-1"_string; |
731 | 0 | } |
732 | | // -> If the alg field of jwk is equal to "RSA-OAEP-256": |
733 | 0 | else if (jwk.alg == "RSA-OAEP-256"sv) { |
734 | | // Let hash be the string "SHA-256". |
735 | 0 | hash = "SHA-256"_string; |
736 | 0 | } |
737 | | // -> If the alg field of jwk is equal to "RSA-OAEP-384": |
738 | 0 | else if (jwk.alg == "RSA-OAEP-384"sv) { |
739 | | // Let hash be the string "SHA-384". |
740 | 0 | hash = "SHA-384"_string; |
741 | 0 | } |
742 | | // -> If the alg field of jwk is equal to "RSA-OAEP-512": |
743 | 0 | else if (jwk.alg == "RSA-OAEP-512"sv) { |
744 | | // Let hash be the string "SHA-512". |
745 | 0 | hash = "SHA-512"_string; |
746 | 0 | } |
747 | | // -> Otherwise: |
748 | 0 | else { |
749 | | // FIXME: Support 'other applicable specifications' |
750 | | // 1. Perform any key import steps defined by other applicable specifications, passing format, jwk and obtaining hash. |
751 | | // 2. If an error occurred or there are no applicable specifications, throw a DataError. |
752 | 0 | return WebIDL::DataError::create(m_realm, "Invalid alg field"_string); |
753 | 0 | } |
754 | | |
755 | | // 9. If hash is not undefined: |
756 | 0 | if (hash.has_value()) { |
757 | | // 1. Let normalizedHash be the result of normalize an algorithm with alg set to hash and op set to digest. |
758 | 0 | auto normalized_hash = TRY(normalize_an_algorithm(m_realm, AlgorithmIdentifier { *hash }, "digest"_string)); |
759 | | |
760 | | // 2. If normalizedHash is not equal to the hash member of normalizedAlgorithm, throw a DataError. |
761 | 0 | if (normalized_hash.parameter->name != TRY(normalized_algorithm.hash.name(realm.vm()))) |
762 | 0 | return WebIDL::DataError::create(m_realm, "Invalid hash"_string); |
763 | 0 | } |
764 | | |
765 | | // 10. -> If the d field of jwk is present: |
766 | 0 | if (jwk.d.has_value()) { |
767 | | // 1. If jwk does not meet the requirements of Section 6.3.2 of JSON Web Algorithms [JWA], then throw a DataError. |
768 | 0 | bool meets_requirements = jwk.e.has_value() && jwk.n.has_value() && jwk.d.has_value(); |
769 | 0 | if (jwk.p.has_value() || jwk.q.has_value() || jwk.dp.has_value() || jwk.dq.has_value() || jwk.qi.has_value()) |
770 | 0 | meets_requirements |= jwk.p.has_value() && jwk.q.has_value() && jwk.dp.has_value() && jwk.dq.has_value() && jwk.qi.has_value(); |
771 | |
|
772 | 0 | if (jwk.oth.has_value()) { |
773 | | // FIXME: We don't support > 2 primes in RSA keys |
774 | 0 | meets_requirements = false; |
775 | 0 | } |
776 | |
|
777 | 0 | if (!meets_requirements) |
778 | 0 | return WebIDL::DataError::create(m_realm, "Invalid JWK private key"_string); |
779 | | |
780 | | // FIXME: Spec error, it should say 'the RSA private key identified by interpreting jwk according to section 6.3.2' |
781 | | // 2. Let privateKey represent the RSA public key identified by interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA]. |
782 | 0 | auto private_key = TRY(parse_jwk_rsa_private_key(realm, jwk)); |
783 | | |
784 | | // FIXME: Spec error, it should say 'not to be a valid RSA private key' |
785 | | // 3. If privateKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError. |
786 | | // FIXME: Validate the private key |
787 | | |
788 | | // 4. Let key be a new CryptoKey representing privateKey. |
789 | 0 | key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key }); |
790 | | |
791 | | // 5. Set the [[type]] internal slot of key to "private" |
792 | 0 | key->set_type(Bindings::KeyType::Private); |
793 | 0 | } |
794 | | |
795 | | // -> Otherwise: |
796 | 0 | else { |
797 | | // 1. If jwk does not meet the requirements of Section 6.3.1 of JSON Web Algorithms [JWA], then throw a DataError. |
798 | 0 | if (!jwk.e.has_value() || !jwk.n.has_value()) |
799 | 0 | return WebIDL::DataError::create(m_realm, "Invalid JWK public key"_string); |
800 | | |
801 | | // 2. Let publicKey represent the RSA public key identified by interpreting jwk according to Section 6.3.1 of JSON Web Algorithms [JWA]. |
802 | 0 | auto public_key = TRY(parse_jwk_rsa_public_key(realm, jwk)); |
803 | | |
804 | | // 3. If publicKey can be determined to not be a valid RSA public key according to [RFC3447], then throw a DataError. |
805 | | // FIXME: Validate the public key |
806 | | |
807 | | // 4. Let key be a new CryptoKey representing publicKey. |
808 | 0 | key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key }); |
809 | | |
810 | | // 5. Set the [[type]] internal slot of key to "public" |
811 | 0 | key->set_type(Bindings::KeyType::Public); |
812 | 0 | } |
813 | 0 | } |
814 | | |
815 | | // -> Otherwise: throw a NotSupportedError. |
816 | 0 | else { |
817 | 0 | return WebIDL::NotSupportedError::create(m_realm, "Unsupported key format"_string); |
818 | 0 | } |
819 | | |
820 | | // 3. Let algorithm be a new RsaHashedKeyAlgorithm. |
821 | 0 | auto algorithm = RsaHashedKeyAlgorithm::create(m_realm); |
822 | | |
823 | | // 4. Set the name attribute of algorithm to "RSA-OAEP" |
824 | 0 | algorithm->set_name("RSA-OAEP"_string); |
825 | | |
826 | | // 5. Set the modulusLength attribute of algorithm to the length, in bits, of the RSA public modulus. |
827 | | // 6. Set the publicExponent attribute of algorithm to the BigInteger representation of the RSA public exponent. |
828 | 0 | TRY(key->handle().visit( |
829 | 0 | [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> WebIDL::ExceptionOr<void> { |
830 | 0 | algorithm->set_modulus_length(public_key.modulus().trimmed_byte_length() * 8); |
831 | 0 | TRY(algorithm->set_public_exponent(public_key.public_exponent())); |
832 | 0 | return {}; |
833 | 0 | }, |
834 | 0 | [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> WebIDL::ExceptionOr<void> { |
835 | 0 | algorithm->set_modulus_length(private_key.modulus().trimmed_byte_length() * 8); |
836 | 0 | TRY(algorithm->set_public_exponent(private_key.public_exponent())); |
837 | 0 | return {}; |
838 | 0 | }, |
839 | 0 | [](auto) -> WebIDL::ExceptionOr<void> { VERIFY_NOT_REACHED(); })); |
840 | | |
841 | | // 7. Set the hash attribute of algorithm to the hash member of normalizedAlgorithm. |
842 | 0 | algorithm->set_hash(normalized_algorithm.hash); |
843 | | |
844 | | // 8. Set the [[algorithm]] internal slot of key to algorithm |
845 | 0 | key->set_algorithm(algorithm); |
846 | | |
847 | | // 9. Return key. |
848 | 0 | return JS::NonnullGCPtr { *key }; |
849 | 0 | } |
850 | | |
851 | | // https://w3c.github.io/webcrypto/#rsa-oaep-operations |
852 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Object>> RSAOAEP::export_key(Bindings::KeyFormat format, JS::NonnullGCPtr<CryptoKey> key) |
853 | 0 | { |
854 | 0 | auto& realm = *m_realm; |
855 | 0 | auto& vm = realm.vm(); |
856 | | |
857 | | // 1. Let key be the key to be exported. |
858 | | |
859 | | // 2. If the underlying cryptographic key material represented by the [[handle]] internal slot of key cannot be accessed, then throw an OperationError. |
860 | | // Note: In our impl this is always accessible |
861 | 0 | auto const& handle = key->handle(); |
862 | |
|
863 | 0 | JS::GCPtr<JS::Object> result = nullptr; |
864 | | |
865 | | // 3. If format is "spki" |
866 | 0 | if (format == Bindings::KeyFormat::Spki) { |
867 | | // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. |
868 | 0 | if (key->type() != Bindings::KeyType::Public) |
869 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not public"_string); |
870 | | |
871 | | // 2. Let data be an instance of the subjectPublicKeyInfo ASN.1 structure defined in [RFC5280] with the following properties: |
872 | | // - Set the algorithm field to an AlgorithmIdentifier ASN.1 type with the following properties: |
873 | | // - Set the algorithm field to the OID rsaEncryption defined in [RFC3447]. |
874 | | // - Set the params field to the ASN.1 type NULL. |
875 | | // - Set the subjectPublicKey field to the result of DER-encoding an RSAPublicKey ASN.1 type, as defined in [RFC3447], Appendix A.1.1, |
876 | | // that represents the RSA public key represented by the [[handle]] internal slot of key |
877 | 0 | auto maybe_data = handle.visit( |
878 | 0 | [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> ErrorOr<ByteBuffer> { |
879 | 0 | auto rsa_encryption_oid = Array<int, 7> { 1, 2, 840, 113549, 1, 1, 1 }; |
880 | 0 | return TRY(::Crypto::PK::wrap_in_subject_public_key_info(public_key, rsa_encryption_oid)); |
881 | 0 | }, |
882 | 0 | [](auto) -> ErrorOr<ByteBuffer> { |
883 | 0 | VERIFY_NOT_REACHED(); |
884 | 0 | }); Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_1::operator()<AK::Detail::ByteBuffer<32ul> >(AK::Detail::ByteBuffer<32ul>) const Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_1::operator()<Web::Bindings::JsonWebKey>(Web::Bindings::JsonWebKey) const Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_1::operator()<Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger> >(Crypto::PK::RSAPrivateKey<Crypto::UnsignedBigInteger>) const |
885 | | // FIXME: clang-format butchers the visit if we do the TRY inline |
886 | 0 | auto data = TRY_OR_THROW_OOM(vm, maybe_data); |
887 | | |
888 | | // 3. Let result be the result of creating an ArrayBuffer containing data. |
889 | 0 | result = JS::ArrayBuffer::create(realm, data); |
890 | 0 | } |
891 | | |
892 | | // If format is "pkcs8" |
893 | 0 | else if (format == Bindings::KeyFormat::Pkcs8) { |
894 | | // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. |
895 | 0 | if (key->type() != Bindings::KeyType::Private) |
896 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not private"_string); |
897 | | |
898 | | // 2. Let data be the result of encoding a privateKeyInfo structure with the following properties: |
899 | | // - Set the version field to 0. |
900 | | // - Set the privateKeyAlgorithm field to an PrivateKeyAlgorithmIdentifier ASN.1 type with the following properties: |
901 | | // - - Set the algorithm field to the OID rsaEncryption defined in [RFC3447]. |
902 | | // - - Set the params field to the ASN.1 type NULL. |
903 | | // - Set the privateKey field to the result of DER-encoding an RSAPrivateKey ASN.1 type, as defined in [RFC3447], Appendix A.1.2, |
904 | | // that represents the RSA private key represented by the [[handle]] internal slot of key |
905 | 0 | auto maybe_data = handle.visit( |
906 | 0 | [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> ErrorOr<ByteBuffer> { |
907 | 0 | auto rsa_encryption_oid = Array<int, 7> { 1, 2, 840, 113549, 1, 1, 1 }; |
908 | 0 | return TRY(::Crypto::PK::wrap_in_private_key_info(private_key, rsa_encryption_oid)); |
909 | 0 | }, |
910 | 0 | [](auto) -> ErrorOr<ByteBuffer> { |
911 | 0 | VERIFY_NOT_REACHED(); |
912 | 0 | }); Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_3::operator()<AK::Detail::ByteBuffer<32ul> >(AK::Detail::ByteBuffer<32ul>) const Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_3::operator()<Web::Bindings::JsonWebKey>(Web::Bindings::JsonWebKey) const Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<AK::Detail::ByteBuffer<32ul>, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_3::operator()<Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger> >(Crypto::PK::RSAPublicKey<Crypto::UnsignedBigInteger>) const |
913 | | |
914 | | // FIXME: clang-format butchers the visit if we do the TRY inline |
915 | 0 | auto data = TRY_OR_THROW_OOM(vm, maybe_data); |
916 | | |
917 | | // 3. Let result be the result of creating an ArrayBuffer containing data. |
918 | 0 | result = JS::ArrayBuffer::create(realm, data); |
919 | 0 | } |
920 | | |
921 | | // If format is "jwk" |
922 | 0 | else if (format == Bindings::KeyFormat::Jwk) { |
923 | | // 1. Let jwk be a new JsonWebKey dictionary. |
924 | 0 | Bindings::JsonWebKey jwk = {}; |
925 | | |
926 | | // 2. Set the kty attribute of jwk to the string "RSA". |
927 | 0 | jwk.kty = "RSA"_string; |
928 | | |
929 | | // 4. Let hash be the name attribute of the hash attribute of the [[algorithm]] internal slot of key. |
930 | 0 | auto hash = TRY(verify_cast<RsaHashedKeyAlgorithm>(*key->algorithm()).hash().name(vm)); |
931 | | |
932 | | // 4. If hash is "SHA-1": |
933 | | // - Set the alg attribute of jwk to the string "RSA-OAEP". |
934 | 0 | if (hash == "SHA-1"sv) { |
935 | 0 | jwk.alg = "RSA-OAEP"_string; |
936 | 0 | } |
937 | | // If hash is "SHA-256": |
938 | | // - Set the alg attribute of jwk to the string "RSA-OAEP-256". |
939 | 0 | else if (hash == "SHA-256"sv) { |
940 | 0 | jwk.alg = "RSA-OAEP-256"_string; |
941 | 0 | } |
942 | | // If hash is "SHA-384": |
943 | | // - Set the alg attribute of jwk to the string "RSA-OAEP-384". |
944 | 0 | else if (hash == "SHA-384"sv) { |
945 | 0 | jwk.alg = "RSA-OAEP-384"_string; |
946 | 0 | } |
947 | | // If hash is "SHA-512": |
948 | | // - Set the alg attribute of jwk to the string "RSA-OAEP-512". |
949 | 0 | else if (hash == "SHA-512"sv) { |
950 | 0 | jwk.alg = "RSA-OAEP-512"_string; |
951 | 0 | } else { |
952 | | // FIXME: Support 'other applicable specifications' |
953 | | // - Perform any key export steps defined by other applicable specifications, |
954 | | // passing format and the hash attribute of the [[algorithm]] internal slot of key and obtaining alg. |
955 | | // - Set the alg attribute of jwk to alg. |
956 | 0 | return WebIDL::NotSupportedError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted("Unsupported hash algorithm '{}'", hash))); |
957 | 0 | } |
958 | | |
959 | | // 10. Set the attributes n and e of jwk according to the corresponding definitions in JSON Web Algorithms [JWA], Section 6.3.1. |
960 | 0 | auto maybe_error = handle.visit( |
961 | 0 | [&](::Crypto::PK::RSAPublicKey<> const& public_key) -> ErrorOr<void> { |
962 | 0 | jwk.n = TRY(base64_url_uint_encode(public_key.modulus())); |
963 | 0 | jwk.e = TRY(base64_url_uint_encode(public_key.public_exponent())); |
964 | 0 | return {}; |
965 | 0 | }, |
966 | 0 | [&](::Crypto::PK::RSAPrivateKey<> const& private_key) -> ErrorOr<void> { |
967 | 0 | jwk.n = TRY(base64_url_uint_encode(private_key.modulus())); |
968 | 0 | jwk.e = TRY(base64_url_uint_encode(private_key.public_exponent())); |
969 | | |
970 | | // 11. If the [[type]] internal slot of key is "private": |
971 | | // 1. Set the attributes named d, p, q, dp, dq, and qi of jwk according to the corresponding definitions in JSON Web Algorithms [JWA], Section 6.3.2. |
972 | 0 | jwk.d = TRY(base64_url_uint_encode(private_key.private_exponent())); |
973 | 0 | jwk.p = TRY(base64_url_uint_encode(private_key.prime1())); |
974 | 0 | jwk.q = TRY(base64_url_uint_encode(private_key.prime2())); |
975 | 0 | jwk.dp = TRY(base64_url_uint_encode(private_key.exponent1())); |
976 | 0 | jwk.dq = TRY(base64_url_uint_encode(private_key.exponent2())); |
977 | 0 | jwk.qi = TRY(base64_url_uint_encode(private_key.coefficient())); |
978 | | |
979 | | // 12. If the underlying RSA private key represented by the [[handle]] internal slot of key is represented by more than two primes, |
980 | | // set the attribute named oth of jwk according to the corresponding definition in JSON Web Algorithms [JWA], Section 6.3.2.7 |
981 | | // FIXME: We don't support more than 2 primes on RSA keys |
982 | 0 | return {}; |
983 | 0 | }, |
984 | 0 | [](auto) -> ErrorOr<void> { |
985 | 0 | VERIFY_NOT_REACHED(); |
986 | 0 | }); Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<void, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_6::operator()<AK::Detail::ByteBuffer<32ul> >(AK::Detail::ByteBuffer<32ul>) const Unexecuted instantiation: CryptoAlgorithms.cpp:AK::ErrorOr<void, AK::Error> Web::Crypto::RSAOAEP::export_key(Web::Bindings::KeyFormat, JS::NonnullGCPtr<Web::Crypto::CryptoKey>)::$_6::operator()<Web::Bindings::JsonWebKey>(Web::Bindings::JsonWebKey) const |
987 | | // FIXME: clang-format butchers the visit if we do the TRY inline |
988 | 0 | TRY_OR_THROW_OOM(vm, maybe_error); |
989 | | |
990 | | // 13. Set the key_ops attribute of jwk to the usages attribute of key. |
991 | 0 | jwk.key_ops = Vector<String> {}; |
992 | 0 | jwk.key_ops->ensure_capacity(key->internal_usages().size()); |
993 | 0 | for (auto const& usage : key->internal_usages()) { |
994 | 0 | jwk.key_ops->append(Bindings::idl_enum_to_string(usage)); |
995 | 0 | } |
996 | | |
997 | | // 14. Set the ext attribute of jwk to the [[extractable]] internal slot of key. |
998 | 0 | jwk.ext = key->extractable(); |
999 | | |
1000 | | // 15. Let result be the result of converting jwk to an ECMAScript Object, as defined by [WebIDL]. |
1001 | 0 | result = TRY(jwk.to_object(realm)); |
1002 | 0 | } |
1003 | | |
1004 | | // Otherwise throw a NotSupportedError. |
1005 | 0 | else { |
1006 | 0 | return WebIDL::NotSupportedError::create(realm, TRY_OR_THROW_OOM(vm, String::formatted("Exporting to format {} is not supported", Bindings::idl_enum_to_string(format)))); |
1007 | 0 | } |
1008 | | |
1009 | | // 8. Return result |
1010 | 0 | return JS::NonnullGCPtr { *result }; |
1011 | 0 | } |
1012 | | |
1013 | | // https://w3c.github.io/webcrypto/#hkdf-operations |
1014 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> HKDF::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector<Bindings::KeyUsage> const& key_usages) |
1015 | 0 | { |
1016 | | // 1. Let keyData be the key data to be imported. |
1017 | | |
1018 | | // 2. If format is "raw": |
1019 | | // (… see below …) |
1020 | | // Otherwise: |
1021 | | // throw a NotSupportedError. |
1022 | 0 | if (format != Bindings::KeyFormat::Raw) { |
1023 | 0 | return WebIDL::NotSupportedError::create(m_realm, "Only raw format is supported"_string); |
1024 | 0 | } |
1025 | | |
1026 | | // 1. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError. |
1027 | 0 | for (auto& usage : key_usages) { |
1028 | 0 | if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) { |
1029 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
1030 | 0 | } |
1031 | 0 | } |
1032 | | |
1033 | | // 2. If extractable is not false, then throw a SyntaxError. |
1034 | 0 | if (extractable) |
1035 | 0 | return WebIDL::SyntaxError::create(m_realm, "extractable must be false"_string); |
1036 | | |
1037 | | // 3. Let key be a new CryptoKey representing the key data provided in keyData. |
1038 | 0 | auto key = CryptoKey::create(m_realm, move(key_data)); |
1039 | | |
1040 | | // 4. Set the [[type]] internal slot of key to "secret". |
1041 | 0 | key->set_type(Bindings::KeyType::Secret); |
1042 | | |
1043 | | // 5. Let algorithm be a new KeyAlgorithm object. |
1044 | 0 | auto algorithm = KeyAlgorithm::create(m_realm); |
1045 | | |
1046 | | // 6. Set the name attribute of algorithm to "HKDF". |
1047 | 0 | algorithm->set_name("HKDF"_string); |
1048 | | |
1049 | | // 7. Set the [[algorithm]] internal slot of key to algorithm. |
1050 | 0 | key->set_algorithm(algorithm); |
1051 | | |
1052 | | // 8. Return key. |
1053 | 0 | return key; |
1054 | 0 | } |
1055 | | |
1056 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<CryptoKey>> PBKDF2::import_key(AlgorithmParams const&, Bindings::KeyFormat format, CryptoKey::InternalKeyData key_data, bool extractable, Vector<Bindings::KeyUsage> const& key_usages) |
1057 | 0 | { |
1058 | | // 1. If format is not "raw", throw a NotSupportedError |
1059 | 0 | if (format != Bindings::KeyFormat::Raw) { |
1060 | 0 | return WebIDL::NotSupportedError::create(m_realm, "Only raw format is supported"_string); |
1061 | 0 | } |
1062 | | |
1063 | | // 2. If usages contains a value that is not "deriveKey" or "deriveBits", then throw a SyntaxError. |
1064 | 0 | for (auto& usage : key_usages) { |
1065 | 0 | if (usage != Bindings::KeyUsage::Derivekey && usage != Bindings::KeyUsage::Derivebits) { |
1066 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
1067 | 0 | } |
1068 | 0 | } |
1069 | | |
1070 | | // 3. If extractable is not false, then throw a SyntaxError. |
1071 | 0 | if (extractable) |
1072 | 0 | return WebIDL::SyntaxError::create(m_realm, "extractable must be false"_string); |
1073 | | |
1074 | | // 4. Let key be a new CryptoKey representing keyData. |
1075 | 0 | auto key = CryptoKey::create(m_realm, move(key_data)); |
1076 | | |
1077 | | // 5. Set the [[type]] internal slot of key to "secret". |
1078 | 0 | key->set_type(Bindings::KeyType::Secret); |
1079 | | |
1080 | | // 6. Let algorithm be a new KeyAlgorithm object. |
1081 | 0 | auto algorithm = KeyAlgorithm::create(m_realm); |
1082 | | |
1083 | | // 7. Set the name attribute of algorithm to "PBKDF2". |
1084 | 0 | algorithm->set_name("PBKDF2"_string); |
1085 | | |
1086 | | // 8. Set the [[algorithm]] internal slot of key to algorithm. |
1087 | 0 | key->set_algorithm(algorithm); |
1088 | | |
1089 | | // 9. Return key. |
1090 | 0 | return key; |
1091 | 0 | } |
1092 | | |
1093 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> SHA::digest(AlgorithmParams const& algorithm, ByteBuffer const& data) |
1094 | 0 | { |
1095 | 0 | auto& algorithm_name = algorithm.name; |
1096 | |
|
1097 | 0 | ::Crypto::Hash::HashKind hash_kind; |
1098 | 0 | if (algorithm_name.equals_ignoring_ascii_case("SHA-1"sv)) { |
1099 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA1; |
1100 | 0 | } else if (algorithm_name.equals_ignoring_ascii_case("SHA-256"sv)) { |
1101 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA256; |
1102 | 0 | } else if (algorithm_name.equals_ignoring_ascii_case("SHA-384"sv)) { |
1103 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA384; |
1104 | 0 | } else if (algorithm_name.equals_ignoring_ascii_case("SHA-512"sv)) { |
1105 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA512; |
1106 | 0 | } else { |
1107 | 0 | return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", algorithm_name))); |
1108 | 0 | } |
1109 | | |
1110 | 0 | ::Crypto::Hash::Manager hash { hash_kind }; |
1111 | 0 | hash.update(data); |
1112 | |
|
1113 | 0 | auto digest = hash.digest(); |
1114 | 0 | auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size()); |
1115 | 0 | if (result_buffer.is_error()) |
1116 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to create result buffer"_string); |
1117 | | |
1118 | 0 | return JS::ArrayBuffer::create(m_realm, result_buffer.release_value()); |
1119 | 0 | } |
1120 | | |
1121 | | // https://w3c.github.io/webcrypto/#ecdsa-operations |
1122 | | WebIDL::ExceptionOr<Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>>> ECDSA::generate_key(AlgorithmParams const& params, bool extractable, Vector<Bindings::KeyUsage> const& key_usages) |
1123 | 0 | { |
1124 | | // 1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError. |
1125 | 0 | for (auto const& usage : key_usages) { |
1126 | 0 | if (usage != Bindings::KeyUsage::Sign && usage != Bindings::KeyUsage::Verify) { |
1127 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
1128 | 0 | } |
1129 | 0 | } |
1130 | | |
1131 | 0 | auto const& normalized_algorithm = static_cast<EcKeyGenParams const&>(params); |
1132 | | |
1133 | | // 2. If the namedCurve member of normalizedAlgorithm is "P-256", "P-384" or "P-521": |
1134 | | // Generate an Elliptic Curve key pair, as defined in [RFC6090] |
1135 | | // with domain parameters for the curve identified by the namedCurve member of normalizedAlgorithm. |
1136 | 0 | Variant<Empty, ::Crypto::Curves::SECP256r1, ::Crypto::Curves::SECP384r1> curve; |
1137 | 0 | if (normalized_algorithm.named_curve.is_one_of("P-256"sv, "P-384"sv, "P-521"sv)) { |
1138 | 0 | if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-256"sv)) |
1139 | 0 | curve = ::Crypto::Curves::SECP256r1 {}; |
1140 | |
|
1141 | 0 | if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-384"sv)) |
1142 | 0 | curve = ::Crypto::Curves::SECP384r1 {}; |
1143 | | |
1144 | | // FIXME: Support P-521 |
1145 | 0 | if (normalized_algorithm.named_curve.equals_ignoring_ascii_case("P-521"sv)) |
1146 | 0 | return WebIDL::NotSupportedError::create(m_realm, "'P-521' is not supported yet"_string); |
1147 | 0 | } else { |
1148 | | // If the namedCurve member of normalizedAlgorithm is a value specified in an applicable specification: |
1149 | | // Perform the ECDSA generation steps specified in that specification, |
1150 | | // passing in normalizedAlgorithm and resulting in an elliptic curve key pair. |
1151 | | |
1152 | | // Otherwise: throw a NotSupportedError |
1153 | 0 | return WebIDL::NotSupportedError::create(m_realm, "Only 'P-256', 'P-384' and 'P-521' is supported"_string); |
1154 | 0 | } |
1155 | | |
1156 | | // NOTE: Spec jumps to 6 here for some reason |
1157 | | // 6. If performing the key generation operation results in an error, then throw an OperationError. |
1158 | 0 | auto maybe_private_key_data = curve.visit( |
1159 | 0 | [](Empty const&) -> ErrorOr<ByteBuffer> { return Error::from_string_literal("noop error"); }, |
1160 | 0 | [](auto instance) { return instance.generate_private_key(); });Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::generate_key(Web::Crypto::AlgorithmParams const&, bool, AK::Vector<Web::Bindings::KeyUsage, 0ul> const&)::$_1::operator()<Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS>) const Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::generate_key(Web::Crypto::AlgorithmParams const&, bool, AK::Vector<Web::Bindings::KeyUsage, 0ul> const&)::$_1::operator()<Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS>) const |
1161 | |
|
1162 | 0 | if (maybe_private_key_data.is_error()) |
1163 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to create valid crypto instance"_string); |
1164 | | |
1165 | 0 | auto private_key_data = maybe_private_key_data.release_value(); |
1166 | |
|
1167 | 0 | auto maybe_public_key_data = curve.visit( |
1168 | 0 | [](Empty const&) -> ErrorOr<ByteBuffer> { return Error::from_string_literal("noop error"); }, |
1169 | 0 | [&](auto instance) { return instance.generate_public_key(private_key_data); });Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::generate_key(Web::Crypto::AlgorithmParams const&, bool, AK::Vector<Web::Bindings::KeyUsage, 0ul> const&)::$_3::operator()<Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS>) const Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::generate_key(Web::Crypto::AlgorithmParams const&, bool, AK::Vector<Web::Bindings::KeyUsage, 0ul> const&)::$_3::operator()<Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS>) const |
1170 | |
|
1171 | 0 | if (maybe_public_key_data.is_error()) |
1172 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to create valid crypto instance"_string); |
1173 | | |
1174 | 0 | auto public_key_data = maybe_public_key_data.release_value(); |
1175 | | |
1176 | | // 7. Let algorithm be a new EcKeyAlgorithm object. |
1177 | 0 | auto algorithm = EcKeyAlgorithm::create(m_realm); |
1178 | | |
1179 | | // 8. Set the name attribute of algorithm to "ECDSA". |
1180 | 0 | algorithm->set_name("ECDSA"_string); |
1181 | | |
1182 | | // 9. Set the namedCurve attribute of algorithm to equal the namedCurve member of normalizedAlgorithm. |
1183 | 0 | algorithm->set_named_curve(normalized_algorithm.named_curve); |
1184 | | |
1185 | | // 10. Let publicKey be a new CryptoKey representing the public key of the generated key pair. |
1186 | 0 | auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key_data }); |
1187 | | |
1188 | | // 11. Set the [[type]] internal slot of publicKey to "public" |
1189 | 0 | public_key->set_type(Bindings::KeyType::Public); |
1190 | | |
1191 | | // 12. Set the [[algorithm]] internal slot of publicKey to algorithm. |
1192 | 0 | public_key->set_algorithm(algorithm); |
1193 | | |
1194 | | // 13. Set the [[extractable]] internal slot of publicKey to true. |
1195 | 0 | public_key->set_extractable(true); |
1196 | | |
1197 | | // 14. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ]. |
1198 | 0 | public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Verify } })); |
1199 | | |
1200 | | // 15. Let privateKey be a new CryptoKey representing the private key of the generated key pair. |
1201 | 0 | auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key_data }); |
1202 | | |
1203 | | // 16. Set the [[type]] internal slot of privateKey to "private" |
1204 | 0 | private_key->set_type(Bindings::KeyType::Private); |
1205 | | |
1206 | | // 17. Set the [[algorithm]] internal slot of privateKey to algorithm. |
1207 | 0 | private_key->set_algorithm(algorithm); |
1208 | | |
1209 | | // 18. Set the [[extractable]] internal slot of privateKey to extractable. |
1210 | 0 | private_key->set_extractable(extractable); |
1211 | | |
1212 | | // 19. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ]. |
1213 | 0 | private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Sign } })); |
1214 | | |
1215 | | // 20. Let result be a new CryptoKeyPair dictionary. |
1216 | | // 21. Set the publicKey attribute of result to be publicKey. |
1217 | | // 22. Set the privateKey attribute of result to be privateKey. |
1218 | | // 23. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. |
1219 | 0 | return Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>> { CryptoKeyPair::create(m_realm, public_key, private_key) }; |
1220 | 0 | } |
1221 | | |
1222 | | // https://w3c.github.io/webcrypto/#ecdsa-operations |
1223 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> ECDSA::sign(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& message) |
1224 | 0 | { |
1225 | 0 | auto& realm = *m_realm; |
1226 | 0 | auto& vm = realm.vm(); |
1227 | 0 | auto const& normalized_algorithm = static_cast<EcdsaParams const&>(params); |
1228 | |
|
1229 | 0 | (void)vm; |
1230 | 0 | (void)message; |
1231 | | |
1232 | | // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. |
1233 | 0 | if (key->type() != Bindings::KeyType::Private) |
1234 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a private key"_string); |
1235 | | |
1236 | | // 2. Let hashAlgorithm be the hash member of normalizedAlgorithm. |
1237 | 0 | [[maybe_unused]] auto const& hash_algorithm = normalized_algorithm.hash; |
1238 | | |
1239 | | // NOTE: We dont have sign() on the SECPxxxr1 curves, so we can't implement this yet |
1240 | | // FIXME: 3. Let M be the result of performing the digest operation specified by hashAlgorithm using message. |
1241 | | // FIXME: 4. Let d be the ECDSA private key associated with key. |
1242 | | // FIXME: 5. Let params be the EC domain parameters associated with key. |
1243 | | // FIXME: 6. If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256", "P-384" or "P-521": |
1244 | | |
1245 | | // FIXME: 1. Perform the ECDSA signing process, as specified in [RFC6090], Section 5.4, with M as the message, using params as the EC domain parameters, and with d as the private key. |
1246 | | // FIXME: 2. Let r and s be the pair of integers resulting from performing the ECDSA signing process. |
1247 | | // FIXME: 3. Let result be an empty byte sequence. |
1248 | | // FIXME: 4. Let n be the smallest integer such that n * 8 is greater than the logarithm to base 2 of the order of the base point of the elliptic curve identified by params. |
1249 | | // FIXME: 5. Convert r to an octet string of length n and append this sequence of bytes to result. |
1250 | | // FIXME: 6. Convert s to an octet string of length n and append this sequence of bytes to result. |
1251 | | |
1252 | | // FIXME: Otherwise, the namedCurve attribute of the [[algorithm]] internal slot of key is a value specified in an applicable specification: |
1253 | | // FIXME: Perform the ECDSA signature steps specified in that specification, passing in M, params and d and resulting in result. |
1254 | | |
1255 | | // NOTE: The spec jumps to 9 here for some reason |
1256 | | // FIXME: 9. Return the result of creating an ArrayBuffer containing result. |
1257 | 0 | return WebIDL::NotSupportedError::create(realm, "ECDSA signing is not supported yet"_string); |
1258 | 0 | } |
1259 | | |
1260 | | // https://w3c.github.io/webcrypto/#ecdsa-operations |
1261 | | WebIDL::ExceptionOr<JS::Value> ECDSA::verify(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& signature, ByteBuffer const& message) |
1262 | 0 | { |
1263 | 0 | auto& realm = *m_realm; |
1264 | 0 | auto const& normalized_algorithm = static_cast<EcdsaParams const&>(params); |
1265 | | |
1266 | | // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. |
1267 | 0 | if (key->type() != Bindings::KeyType::Public) |
1268 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a public key"_string); |
1269 | | |
1270 | | // 2. Let hashAlgorithm be the hash member of normalizedAlgorithm. |
1271 | 0 | [[maybe_unused]] auto const& hash_algorithm = TRY(normalized_algorithm.hash.name(realm.vm())); |
1272 | | |
1273 | | // 3. Let M be the result of performing the digest operation specified by hashAlgorithm using message. |
1274 | 0 | ::Crypto::Hash::HashKind hash_kind; |
1275 | 0 | if (hash_algorithm.equals_ignoring_ascii_case("SHA-1"sv)) { |
1276 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA1; |
1277 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-256"sv)) { |
1278 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA256; |
1279 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-384"sv)) { |
1280 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA384; |
1281 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-512"sv)) { |
1282 | 0 | hash_kind = ::Crypto::Hash::HashKind::SHA512; |
1283 | 0 | } else { |
1284 | 0 | return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm))); |
1285 | 0 | } |
1286 | 0 | ::Crypto::Hash::Manager hash { hash_kind }; |
1287 | 0 | hash.update(message); |
1288 | 0 | auto digest = hash.digest(); |
1289 | |
|
1290 | 0 | auto result_buffer = ByteBuffer::copy(digest.immutable_data(), hash.digest_size()); |
1291 | 0 | if (result_buffer.is_error()) |
1292 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to create result buffer"_string); |
1293 | | |
1294 | 0 | auto M = result_buffer.release_value(); |
1295 | | |
1296 | | // 4. Let Q be the ECDSA public key associated with key. |
1297 | 0 | auto Q = key->handle().get<ByteBuffer>(); |
1298 | | |
1299 | | // FIXME: 5. Let params be the EC domain parameters associated with key. |
1300 | | |
1301 | | // 6. If the namedCurve attribute of the [[algorithm]] internal slot of key is "P-256", "P-384" or "P-521": |
1302 | 0 | auto const& internal_algorithm = static_cast<EcKeyAlgorithm const&>(*key->algorithm()); |
1303 | 0 | auto const& named_curve = internal_algorithm.named_curve(); |
1304 | |
|
1305 | 0 | auto result = false; |
1306 | |
|
1307 | 0 | Variant<Empty, ::Crypto::Curves::SECP256r1, ::Crypto::Curves::SECP384r1> curve; |
1308 | 0 | if (named_curve.is_one_of("P-256"sv, "P-384"sv, "P-521"sv)) { |
1309 | 0 | if (named_curve.equals_ignoring_ascii_case("P-256"sv)) |
1310 | 0 | curve = ::Crypto::Curves::SECP256r1 {}; |
1311 | |
|
1312 | 0 | if (named_curve.equals_ignoring_ascii_case("P-384"sv)) |
1313 | 0 | curve = ::Crypto::Curves::SECP384r1 {}; |
1314 | | |
1315 | | // FIXME: Support P-521 |
1316 | 0 | if (named_curve.equals_ignoring_ascii_case("P-521"sv)) |
1317 | 0 | return WebIDL::NotSupportedError::create(m_realm, "'P-521' is not supported yet"_string); |
1318 | | |
1319 | | // Perform the ECDSA verifying process, as specified in [RFC6090], Section 5.3, |
1320 | | // with M as the received message, |
1321 | | // signature as the received signature |
1322 | | // and using params as the EC domain parameters, |
1323 | | // and Q as the public key. |
1324 | | |
1325 | | // NOTE: verify() takes the signature in X.509 format but JS uses IEEE P1363 format, so we need to convert it |
1326 | | // FIXME: Dont construct an ASN1 object here just to pass it to verify |
1327 | 0 | auto half_size = signature.size() / 2; |
1328 | 0 | auto r = ::Crypto::UnsignedBigInteger::import_data(signature.data(), half_size); |
1329 | 0 | auto s = ::Crypto::UnsignedBigInteger::import_data(signature.data() + half_size, half_size); |
1330 | |
|
1331 | 0 | ::Crypto::ASN1::Encoder encoder; |
1332 | 0 | (void)encoder.write_constructed(::Crypto::ASN1::Class::Universal, ::Crypto::ASN1::Kind::Sequence, [&] { |
1333 | 0 | (void)encoder.write(r); |
1334 | 0 | (void)encoder.write(s); |
1335 | 0 | }); |
1336 | 0 | auto encoded_signature = encoder.finish(); |
1337 | |
|
1338 | 0 | auto maybe_result = curve.visit( |
1339 | 0 | [](Empty const&) -> ErrorOr<bool> { return Error::from_string_literal("Failed to create valid crypto instance"); }, |
1340 | 0 | [&](auto instance) { return instance.verify(M, Q, encoded_signature); });Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::verify(Web::Crypto::AlgorithmParams const&, JS::NonnullGCPtr<Web::Crypto::CryptoKey>, AK::Detail::ByteBuffer<32ul> const&, AK::Detail::ByteBuffer<32ul> const&)::$_1::operator()<Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<256ul, Crypto::Curves::SECP256r1_CURVE_PARAMETERS>) const Unexecuted instantiation: CryptoAlgorithms.cpp:auto Web::Crypto::ECDSA::verify(Web::Crypto::AlgorithmParams const&, JS::NonnullGCPtr<Web::Crypto::CryptoKey>, AK::Detail::ByteBuffer<32ul> const&, AK::Detail::ByteBuffer<32ul> const&)::$_1::operator()<Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS> >(Crypto::Curves::SECPxxxr1<384ul, Crypto::Curves::SECP384r1_CURVE_PARAMETERS>) const |
1341 | |
|
1342 | 0 | if (maybe_result.is_error()) { |
1343 | 0 | auto error_message = MUST(String::from_utf8(maybe_result.error().string_literal())); |
1344 | 0 | return WebIDL::OperationError::create(m_realm, error_message); |
1345 | 0 | } |
1346 | | |
1347 | 0 | result = maybe_result.release_value(); |
1348 | 0 | } else { |
1349 | | // FIXME: Otherwise, the namedCurve attribute of the [[algorithm]] internal slot of key is a value specified in an applicable specification: |
1350 | | // FIXME: Perform the ECDSA verification steps specified in that specification passing in M, signature, params and Q and resulting in an indication of whether or not the purported signature is valid. |
1351 | 0 | } |
1352 | | |
1353 | | // 9. Let result be a boolean with the value true if the signature is valid and the value false otherwise. |
1354 | | // 10. Return result. |
1355 | 0 | return JS::Value(result); |
1356 | 0 | } |
1357 | | |
1358 | | // https://wicg.github.io/webcrypto-secure-curves/#ed25519-operations |
1359 | | WebIDL::ExceptionOr<Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>>> ED25519::generate_key([[maybe_unused]] AlgorithmParams const& params, bool extractable, Vector<Bindings::KeyUsage> const& key_usages) |
1360 | 0 | { |
1361 | | // 1. If usages contains a value which is not one of "sign" or "verify", then throw a SyntaxError. |
1362 | 0 | for (auto const& usage : key_usages) { |
1363 | 0 | if (usage != Bindings::KeyUsage::Sign && usage != Bindings::KeyUsage::Verify) { |
1364 | 0 | return WebIDL::SyntaxError::create(m_realm, MUST(String::formatted("Invalid key usage '{}'", idl_enum_to_string(usage)))); |
1365 | 0 | } |
1366 | 0 | } |
1367 | | |
1368 | | // 2. Generate an Ed25519 key pair, as defined in [RFC8032], section 5.1.5. |
1369 | 0 | ::Crypto::Curves::Ed25519 curve; |
1370 | 0 | auto maybe_private_key = curve.generate_private_key(); |
1371 | 0 | if (maybe_private_key.is_error()) |
1372 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to generate private key"_string); |
1373 | 0 | auto private_key_data = maybe_private_key.release_value(); |
1374 | |
|
1375 | 0 | auto maybe_public_key = curve.generate_public_key(private_key_data); |
1376 | 0 | if (maybe_public_key.is_error()) |
1377 | 0 | return WebIDL::OperationError::create(m_realm, "Failed to generate public key"_string); |
1378 | 0 | auto public_key_data = maybe_public_key.release_value(); |
1379 | | |
1380 | | // 3. Let algorithm be a new KeyAlgorithm object. |
1381 | 0 | auto algorithm = KeyAlgorithm::create(m_realm); |
1382 | | |
1383 | | // 4. Set the name attribute of algorithm to "Ed25519". |
1384 | 0 | algorithm->set_name("Ed25519"_string); |
1385 | | |
1386 | | // 5. Let publicKey be a new CryptoKey associated with the relevant global object of this [HTML], |
1387 | | // and representing the public key of the generated key pair. |
1388 | 0 | auto public_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { public_key_data }); |
1389 | | |
1390 | | // 6. Set the [[type]] internal slot of publicKey to "public" |
1391 | 0 | public_key->set_type(Bindings::KeyType::Public); |
1392 | | |
1393 | | // 7. Set the [[algorithm]] internal slot of publicKey to algorithm. |
1394 | 0 | public_key->set_algorithm(algorithm); |
1395 | | |
1396 | | // 8. Set the [[extractable]] internal slot of publicKey to true. |
1397 | 0 | public_key->set_extractable(true); |
1398 | | |
1399 | | // 9. Set the [[usages]] internal slot of publicKey to be the usage intersection of usages and [ "verify" ]. |
1400 | 0 | public_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Verify } })); |
1401 | | |
1402 | | // 10. Let privateKey be a new CryptoKey associated with the relevant global object of this [HTML], |
1403 | | // and representing the private key of the generated key pair. |
1404 | 0 | auto private_key = CryptoKey::create(m_realm, CryptoKey::InternalKeyData { private_key_data }); |
1405 | | |
1406 | | // 11. Set the [[type]] internal slot of privateKey to "private" |
1407 | 0 | private_key->set_type(Bindings::KeyType::Private); |
1408 | | |
1409 | | // 12. Set the [[algorithm]] internal slot of privateKey to algorithm. |
1410 | 0 | private_key->set_algorithm(algorithm); |
1411 | | |
1412 | | // 13. Set the [[extractable]] internal slot of privateKey to extractable. |
1413 | 0 | private_key->set_extractable(extractable); |
1414 | | |
1415 | | // 14. Set the [[usages]] internal slot of privateKey to be the usage intersection of usages and [ "sign" ]. |
1416 | 0 | private_key->set_usages(usage_intersection(key_usages, { { Bindings::KeyUsage::Sign } })); |
1417 | | |
1418 | | // 15. Let result be a new CryptoKeyPair dictionary. |
1419 | | // 16. Set the publicKey attribute of result to be publicKey. |
1420 | | // 17. Set the privateKey attribute of result to be privateKey. |
1421 | | // 18. Return the result of converting result to an ECMAScript Object, as defined by [WebIDL]. |
1422 | 0 | return Variant<JS::NonnullGCPtr<CryptoKey>, JS::NonnullGCPtr<CryptoKeyPair>> { CryptoKeyPair::create(m_realm, public_key, private_key) }; |
1423 | 0 | } |
1424 | | |
1425 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> ED25519::sign([[maybe_unused]] AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& message) |
1426 | 0 | { |
1427 | 0 | auto& realm = *m_realm; |
1428 | 0 | auto& vm = realm.vm(); |
1429 | | |
1430 | | // 1. If the [[type]] internal slot of key is not "private", then throw an InvalidAccessError. |
1431 | 0 | if (key->type() != Bindings::KeyType::Private) |
1432 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a private key"_string); |
1433 | | |
1434 | | // 2. Perform the Ed25519 signing process, as specified in [RFC8032], Section 5.1.6, |
1435 | | // with message as M, using the Ed25519 private key associated with key. |
1436 | 0 | auto private_key = key->handle().get<ByteBuffer>(); |
1437 | |
|
1438 | 0 | ::Crypto::Curves::Ed25519 curve; |
1439 | 0 | auto maybe_public_key = curve.generate_public_key(private_key); |
1440 | 0 | if (maybe_public_key.is_error()) |
1441 | 0 | return WebIDL::OperationError::create(realm, "Failed to generate public key"_string); |
1442 | 0 | auto public_key = maybe_public_key.release_value(); |
1443 | |
|
1444 | 0 | auto maybe_signature = curve.sign(public_key, private_key, message); |
1445 | 0 | if (maybe_signature.is_error()) |
1446 | 0 | return WebIDL::OperationError::create(realm, "Failed to sign message"_string); |
1447 | 0 | auto signature = maybe_signature.release_value(); |
1448 | | |
1449 | | // 3. Return a new ArrayBuffer associated with the relevant global object of this [HTML], |
1450 | | // and containing the bytes of the signature resulting from performing the Ed25519 signing process. |
1451 | 0 | auto result = TRY_OR_THROW_OOM(vm, ByteBuffer::copy(signature)); |
1452 | 0 | return JS::ArrayBuffer::create(realm, move(result)); |
1453 | 0 | } |
1454 | | |
1455 | | WebIDL::ExceptionOr<JS::Value> ED25519::verify([[maybe_unused]] AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, ByteBuffer const& signature, ByteBuffer const& message) |
1456 | 0 | { |
1457 | 0 | auto& realm = *m_realm; |
1458 | | |
1459 | | // 1. If the [[type]] internal slot of key is not "public", then throw an InvalidAccessError. |
1460 | 0 | if (key->type() != Bindings::KeyType::Public) |
1461 | 0 | return WebIDL::InvalidAccessError::create(realm, "Key is not a public key"_string); |
1462 | | |
1463 | | // NOTE: this is checked by ED25519::verify() |
1464 | | // 2. If the key data of key represents an invalid point or a small-order element on the Elliptic Curve of Ed25519, return false. |
1465 | | // 3. If the point R, encoded in the first half of signature, represents an invalid point or a small-order element on the Elliptic Curve of Ed25519, return false. |
1466 | | |
1467 | | // 4. Perform the Ed25519 verification steps, as specified in [RFC8032], Section 5.1.7, |
1468 | | // using the cofactorless (unbatched) equation, [S]B = R + [k]A', on the signature, |
1469 | | // with message as M, using the Ed25519 public key associated with key. |
1470 | | |
1471 | 0 | auto public_key = key->handle().get<ByteBuffer>(); |
1472 | | |
1473 | | // 9. Let result be a boolean with the value true if the signature is valid and the value false otherwise. |
1474 | 0 | ::Crypto::Curves::Ed25519 curve; |
1475 | 0 | auto result = curve.verify(public_key, signature, message); |
1476 | | |
1477 | | // 10. Return result. |
1478 | 0 | return JS::Value(result); |
1479 | 0 | } |
1480 | | |
1481 | | // https://w3c.github.io/webcrypto/#hkdf-operations |
1482 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> HKDF::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, Optional<u32> length_optional) |
1483 | 0 | { |
1484 | 0 | auto& realm = *m_realm; |
1485 | 0 | auto const& normalized_algorithm = static_cast<HKDFParams const&>(params); |
1486 | | |
1487 | | // 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError. |
1488 | 0 | auto length = length_optional.value_or(0); |
1489 | |
|
1490 | 0 | if (length == 0 || length % 8 != 0) |
1491 | 0 | return WebIDL::OperationError::create(realm, "Length must be greater than 0 and divisible by 8"_string); |
1492 | | |
1493 | | // 2. Let keyDerivationKey be the secret represented by [[handle]] internal slot of key as the message. |
1494 | 0 | auto key_derivation_key = key->handle().get<ByteBuffer>(); |
1495 | | |
1496 | | // 3. Let result be the result of performing the HKDF extract and then the HKDF expand step described in Section 2 of [RFC5869] using: |
1497 | | // * the hash member of normalizedAlgorithm as Hash, |
1498 | | // * keyDerivationKey as the input keying material, IKM, |
1499 | | // * the contents of the salt member of normalizedAlgorithm as salt, |
1500 | | // * the contents of the info member of normalizedAlgorithm as info, |
1501 | | // * length divided by 8 as the value of L, |
1502 | | // Note: Although HKDF technically supports absent salt (treating it as hashLen many NUL bytes), |
1503 | | // all major browsers instead raise a TypeError, for example: |
1504 | | // "Failed to execute 'deriveBits' on 'SubtleCrypto': HkdfParams: salt: Not a BufferSource" |
1505 | | // Because we are forced by neither peer pressure nor the spec, we don't support it either. |
1506 | 0 | auto const& hash_algorithm = TRY(normalized_algorithm.hash.name(realm.vm())); |
1507 | 0 | ErrorOr<ByteBuffer> result = Error::from_string_literal("noop error"); |
1508 | 0 | if (hash_algorithm.equals_ignoring_ascii_case("SHA-1"sv)) { |
1509 | 0 | result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA1>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, length / 8); |
1510 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-256"sv)) { |
1511 | 0 | result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA256>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, length / 8); |
1512 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-384"sv)) { |
1513 | 0 | result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA384>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, length / 8); |
1514 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-512"sv)) { |
1515 | 0 | result = ::Crypto::Hash::HKDF<::Crypto::Hash::SHA512>::derive_key(Optional<ReadonlyBytes>(normalized_algorithm.salt), key_derivation_key, normalized_algorithm.info, length / 8); |
1516 | 0 | } else { |
1517 | 0 | return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm))); |
1518 | 0 | } |
1519 | | |
1520 | | // 4. If the key derivation operation fails, then throw an OperationError. |
1521 | 0 | if (result.is_error()) |
1522 | 0 | return WebIDL::OperationError::create(realm, "Failed to derive key"_string); |
1523 | | |
1524 | | // 5. Return result |
1525 | 0 | return JS::ArrayBuffer::create(realm, result.release_value()); |
1526 | 0 | } |
1527 | | |
1528 | | WebIDL::ExceptionOr<JS::Value> HKDF::get_key_length(AlgorithmParams const&) |
1529 | 0 | { |
1530 | | // 1. Return null. |
1531 | 0 | return JS::js_null(); |
1532 | 0 | } |
1533 | | |
1534 | | WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> PBKDF2::derive_bits(AlgorithmParams const& params, JS::NonnullGCPtr<CryptoKey> key, Optional<u32> length_optional) |
1535 | 0 | { |
1536 | 0 | auto& realm = *m_realm; |
1537 | 0 | auto const& normalized_algorithm = static_cast<PBKDF2Params const&>(params); |
1538 | | |
1539 | | // 1. If length is null or zero, or is not a multiple of 8, then throw an OperationError. |
1540 | 0 | auto length = length_optional.value_or(0); |
1541 | |
|
1542 | 0 | if (length == 0 || length % 8 != 0) |
1543 | 0 | return WebIDL::OperationError::create(realm, "Length must be greater than 0 and divisible by 8"_string); |
1544 | | |
1545 | | // 2. If the iterations member of normalizedAlgorithm is zero, then throw an OperationError. |
1546 | 0 | if (normalized_algorithm.iterations == 0) |
1547 | 0 | return WebIDL::OperationError::create(realm, "Iterations must be greater than 0"_string); |
1548 | | |
1549 | | // 3. Let prf be the MAC Generation function described in Section 4 of [FIPS-198-1] using the hash function described by the hash member of normalizedAlgorithm. |
1550 | 0 | auto const& hash_algorithm = TRY(normalized_algorithm.hash.name(realm.vm())); |
1551 | | |
1552 | | // 4. Let result be the result of performing the PBKDF2 operation defined in Section 5.2 of [RFC8018] |
1553 | | // using prf as the pseudo-random function, PRF, |
1554 | | // the password represented by [[handle]] internal slot of key as the password, P, |
1555 | | // the contents of the salt attribute of normalizedAlgorithm as the salt, S, |
1556 | | // the value of the iterations attribute of normalizedAlgorithm as the iteration count, c, |
1557 | | // and length divided by 8 as the intended key length, dkLen. |
1558 | 0 | ErrorOr<ByteBuffer> result = Error::from_string_literal("noop error"); |
1559 | |
|
1560 | 0 | auto password = key->handle().get<ByteBuffer>(); |
1561 | |
|
1562 | 0 | auto salt = normalized_algorithm.salt; |
1563 | 0 | auto iterations = normalized_algorithm.iterations; |
1564 | 0 | auto derived_key_length_bytes = length / 8; |
1565 | |
|
1566 | 0 | if (hash_algorithm.equals_ignoring_ascii_case("SHA-1"sv)) { |
1567 | 0 | result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA1>>(password, salt, iterations, derived_key_length_bytes); |
1568 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-256"sv)) { |
1569 | 0 | result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA256>>(password, salt, iterations, derived_key_length_bytes); |
1570 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-384"sv)) { |
1571 | 0 | result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA384>>(password, salt, iterations, derived_key_length_bytes); |
1572 | 0 | } else if (hash_algorithm.equals_ignoring_ascii_case("SHA-512"sv)) { |
1573 | 0 | result = ::Crypto::Hash::PBKDF2::derive_key<::Crypto::Authentication::HMAC<::Crypto::Hash::SHA512>>(password, salt, iterations, derived_key_length_bytes); |
1574 | 0 | } else { |
1575 | 0 | return WebIDL::NotSupportedError::create(m_realm, MUST(String::formatted("Invalid hash function '{}'", hash_algorithm))); |
1576 | 0 | } |
1577 | | |
1578 | | // 5. If the key derivation operation fails, then throw an OperationError. |
1579 | 0 | if (result.is_error()) |
1580 | 0 | return WebIDL::OperationError::create(realm, "Failed to derive key"_string); |
1581 | | |
1582 | | // 6. Return result |
1583 | 0 | return JS::ArrayBuffer::create(realm, result.release_value()); |
1584 | 0 | } |
1585 | | |
1586 | | WebIDL::ExceptionOr<JS::Value> PBKDF2::get_key_length(AlgorithmParams const&) |
1587 | 0 | { |
1588 | | // 1. Return null. |
1589 | 0 | return JS::js_null(); |
1590 | 0 | } |
1591 | | |
1592 | | } |