CryptoAlgorithms.cpp 107 KB

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