022692506d26e4dec8a7a146c58c2eda88437646
[WebKit-https.git] / Source / WebCore / bindings / js / JSCryptoKeySerializationJWK.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #include "config.h"
27 #include "JSCryptoKeySerializationJWK.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithm.h"
32 #include "CryptoAlgorithmHmacParams.h"
33 #include "CryptoAlgorithmRegistry.h"
34 #include "CryptoAlgorithmRsaSsaKeyParams.h"
35 #include "CryptoKeyDataOctetSequence.h"
36 #include "CryptoKeyDataRSAComponents.h"
37 #include "ExceptionCode.h"
38 #include "JSDOMBinding.h"
39 #include <heap/StrongInlines.h>
40 #include <runtime/JSONObject.h>
41 #include <wtf/text/Base64.h>
42
43 using namespace JSC;
44
45 namespace WebCore {
46
47 static bool getJSArrayFromJSON(ExecState* exec, JSObject* json, const char* key, JSArray*& result)
48 {
49     Identifier identifier(exec, key);
50     PropertySlot slot(json);
51
52     if (!json->getPropertySlot(exec, identifier, slot))
53         return false;
54
55     JSValue value = slot.getValue(exec, identifier);
56     ASSERT(!exec->hadException());
57     if (isJSArray(value)) {
58         throwTypeError(exec, String::format("Expected an array for \"%s\" JSON key",  key));
59         return false;
60     }
61
62     result = asArray(value);
63
64     return true;
65 }
66
67 static bool getStringFromJSON(ExecState* exec, JSObject* json, const char* key, String& result)
68 {
69     Identifier identifier(exec, key);
70     PropertySlot slot(json);
71
72     if (!json->getPropertySlot(exec, identifier, slot))
73         return false;
74
75     JSValue jsValue = slot.getValue(exec, identifier);
76     ASSERT(!exec->hadException());
77     if (!jsValue.getString(exec, result)) {
78         // Can get an out of memory exception.
79         if (exec->hadException())
80             return false;
81         throwTypeError(exec, String::format("Expected a string value for \"%s\" JSON key",  key));
82         return false;
83     }
84
85     return true;
86 }
87
88 static bool getBooleanFromJSON(ExecState* exec, JSObject* json, const char* key, bool& result)
89 {
90     Identifier identifier(exec, key);
91     PropertySlot slot(json);
92
93     if (!json->getPropertySlot(exec, identifier, slot))
94         return false;
95
96     JSValue jsValue = slot.getValue(exec, identifier);
97     ASSERT(!exec->hadException());
98     if (!jsValue.isBoolean()) {
99         throwTypeError(exec, String::format("Expected a boolean value for \"%s\" JSON key",  key));
100         return false;
101     }
102
103     result = jsValue.asBoolean();
104     return true;
105 }
106
107 static bool getBigIntegerVectorFromJSON(ExecState* exec, JSObject* json, const char* key, Vector<char>& result)
108 {
109     String base64urlEncodedNumber;
110     if (!getStringFromJSON(exec, json, key, base64urlEncodedNumber))
111         return false;
112
113     if (!base64URLDecode(base64urlEncodedNumber, result)) {
114         throwTypeError(exec, "Cannot decode base64url key data in JWK");
115         return false;
116     }
117
118     if (result[0] == 0) {
119         throwTypeError(exec, "JWK BigInteger must utilize the minimum number of octets to represent the value");
120         return false;
121     }
122
123     return true;
124 }
125
126 JSCryptoKeySerializationJWK::JSCryptoKeySerializationJWK(ExecState* exec, const String& jsonString)
127     : m_exec(exec)
128 {
129     JSValue jsonValue = JSONParse(exec, jsonString);
130     if (exec->hadException())
131         return;
132
133     if (!jsonValue || !jsonValue.isObject()) {
134         throwTypeError(exec, "Invalid JWK serialization");
135         return;
136     }
137
138     m_json.set(m_exec->vm(), asObject(jsonValue));
139 }
140
141 JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
142 {
143 }
144
145 static std::unique_ptr<CryptoAlgorithmParameters> createHMACParameters(CryptoAlgorithmIdentifier hashFunction)
146 {
147     std::unique_ptr<CryptoAlgorithmHmacParams> hmacParameters = std::make_unique<CryptoAlgorithmHmacParams>();
148     hmacParameters->hash = hashFunction;
149     return std::move(hmacParameters);
150 }
151
152 static std::unique_ptr<CryptoAlgorithmParameters> createRSASSAKeyParameters(CryptoAlgorithmIdentifier hashFunction)
153 {
154     std::unique_ptr<CryptoAlgorithmRsaSsaKeyParams> rsaSSAParameters = std::make_unique<CryptoAlgorithmRsaSsaKeyParams>();
155     rsaSSAParameters->hasHash = true;
156     rsaSSAParameters->hash = hashFunction;
157     return std::move(rsaSSAParameters);
158 }
159
160
161 bool JSCryptoKeySerializationJWK::reconcileAlgorithm(std::unique_ptr<CryptoAlgorithm>& suggestedAlgorithm, std::unique_ptr<CryptoAlgorithmParameters>& suggestedParameters) const
162 {
163     if (!getStringFromJSON(m_exec, m_json.get(), "alg", m_jwkAlgorithmName)) {
164         // Algorithm is optional in JWK.
165         return true;
166     }
167
168     std::unique_ptr<CryptoAlgorithm> algorithm;
169     std::unique_ptr<CryptoAlgorithmParameters> parameters;
170     if (m_jwkAlgorithmName == "HS256") {
171         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
172         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_256);
173     } else if (m_jwkAlgorithmName == "HS384") {
174         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
175         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_384);
176     } else if (m_jwkAlgorithmName == "HS512") {
177         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
178         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_512);
179     } else if (m_jwkAlgorithmName == "RS256") {
180         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
181         parameters = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_256);
182     } else if (m_jwkAlgorithmName == "RS384") {
183         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
184         parameters = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_384);
185     } else if (m_jwkAlgorithmName == "RS512") {
186         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5);
187         parameters = createRSASSAKeyParameters(CryptoAlgorithmIdentifier::SHA_512);
188     } else if (m_jwkAlgorithmName == "A128CBC") {
189         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
190         parameters = std::make_unique<CryptoAlgorithmParameters>();
191     } else if (m_jwkAlgorithmName == "A192CBC") {
192         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
193         parameters = std::make_unique<CryptoAlgorithmParameters>();
194     } else if (m_jwkAlgorithmName == "A256CBC") {
195         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
196         parameters = std::make_unique<CryptoAlgorithmParameters>();
197     } else {
198         throwTypeError(m_exec, "Unsupported JWK algorithm " + m_jwkAlgorithmName);
199         return false;
200     }
201
202     if (!suggestedAlgorithm) {
203         suggestedAlgorithm = std::move(algorithm);
204         suggestedParameters =  std::move(parameters);
205         return true;
206     }
207
208     if (!algorithm)
209         return true;
210
211     if (algorithm->identifier() != suggestedAlgorithm->identifier())
212         return false;
213
214     if (algorithm->identifier() == CryptoAlgorithmIdentifier::HMAC)
215         return static_cast<CryptoAlgorithmHmacParams&>(*parameters).hash == static_cast<CryptoAlgorithmHmacParams&>(*suggestedParameters).hash;
216     if (algorithm->identifier() == CryptoAlgorithmIdentifier::RSASSA_PKCS1_v1_5) {
217         CryptoAlgorithmRsaSsaKeyParams& rsaSSAParameters = static_cast<CryptoAlgorithmRsaSsaKeyParams&>(*parameters);
218         CryptoAlgorithmRsaSsaKeyParams& suggestedRSASSAParameters = static_cast<CryptoAlgorithmRsaSsaKeyParams&>(*suggestedParameters);
219         ASSERT(rsaSSAParameters.hasHash);
220         if (suggestedRSASSAParameters.hasHash)
221             return suggestedRSASSAParameters.hash == rsaSSAParameters.hash;
222         suggestedRSASSAParameters.hasHash = true;
223         suggestedRSASSAParameters.hash = rsaSSAParameters.hash;
224     }
225
226     // Other algorithms don't have parameters.
227     return true;
228 }
229
230 void JSCryptoKeySerializationJWK::reconcileUsages(CryptoKeyUsage& suggestedUsage) const
231 {
232     String jwkUseString;
233     if (!getStringFromJSON(m_exec, m_json.get(), "use", jwkUseString)) {
234         // "use" is optional in JWK.
235         return;
236     }
237
238     // FIXME: CryptoKeyUsageDeriveKey, CryptoKeyUsageDeriveBits - should these be implicitly allowed by any JWK use value?
239     // FIXME: There is a mismatch between specs for wrap/unwrap usages, <http://lists.w3.org/Archives/Public/public-webcrypto/2013Nov/0016.html>.
240     if (jwkUseString == "sig")
241         suggestedUsage = suggestedUsage & (CryptoKeyUsageSign | CryptoKeyUsageVerify);
242     else if (jwkUseString == "enc")
243         suggestedUsage = suggestedUsage & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
244     else if (jwkUseString == "wrap")
245         suggestedUsage = suggestedUsage & (CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
246     else
247         suggestedUsage = 0; // Unknown usage, better be safe.
248 }
249
250 void JSCryptoKeySerializationJWK::reconcileExtractable(bool& suggestedExtractable) const
251 {
252     bool jwkExtractable;
253     if (!getBooleanFromJSON(m_exec, m_json.get(), "extractable", jwkExtractable)) {
254         // "extractable" is a Netflix proposal that's not in any spec yet. It will certainly be optional once specified.
255         return;
256     }
257
258     suggestedExtractable = suggestedExtractable && jwkExtractable;
259 }
260
261 bool JSCryptoKeySerializationJWK::keySizeIsValid(size_t sizeInBits) const
262 {
263     if (m_jwkAlgorithmName == "HS256")
264         return sizeInBits >= 256;
265     if (m_jwkAlgorithmName == "HS384")
266         return sizeInBits >= 384;
267     if (m_jwkAlgorithmName == "HS512")
268         return sizeInBits >= 512;
269     if (m_jwkAlgorithmName == "A128CBC")
270         return sizeInBits == 128;
271     if (m_jwkAlgorithmName == "A192CBC")
272         return sizeInBits == 192;
273     if (m_jwkAlgorithmName == "A256CBC")
274         return sizeInBits == 256;
275     return true;
276 }
277
278 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataOctetSequence() const
279 {
280     String keyBase64URL;
281     if (!getStringFromJSON(m_exec, m_json.get(), "k", keyBase64URL)) {
282         if (!m_exec->hadException())
283             throwTypeError(m_exec, "Secret key data is not present is JWK");
284         return nullptr;
285     }
286
287     Vector<char> octetSequence;
288     if (!base64URLDecode(keyBase64URL, octetSequence)) {
289         throwTypeError(m_exec, "Cannot decode base64url key data in JWK");
290         return nullptr;
291     }
292
293     if (!keySizeIsValid(octetSequence.size() * 8)) {
294         throwTypeError(m_exec, "Key size is not valid for " + m_jwkAlgorithmName);
295         return nullptr;
296     }
297
298     return CryptoKeyDataOctetSequence::create(octetSequence);
299 }
300
301 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyDataRSAComponents() const
302 {
303     Vector<char> modulus;
304     Vector<char> exponent;
305     Vector<char> privateExponent;
306
307     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "n", modulus)) {
308         if (!m_exec->hadException())
309             throwTypeError(m_exec, "Required JWK \"n\" member is missing");
310         return nullptr;
311     }
312
313     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "e", exponent)) {
314         if (!m_exec->hadException())
315             throwTypeError(m_exec, "Required JWK \"e\" member is missing");
316         return nullptr;
317     }
318
319     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "d", modulus)) {
320         if (m_exec->hadException())
321             return nullptr;
322         return CryptoKeyDataRSAComponents::createPublic(modulus, exponent);
323     }
324
325     CryptoKeyDataRSAComponents::PrimeInfo firstPrimeInfo;
326     CryptoKeyDataRSAComponents::PrimeInfo secondPrimeInfo;
327     Vector<CryptoKeyDataRSAComponents::PrimeInfo> otherPrimeInfos;
328     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "p", firstPrimeInfo.primeFactor)) {
329         if (m_exec->hadException())
330             return nullptr;
331         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
332     }
333
334     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dp", firstPrimeInfo.factorCRTExponent)) {
335         if (m_exec->hadException())
336             return nullptr;
337         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
338     }
339
340     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "q", secondPrimeInfo.primeFactor)) {
341         if (m_exec->hadException())
342             return nullptr;
343         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
344     }
345
346     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "dq", secondPrimeInfo.factorCRTExponent)) {
347         if (m_exec->hadException())
348             return nullptr;
349         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
350     }
351
352     if (!getBigIntegerVectorFromJSON(m_exec, m_json.get(), "qi", secondPrimeInfo.factorCRTCoefficient)) {
353         if (m_exec->hadException())
354             return nullptr;
355         return CryptoKeyDataRSAComponents::createPrivate(modulus, exponent, privateExponent);
356     }
357
358     JSArray* otherPrimeInfoJSArray;
359     if (!getJSArrayFromJSON(m_exec, m_json.get(), "oth", otherPrimeInfoJSArray)) {
360         if (m_exec->hadException())
361             return nullptr;
362         return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
363     }
364
365     for (size_t i = 0; i < otherPrimeInfoJSArray->length(); ++i) {
366         CryptoKeyDataRSAComponents::PrimeInfo info;
367         JSValue element = otherPrimeInfoJSArray->getIndex(m_exec, i);
368         if (m_exec->hadException())
369             return nullptr;
370         if (!element.isObject()) {
371             throwTypeError(m_exec, "JWK \"oth\" array member is not an object");
372             return nullptr;
373         }
374         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "r", info.primeFactor)) {
375             if (!m_exec->hadException())
376                 throwTypeError(m_exec, "Cannot get prime factor for a prime in \"oth\" dictionary");
377             return nullptr;
378         }
379         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "d", info.factorCRTExponent)) {
380             if (!m_exec->hadException())
381                 throwTypeError(m_exec, "Cannot get factor CRT exponent for a prime in \"oth\" dictionary");
382             return nullptr;
383         }
384         if (!getBigIntegerVectorFromJSON(m_exec, asObject(element), "t", info.factorCRTCoefficient)) {
385             if (!m_exec->hadException())
386                 throwTypeError(m_exec, "Cannot get factor CRT coefficient for a prime in \"oth\" dictionary");
387             return nullptr;
388         }
389         otherPrimeInfos.append(info);
390     }
391
392     return CryptoKeyDataRSAComponents::createPrivateWithAdditionalData(modulus, exponent, privateExponent, firstPrimeInfo, secondPrimeInfo, otherPrimeInfos);
393 }
394
395 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyData() const
396 {
397     String jwkKeyType;
398     if (!getStringFromJSON(m_exec, m_json.get(), "kty", jwkKeyType)) {
399         if (!m_exec->hadException())
400             throwTypeError(m_exec, "Required JWK \"kty\" member is missing");
401         return nullptr;
402     }
403
404     if (jwkKeyType == "oct")
405         return keyDataOctetSequence();
406
407     if (jwkKeyType == "RSA")
408         return keyDataRSAComponents();
409
410     throwTypeError(m_exec, "Unsupported JWK key type " + jwkKeyType);
411     return nullptr;
412 }
413
414 } // namespace WebCore
415
416 #endif // ENABLE(SUBTLE_CRYPTO)