2 * Copyright (C) 2013, 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "JSCryptoKeySerializationJWK.h"
29 #if ENABLE(SUBTLE_CRYPTO)
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmHmacParams.h"
33 #include "CryptoAlgorithmRegistry.h"
34 #include "CryptoAlgorithmRsaKeyParamsWithHash.h"
35 #include "CryptoKey.h"
36 #include "CryptoKeyAES.h"
37 #include "CryptoKeyDataOctetSequence.h"
38 #include "CryptoKeyDataRSAComponents.h"
39 #include "CryptoKeyHMAC.h"
40 #include "CryptoKeyRSA.h"
41 #include "ExceptionCode.h"
42 #include "JSDOMBinding.h"
43 #include <heap/StrongInlines.h>
44 #include <runtime/JSCInlines.h>
45 #include <runtime/JSONObject.h>
46 #include <runtime/ObjectConstructor.h>
47 #include <wtf/text/Base64.h>
53 static bool getJSArrayFromJSON(ExecState* exec, JSObject* json, const char* key, JSArray*& result)
56 auto scope = DECLARE_THROW_SCOPE(vm);
58 Identifier identifier = Identifier::fromString(exec, key);
59 PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
61 if (!json->getPropertySlot(exec, identifier, slot))
64 JSValue value = slot.getValue(exec, identifier);
65 ASSERT(!scope.exception());
66 if (!isJSArray(value)) {
67 throwTypeError(exec, scope, String::format("Expected an array for \"%s\" JSON key", key));
71 result = asArray(value);
76 static bool getStringFromJSON(ExecState* exec, JSObject* json, const char* key, String& result)
79 auto scope = DECLARE_THROW_SCOPE(vm);
81 Identifier identifier = Identifier::fromString(exec, key);
82 PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
84 if (!json->getPropertySlot(exec, identifier, slot))
87 JSValue jsValue = slot.getValue(exec, identifier);
88 ASSERT(!scope.exception());
89 if (!jsValue.getString(exec, result)) {
90 // Can get an out of memory exception.
91 RETURN_IF_EXCEPTION(scope, false);
92 throwTypeError(exec, scope, String::format("Expected a string value for \"%s\" JSON key", key));
99 static bool getBooleanFromJSON(ExecState* exec, JSObject* json, const char* key, bool& result)
102 auto scope = DECLARE_THROW_SCOPE(vm);
104 Identifier identifier = Identifier::fromString(exec, key);
105 PropertySlot slot(json, PropertySlot::InternalMethodType::Get);
107 if (!json->getPropertySlot(exec, identifier, slot))
110 JSValue jsValue = slot.getValue(exec, identifier);
111 ASSERT(!scope.exception());
112 if (!jsValue.isBoolean()) {
113 throwTypeError(exec, scope, String::format("Expected a boolean value for \"%s\" JSON key", key));
117 result = jsValue.asBoolean();
121 static bool getBigIntegerVectorFromJSON(ExecState* exec, JSObject* json, const char* key, Vector<uint8_t>& result)
124 auto scope = DECLARE_THROW_SCOPE(vm);
126 String base64urlEncodedNumber;
127 if (!getStringFromJSON(exec, json, key, base64urlEncodedNumber))
130 if (!base64URLDecode(base64urlEncodedNumber, result)) {
131 throwTypeError(exec, scope, ASCIILiteral("Cannot decode base64url key data in JWK"));
135 if (result[0] == 0) {
136 throwTypeError(exec, scope, ASCIILiteral("JWK BigInteger must utilize the minimum number of octets to represent the value"));
143 JSCryptoKeySerializationJWK::JSCryptoKeySerializationJWK(ExecState* exec, const String& jsonString)
147 auto scope = DECLARE_THROW_SCOPE(vm);
149 JSValue jsonValue = JSONParse(exec, jsonString);
150 if (UNLIKELY(scope.exception()))
153 if (!jsonValue || !jsonValue.isObject()) {
154 throwTypeError(exec, scope, ASCIILiteral("Invalid JWK serialization"));
158 m_json.set(vm, asObject(jsonValue));
161 JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
165 static Ref<CryptoAlgorithmParameters> createHMACParameters(CryptoAlgorithmIdentifier hashFunction)
167 auto hmacParameters = adoptRef(*new CryptoAlgorithmHmacParams);
168 hmacParameters->hash = hashFunction;
169 return WTFMove(hmacParameters);
172 static Ref<CryptoAlgorithmParameters> createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier hashFunction)
174 auto rsaKeyParameters = adoptRef(*new CryptoAlgorithmRsaKeyParamsWithHash);
175 rsaKeyParameters->hasHash = true;
176 rsaKeyParameters->hash = hashFunction;
177 return WTFMove(rsaKeyParameters);
180 Optional<CryptoAlgorithmPair> JSCryptoKeySerializationJWK::reconcileAlgorithm(CryptoAlgorithm* suggestedAlgorithm, CryptoAlgorithmParameters* suggestedParameters) const
182 VM& vm = m_exec->vm();
183 auto scope = DECLARE_THROW_SCOPE(vm);
185 if (!getStringFromJSON(m_exec, m_json.get(), "alg", m_jwkAlgorithmName)) {
186 // Algorithm is optional in JWK.
187 return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
190 auto& algorithmRegisty = CryptoAlgorithmRegistry::singleton();
191 RefPtr<CryptoAlgorithm> algorithm;
192 RefPtr<CryptoAlgorithmParameters> parameters;
193 if (m_jwkAlgorithmName == "HS256") {
194 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
195 parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_256);
196 } else if (m_jwkAlgorithmName == "HS384") {
197 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
198 parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_384);
199 } else if (m_jwkAlgorithmName == "HS512") {
200 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::HMAC);
201 parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_512);
202 } else if (m_jwkAlgorithmName == "RS256") {
203 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
204 parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_256);
205 } else if (m_jwkAlgorithmName == "RS384") {
206 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
207 parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_384);
208 } else if (m_jwkAlgorithmName == "RS512") {
209 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
210 parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_512);
211 } else if (m_jwkAlgorithmName == "RSA1_5") {
212 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5);
213 parameters = adoptRef(*new CryptoAlgorithmRsaKeyParamsWithHash);
214 } else if (m_jwkAlgorithmName == "RSA-OAEP") {
215 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::RSA_OAEP);
216 parameters = createRSAKeyParametersWithHash(CryptoAlgorithmIdentifier::SHA_1);
217 } else if (m_jwkAlgorithmName == "A128CBC") {
218 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
219 parameters = adoptRef(*new CryptoAlgorithmParameters);
220 } else if (m_jwkAlgorithmName == "A192CBC") {
221 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
222 parameters = adoptRef(*new CryptoAlgorithmParameters);
223 } else if (m_jwkAlgorithmName == "A256CBC") {
224 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_CBC);
225 parameters = adoptRef(*new CryptoAlgorithmParameters);
226 } else if (m_jwkAlgorithmName == "A128KW") {
227 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
228 parameters = adoptRef(*new CryptoAlgorithmParameters);
229 } else if (m_jwkAlgorithmName == "A192KW") {
230 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
231 parameters = adoptRef(*new CryptoAlgorithmParameters);
232 } else if (m_jwkAlgorithmName == "A256KW") {
233 algorithm = algorithmRegisty.create(CryptoAlgorithmIdentifier::AES_KW);
234 parameters = adoptRef(*new CryptoAlgorithmParameters);
236 throwTypeError(m_exec, scope, "Unsupported JWK algorithm " + m_jwkAlgorithmName);
240 if (!suggestedAlgorithm)
241 return CryptoAlgorithmPair { algorithm, parameters };
244 return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
246 if (algorithm->identifier() != suggestedAlgorithm->identifier())
249 if (algorithm->identifier() == CryptoAlgorithmIdentifier::HMAC) {
250 if (downcast<CryptoAlgorithmHmacParams>(*parameters).hash != downcast<CryptoAlgorithmHmacParams>(*suggestedParameters).hash)
252 return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
254 if (algorithm->identifier() == CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5
255 || algorithm->identifier() == CryptoAlgorithmIdentifier::RSA_OAEP) {
256 CryptoAlgorithmRsaKeyParamsWithHash& rsaKeyParameters = downcast<CryptoAlgorithmRsaKeyParamsWithHash>(*parameters);
257 CryptoAlgorithmRsaKeyParamsWithHash& suggestedRSAKeyParameters = downcast<CryptoAlgorithmRsaKeyParamsWithHash>(*suggestedParameters);
258 ASSERT(rsaKeyParameters.hasHash);
259 if (suggestedRSAKeyParameters.hasHash) {
260 if (suggestedRSAKeyParameters.hash != rsaKeyParameters.hash)
262 return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
264 suggestedRSAKeyParameters.hasHash = true;
265 suggestedRSAKeyParameters.hash = rsaKeyParameters.hash;
268 // Other algorithms don't have parameters.
269 return CryptoAlgorithmPair { suggestedAlgorithm, suggestedParameters };
272 static bool tryJWKKeyOpsValue(ExecState* exec, CryptoKeyUsage& usages, const String& operation, const String& tryOperation, CryptoKeyUsage tryUsage)
275 auto scope = DECLARE_THROW_SCOPE(vm);
277 if (operation == tryOperation) {
278 if (usages & tryUsage) {
279 throwTypeError(exec, scope, ASCIILiteral("JWK key_ops contains a duplicate operation"));
287 void JSCryptoKeySerializationJWK::reconcileUsages(CryptoKeyUsage& suggestedUsages) const
289 VM& vm = m_exec->vm();
290 auto scope = DECLARE_THROW_SCOPE(vm);
292 CryptoKeyUsage jwkUsages = 0;
295 if (getJSArrayFromJSON(m_exec, m_json.get(), "key_ops", keyOps)) {
296 for (size_t i = 0; i < keyOps->length(); ++i) {
297 JSValue jsValue = keyOps->getIndex(m_exec, i);
299 if (!jsValue.getString(m_exec, operation)) {
300 if (!scope.exception())
301 throwTypeError(m_exec, scope, ASCIILiteral("JWK key_ops attribute could not be processed"));
304 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("sign"), CryptoKeyUsageSign))
306 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("verify"), CryptoKeyUsageVerify))
308 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("encrypt"), CryptoKeyUsageEncrypt))
310 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("decrypt"), CryptoKeyUsageDecrypt))
312 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("wrapKey"), CryptoKeyUsageWrapKey))
314 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("unwrapKey"), CryptoKeyUsageUnwrapKey))
316 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("deriveKey"), CryptoKeyUsageDeriveKey))
318 if (!tryJWKKeyOpsValue(m_exec, jwkUsages, operation, ASCIILiteral("deriveBits"), CryptoKeyUsageDeriveBits))
322 RETURN_IF_EXCEPTION(scope, void());
325 if (!getStringFromJSON(m_exec, m_json.get(), "use", jwkUseString)) {
326 // We have neither key_ops nor use.
330 if (jwkUseString == "enc")
331 jwkUsages |= (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
332 else if (jwkUseString == "sig")
333 jwkUsages |= (CryptoKeyUsageSign | CryptoKeyUsageVerify);
335 throwTypeError(m_exec, scope, "Unsupported JWK key use value \"" + jwkUseString + "\"");
340 suggestedUsages = suggestedUsages & jwkUsages;
343 void JSCryptoKeySerializationJWK::reconcileExtractable(bool& suggestedExtractable) const
346 if (!getBooleanFromJSON(m_exec, m_json.get(), "ext", jwkExtractable))
349 suggestedExtractable = suggestedExtractable && jwkExtractable;
352 bool JSCryptoKeySerializationJWK::keySizeIsValid(size_t sizeInBits) const
354 if (m_jwkAlgorithmName == "HS256")
355 return sizeInBits >= 256;
356 if (m_jwkAlgorithmName == "HS384")
357 return sizeInBits >= 384;
358 if (m_jwkAlgorithmName == "HS512")
359 return sizeInBits >= 512;
360 if (m_jwkAlgorithmName == "A128CBC")
361 return sizeInBits == 128;
362 if (m_jwkAlgorithmName == "A192CBC")
363 return sizeInBits == 192;
364 if (m_jwkAlgorithmName == "A256CBC")
365 return sizeInBits == 256;
366 if (m_jwkAlgorithmName == "A128KW")
367 return sizeInBits == 128;
368 if (m_jwkAlgorithmName == "A192KW")
369 return sizeInBits == 192;
370 if (m_jwkAlgorithmName == "A256KW")
371 return sizeInBits == 256;
372 if (m_jwkAlgorithmName == "RS256")
373 return sizeInBits >= 2048;
374 if (m_jwkAlgorithmName == "RS384")
375 return sizeInBits >= 2048;
376 if (m_jwkAlgorithmName == "RS512")
377 return sizeInBits >= 2048;
378 if (m_jwkAlgorithmName == "RSA1_5")
379 return sizeInBits >= 2048;
380 if (m_jwkAlgorithmName == "RSA_OAEP")
381 return sizeInBits >= 2048;
385 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataOctetSequence() const
387 VM& vm = m_exec->vm();
388 auto scope = DECLARE_THROW_SCOPE(vm);
391 if (!getStringFromJSON(m_exec, m_json.get(), "k", keyBase64URL)) {
392 if (!scope.exception())
393 throwTypeError(m_exec, scope, ASCIILiteral("Secret key data is not present is JWK"));
397 Vector<uint8_t> octetSequence;
398 if (!base64URLDecode(keyBase64URL, octetSequence)) {
399 throwTypeError(m_exec, scope, ASCIILiteral("Cannot decode base64url key data in JWK"));
403 if (!keySizeIsValid(octetSequence.size() * 8)) {
404 throwTypeError(m_exec, scope, "Key size is not valid for " + m_jwkAlgorithmName);
408 return std::make_unique<CryptoKeyDataOctetSequence>(octetSequence);
411 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataRSAComponents() const
413 VM& vm = m_exec->vm();
414 auto scope = DECLARE_THROW_SCOPE(vm);
416 Vector<uint8_t> modulus;
417 Vector<uint8_t> exponent;
418 Vector<uint8_t> privateExponent;
420 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "n", modulus)) {
421 if (!scope.exception())
422 throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"n\" member is missing"));
426 if (!keySizeIsValid(modulus.size() * 8)) {
427 throwTypeError(m_exec, scope, "Key size is not valid for " + m_jwkAlgorithmName);
431 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "e", exponent)) {
432 if (!scope.exception())
433 throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"e\" member is missing"));
437 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "d", modulus)) {
438 RETURN_IF_EXCEPTION(scope, nullptr);
439 return CryptoKeyDataRSAComponents::createPublic(modulus, exponent);
442 CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
443 CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
444 Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos;
445 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "p", firstPrimeInfo.primeFactor)) {
446 RETURN_IF_EXCEPTION(scope, nullptr);
447 return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
450 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dp", firstPrimeInfo.factorCRTExponent)) {
451 RETURN_IF_EXCEPTION(scope, nullptr);
452 return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
455 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "q", secondPrimeInfo.primeFactor)) {
456 RETURN_IF_EXCEPTION(scope, nullptr);
457 return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
460 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dq", secondPrimeInfo.factorCRTExponent)) {
461 RETURN_IF_EXCEPTION(scope, nullptr);
462 return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
465 if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "qi", secondPrimeInfo.factorCRTCoefficient)) {
466 RETURN_IF_EXCEPTION(scope, nullptr);
467 return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
470 JSArray* otherPrimeInfoJSArray;
471 if (!getJSArrayFromJSON(m_exec, m_json.get(), "oth", otherPrimeInfoJSArray)) {
472 RETURN_IF_EXCEPTION(scope, nullptr);
473 return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
476 for (size_t i = 0; i < otherPrimeInfoJSArray->length(); ++i) {
477 CryptoKeyDataRSAComponents::PrimeInfo info;
478 JSValue element = otherPrimeInfoJSArray->getIndex(m_exec, i);
479 RETURN_IF_EXCEPTION(scope, nullptr);
480 if (!element.isObject()) {
481 throwTypeError(m_exec, scope, ASCIILiteral("JWK \"oth\" array member is not an object"));
484 if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "r", info.primeFactor)) {
485 if (!scope.exception())
486 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get prime factor for a prime in \"oth\" dictionary"));
489 if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "d", info.factorCRTExponent)) {
490 if (!scope.exception())
491 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get factor CRT exponent for a prime in \"oth\" dictionary"));
494 if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "t", info.factorCRTCoefficient)) {
495 if (!scope.exception())
496 throwTypeError(m_exec, scope, ASCIILiteral("Cannot get factor CRT coefficient for a prime in \"oth\" dictionary"));
499 otherPrimeInfos.append(info);
502 return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
505 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyData() const
507 VM& vm = m_exec->vm();
508 auto scope = DECLARE_THROW_SCOPE(vm);
511 if (!getStringFromJSON(m_exec, m_json.get(), "kty", jwkKeyType)) {
512 if (!scope.exception())
513 throwTypeError(m_exec, scope, ASCIILiteral("Required JWK \"kty\" member is missing"));
517 if (jwkKeyType == "oct")
518 return keyDataOctetSequence();
520 if (jwkKeyType == "RSA")
521 return keyDataRSAComponents();
523 throwTypeError(m_exec, scope, "Unsupported JWK key type " + jwkKeyType);
527 static void addToJSON(ExecState* exec, JSObject* json, const char* key, const String& value)
530 Identifier identifier = Identifier::fromString(&vm, key);
531 json->putDirect(vm, identifier, jsString(exec, value));
534 static void buildJSONForOctetSequence(ExecState* exec, const Vector<uint8_t>& keyData, JSObject* result)
536 addToJSON(exec, result, "kty", "oct");
537 addToJSON(exec, result, "k", base64URLEncode(keyData));
540 static void buildJSONForRSAComponents(JSC::ExecState* exec, const CryptoKeyDataRSAComponents& data, JSC::JSObject* result)
543 auto scope = DECLARE_THROW_SCOPE(vm);
545 addToJSON(exec, result, "kty", "RSA");
546 addToJSON(exec, result, "n", base64URLEncode(data.modulus()));
547 addToJSON(exec, result, "e", base64URLEncode(data.exponent()));
549 if (data.type() == CryptoKeyDataRSAComponents::Type::Public)
552 addToJSON(exec, result, "d", base64URLEncode(data.privateExponent()));
554 if (!data.hasAdditionalPrivateKeyParameters())
557 addToJSON(exec, result, "p", base64URLEncode(data.firstPrimeInfo().primeFactor));
558 addToJSON(exec, result, "q", base64URLEncode(data.secondPrimeInfo().primeFactor));
559 addToJSON(exec, result, "dp", base64URLEncode(data.firstPrimeInfo().factorCRTExponent));
560 addToJSON(exec, result, "dq", base64URLEncode(data.secondPrimeInfo().factorCRTExponent));
561 addToJSON(exec, result, "qi", base64URLEncode(data.secondPrimeInfo().factorCRTCoefficient));
563 if (data.otherPrimeInfos().isEmpty())
566 JSArray* oth = constructEmptyArray(exec, 0, exec->lexicalGlobalObject(), data.otherPrimeInfos().size());
567 RETURN_IF_EXCEPTION(scope, void());
568 for (size_t i = 0, size = data.otherPrimeInfos().size(); i < size; ++i) {
569 JSObject* jsPrimeInfo = constructEmptyObject(exec);
570 addToJSON(exec, jsPrimeInfo, "r", base64URLEncode(data.otherPrimeInfos()[i].primeFactor));
571 addToJSON(exec, jsPrimeInfo, "d", base64URLEncode(data.otherPrimeInfos()[i].factorCRTExponent));
572 addToJSON(exec, jsPrimeInfo, "t", base64URLEncode(data.otherPrimeInfos()[i].factorCRTCoefficient));
573 oth->putDirectIndex(exec, i, jsPrimeInfo);
575 result->putDirect(vm, Identifier::fromString(exec, "oth"), oth);
578 static void addBoolToJSON(ExecState* exec, JSObject* json, const char* key, bool value)
581 Identifier identifier = Identifier::fromString(&vm, key);
582 json->putDirect(vm, identifier, jsBoolean(value));
585 static void addJWKAlgorithmToJSON(ExecState* exec, JSObject* json, const CryptoKey& key)
588 auto scope = DECLARE_THROW_SCOPE(vm);
591 switch (key.algorithmIdentifier()) {
592 case CryptoAlgorithmIdentifier::HMAC:
593 switch (downcast<CryptoKeyHMAC>(key).hashAlgorithmIdentifier()) {
594 case CryptoAlgorithmIdentifier::SHA_256:
595 if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 256)
596 jwkAlgorithm = "HS256";
598 case CryptoAlgorithmIdentifier::SHA_384:
599 if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 384)
600 jwkAlgorithm = "HS384";
602 case CryptoAlgorithmIdentifier::SHA_512:
603 if (downcast<CryptoKeyHMAC>(key).key().size() * 8 >= 512)
604 jwkAlgorithm = "HS512";
610 case CryptoAlgorithmIdentifier::AES_CBC:
611 switch (downcast<CryptoKeyAES>(key).key().size() * 8) {
613 jwkAlgorithm = "A128CBC";
616 jwkAlgorithm = "A192CBC";
619 jwkAlgorithm = "A256CBC";
623 case CryptoAlgorithmIdentifier::AES_KW:
624 switch (downcast<CryptoKeyAES>(key).key().size() * 8) {
626 jwkAlgorithm = "A128KW";
629 jwkAlgorithm = "A192KW";
632 jwkAlgorithm = "A256KW";
636 case CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5: {
637 const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
638 CryptoAlgorithmIdentifier hash;
639 if (!rsaKey.isRestrictedToHash(hash))
641 if (rsaKey.keySizeInBits() < 2048)
644 case CryptoAlgorithmIdentifier::SHA_256:
645 jwkAlgorithm = "RS256";
647 case CryptoAlgorithmIdentifier::SHA_384:
648 jwkAlgorithm = "RS384";
650 case CryptoAlgorithmIdentifier::SHA_512:
651 jwkAlgorithm = "RS512";
658 case CryptoAlgorithmIdentifier::RSAES_PKCS1_v1_5: {
659 const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
660 if (rsaKey.keySizeInBits() < 2048)
662 jwkAlgorithm = "RSA1_5";
665 case CryptoAlgorithmIdentifier::RSA_OAEP: {
666 const CryptoKeyRSA& rsaKey = downcast<CryptoKeyRSA>(key);
667 CryptoAlgorithmIdentifier hash;
668 // WebCrypto RSA-OAEP keys are not tied to any particular hash, unless previously imported from JWK, which only supports SHA-1.
669 if (rsaKey.isRestrictedToHash(hash) && hash != CryptoAlgorithmIdentifier::SHA_1)
671 if (rsaKey.keySizeInBits() < 2048)
673 jwkAlgorithm = "RSA-OAEP";
680 if (jwkAlgorithm.isNull()) {
681 // The spec doesn't currently tell whether export should fail, or just skip "alg" (which is an optional key in JWK).
682 throwTypeError(exec, scope, ASCIILiteral("Key algorithm and size do not map to any JWK algorithm identifier"));
686 addToJSON(exec, json, "alg", jwkAlgorithm);
689 static void addUsagesToJSON(ExecState* exec, JSObject* json, CryptoKeyUsage usages)
692 auto scope = DECLARE_THROW_SCOPE(vm);
693 JSArray* keyOps = constructEmptyArray(exec, 0, exec->lexicalGlobalObject(), 0);
694 RETURN_IF_EXCEPTION(scope, void());
697 if (usages & CryptoKeyUsageSign)
698 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("sign")));
699 if (usages & CryptoKeyUsageVerify)
700 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("verify")));
701 if (usages & CryptoKeyUsageEncrypt)
702 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("encrypt")));
703 if (usages & CryptoKeyUsageDecrypt)
704 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("decrypt")));
705 if (usages & CryptoKeyUsageWrapKey)
706 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("wrapKey")));
707 if (usages & CryptoKeyUsageUnwrapKey)
708 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("unwrapKey")));
709 if (usages & CryptoKeyUsageDeriveKey)
710 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("deriveKey")));
711 if (usages & CryptoKeyUsageDeriveBits)
712 keyOps->putDirectIndex(exec, index++, jsNontrivialString(exec, ASCIILiteral("deriveBits")));
714 json->putDirect(vm, Identifier::fromString(exec, "key_ops"), keyOps);
717 String JSCryptoKeySerializationJWK::serialize(ExecState* exec, const CryptoKey& key)
720 auto scope = DECLARE_THROW_SCOPE(vm);
722 std::unique_ptr<CryptoKeyData> keyData = key.exportData();
724 // This generally shouldn't happen as long as all key types implement exportData(), but as underlying libraries return errors, there may be some rare failure conditions.
725 throwTypeError(exec, scope, ASCIILiteral("Couldn't export key material"));
729 JSObject* result = constructEmptyObject(exec);
731 addJWKAlgorithmToJSON(exec, result, key);
732 RETURN_IF_EXCEPTION(scope, String());
734 addBoolToJSON(exec, result, "ext", key.extractable());
736 addUsagesToJSON(exec, result, key.usagesBitmap());
737 RETURN_IF_EXCEPTION(scope, String());
739 if (is<CryptoKeyDataOctetSequence>(*keyData))
740 buildJSONForOctetSequence(exec, downcast<CryptoKeyDataOctetSequence>(*keyData).octetSequence(), result);
741 else if (is<CryptoKeyDataRSAComponents>(*keyData))
742 buildJSONForRSAComponents(exec, downcast<CryptoKeyDataRSAComponents>(*keyData), result);
744 throwTypeError(exec, scope, ASCIILiteral("Key doesn't support exportKey"));
747 RETURN_IF_EXCEPTION(scope, String());
749 return JSONStringify(exec, result, 0);
752 } // namespace WebCore
754 #endif // ENABLE(SUBTLE_CRYPTO)