/src/serenity/Userland/Libraries/LibWeb/Crypto/Crypto.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org> |
3 | | * Copyright (c) 2022, stelar7 <dudedbz@gmail.com> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/Random.h> |
9 | | #include <AK/StringBuilder.h> |
10 | | #include <LibJS/Runtime/TypedArray.h> |
11 | | #include <LibWeb/Bindings/CryptoPrototype.h> |
12 | | #include <LibWeb/Bindings/ExceptionOrUtils.h> |
13 | | #include <LibWeb/Bindings/Intrinsics.h> |
14 | | #include <LibWeb/Crypto/Crypto.h> |
15 | | #include <LibWeb/Crypto/SubtleCrypto.h> |
16 | | #include <LibWeb/WebIDL/Buffers.h> |
17 | | |
18 | | namespace Web::Crypto { |
19 | | |
20 | | JS_DEFINE_ALLOCATOR(Crypto); |
21 | | |
22 | | JS::NonnullGCPtr<Crypto> Crypto::create(JS::Realm& realm) |
23 | 0 | { |
24 | 0 | return realm.heap().allocate<Crypto>(realm, realm); |
25 | 0 | } |
26 | | |
27 | | Crypto::Crypto(JS::Realm& realm) |
28 | 0 | : PlatformObject(realm) |
29 | 0 | { |
30 | 0 | } |
31 | | |
32 | 0 | Crypto::~Crypto() = default; |
33 | | |
34 | | void Crypto::initialize(JS::Realm& realm) |
35 | 0 | { |
36 | 0 | Base::initialize(realm); |
37 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(Crypto); |
38 | 0 | m_subtle = SubtleCrypto::create(realm); |
39 | 0 | } |
40 | | |
41 | | JS::NonnullGCPtr<SubtleCrypto> Crypto::subtle() const |
42 | 0 | { |
43 | 0 | return *m_subtle; |
44 | 0 | } |
45 | | |
46 | | // https://w3c.github.io/webcrypto/#dfn-Crypto-method-getRandomValues |
47 | | WebIDL::ExceptionOr<JS::Handle<WebIDL::ArrayBufferView>> Crypto::get_random_values(JS::Handle<WebIDL::ArrayBufferView> array) const |
48 | 0 | { |
49 | | // 1. If array is not an Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array, then throw a TypeMismatchError and terminate the algorithm. |
50 | 0 | if (!array->is_typed_array_base()) |
51 | 0 | return WebIDL::TypeMismatchError::create(realm(), "array must be one of Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array"_string); |
52 | | |
53 | 0 | auto const& typed_array = *array->bufferable_object().get<JS::NonnullGCPtr<JS::TypedArrayBase>>(); |
54 | | // Still need to exclude Float32Array, and potential future siblings like Float16Array: |
55 | 0 | if (!typed_array.element_name().is_one_of("Int8Array", "Uint8Array", "Uint8ClampedArray", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "BigInt64Array", "BigUint64Array")) |
56 | 0 | return WebIDL::TypeMismatchError::create(realm(), "array must be one of Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, BigInt64Array, or BigUint64Array"_string); |
57 | | |
58 | 0 | auto typed_array_record = JS::make_typed_array_with_buffer_witness_record(typed_array, JS::ArrayBuffer::Order::SeqCst); |
59 | | |
60 | | // IMPLEMENTATION DEFINED: If the viewed array buffer is out-of-bounds, throw a InvalidStateError and terminate the algorithm. |
61 | 0 | if (JS::is_typed_array_out_of_bounds(typed_array_record)) |
62 | 0 | return WebIDL::InvalidStateError::create(realm(), MUST(String::formatted(JS::ErrorType::BufferOutOfBounds.message(), "TypedArray"sv))); |
63 | | |
64 | | // 2. If the byteLength of array is greater than 65536, throw a QuotaExceededError and terminate the algorithm. |
65 | 0 | if (JS::typed_array_byte_length(typed_array_record) > 65536) |
66 | 0 | return WebIDL::QuotaExceededError::create(realm(), "array's byteLength may not be greater than 65536"_string); |
67 | | |
68 | | // FIXME: Handle SharedArrayBuffers |
69 | | |
70 | | // 3. Overwrite all elements of array with cryptographically strong random values of the appropriate type. |
71 | 0 | fill_with_random(array->viewed_array_buffer()->buffer().bytes().slice(array->byte_offset(), array->byte_length())); |
72 | | |
73 | | // 4. Return array. |
74 | 0 | return array; |
75 | 0 | } |
76 | | |
77 | | // https://w3c.github.io/webcrypto/#dfn-Crypto-method-randomUUID |
78 | | WebIDL::ExceptionOr<String> Crypto::random_uuid() const |
79 | 0 | { |
80 | 0 | auto& vm = realm().vm(); |
81 | |
|
82 | 0 | return TRY_OR_THROW_OOM(vm, generate_random_uuid()); |
83 | 0 | } |
84 | | |
85 | | void Crypto::visit_edges(Cell::Visitor& visitor) |
86 | 0 | { |
87 | 0 | Base::visit_edges(visitor); |
88 | 0 | visitor.visit(m_subtle); |
89 | 0 | } |
90 | | |
91 | | // https://w3c.github.io/webcrypto/#dfn-generate-a-random-uuid |
92 | | ErrorOr<String> generate_random_uuid() |
93 | 0 | { |
94 | | // 1. Let bytes be a byte sequence of length 16. |
95 | 0 | u8 bytes[16]; |
96 | | |
97 | | // 2. Fill bytes with cryptographically secure random bytes. |
98 | 0 | fill_with_random(bytes); |
99 | | |
100 | | // 3. Set the 4 most significant bits of bytes[6], which represent the UUID version, to 0100. |
101 | 0 | bytes[6] &= ~(1 << 7); |
102 | 0 | bytes[6] |= 1 << 6; |
103 | 0 | bytes[6] &= ~(1 << 5); |
104 | 0 | bytes[6] &= ~(1 << 4); |
105 | | |
106 | | // 4. Set the 2 most significant bits of bytes[8], which represent the UUID variant, to 10. |
107 | 0 | bytes[8] |= 1 << 7; |
108 | 0 | bytes[8] &= ~(1 << 6); |
109 | | |
110 | | /* 5. Return the string concatenation of |
111 | | « |
112 | | hexadecimal representation of bytes[0], |
113 | | hexadecimal representation of bytes[1], |
114 | | hexadecimal representation of bytes[2], |
115 | | hexadecimal representation of bytes[3], |
116 | | "-", |
117 | | hexadecimal representation of bytes[4], |
118 | | hexadecimal representation of bytes[5], |
119 | | "-", |
120 | | hexadecimal representation of bytes[6], |
121 | | hexadecimal representation of bytes[7], |
122 | | "-", |
123 | | hexadecimal representation of bytes[8], |
124 | | hexadecimal representation of bytes[9], |
125 | | "-", |
126 | | hexadecimal representation of bytes[10], |
127 | | hexadecimal representation of bytes[11], |
128 | | hexadecimal representation of bytes[12], |
129 | | hexadecimal representation of bytes[13], |
130 | | hexadecimal representation of bytes[14], |
131 | | hexadecimal representation of bytes[15] |
132 | | ». |
133 | | */ |
134 | 0 | StringBuilder builder; |
135 | 0 | TRY(builder.try_appendff("{:02x}{:02x}{:02x}{:02x}-", bytes[0], bytes[1], bytes[2], bytes[3])); |
136 | 0 | TRY(builder.try_appendff("{:02x}{:02x}-", bytes[4], bytes[5])); |
137 | 0 | TRY(builder.try_appendff("{:02x}{:02x}-", bytes[6], bytes[7])); |
138 | 0 | TRY(builder.try_appendff("{:02x}{:02x}-", bytes[8], bytes[9])); |
139 | 0 | TRY(builder.try_appendff("{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}", bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15])); |
140 | | |
141 | 0 | return builder.to_string(); |
142 | 0 | } |
143 | | |
144 | | } |