Unreviewed, rolling out r159160, r159161, and r159164.
[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 "CryptoKeyDataOctetSequence.h"
35 #include "ExceptionCode.h"
36 #include "JSDOMBinding.h"
37 #include <heap/StrongInlines.h>
38 #include <runtime/JSONObject.h>
39 #include <wtf/text/Base64.h>
40
41 using namespace JSC;
42
43 namespace WebCore {
44
45 static bool getStringFromJSON(ExecState* exec, JSObject* json, const char* key, String& result)
46 {
47     Identifier identifier(exec, key);
48     PropertySlot slot(json);
49
50     if (!json->getPropertySlot(exec, identifier, slot))
51         return false;
52
53     JSValue jsValue = slot.getValue(exec, identifier);
54     ASSERT(!exec->hadException());
55     if (!jsValue.getString(exec, result)) {
56         // Can get an out of memory exception.
57         if (exec->hadException())
58             return false;
59         throwTypeError(exec, String::format("Expected a string value for \"%s\" JSON key",  key));
60         return false;
61     }
62
63     return true;
64 }
65
66 static bool getBooleanFromJSON(ExecState* exec, JSObject* json, const char* key, bool& result)
67 {
68     Identifier identifier(exec, key);
69     PropertySlot slot(json);
70
71     if (!json->getPropertySlot(exec, identifier, slot))
72         return false;
73
74     JSValue jsValue = slot.getValue(exec, identifier);
75     ASSERT(!exec->hadException());
76     if (!jsValue.isBoolean()) {
77         throwTypeError(exec, String::format("Expected a boolean value for \"%s\" JSON key",  key));
78         return false;
79     }
80
81     result = jsValue.asBoolean();
82     return true;
83 }
84
85 JSCryptoKeySerializationJWK::JSCryptoKeySerializationJWK(ExecState* exec, const String& jsonString)
86     : m_exec(exec)
87 {
88     JSValue jsonValue = JSONParse(exec, jsonString);
89     if (exec->hadException())
90         return;
91
92     if (!jsonValue || !jsonValue.isObject()) {
93         throwTypeError(exec, "Invalid JWK serialization");
94         return;
95     }
96
97     m_json.set(m_exec->vm(), asObject(jsonValue));
98 }
99
100 JSCryptoKeySerializationJWK::~JSCryptoKeySerializationJWK()
101 {
102 }
103
104 static std::unique_ptr<CryptoAlgorithmParameters> createHMACParameters(CryptoAlgorithmIdentifier hashFunction)
105 {
106     std::unique_ptr<CryptoAlgorithmHmacParams> hmacParameters = std::make_unique<CryptoAlgorithmHmacParams>();
107     hmacParameters->hash = hashFunction;
108     return std::move(hmacParameters);
109 }
110
111 bool JSCryptoKeySerializationJWK::reconcileAlgorithm(std::unique_ptr<CryptoAlgorithm>& suggestedAlgorithm, std::unique_ptr<CryptoAlgorithmParameters>& suggestedParameters) const
112 {
113     if (!getStringFromJSON(m_exec, m_json.get(), "alg", m_jwkAlgorithmName)) {
114         // Algorithm is optional in JWK.
115         return true;
116     }
117
118     std::unique_ptr<CryptoAlgorithm> algorithm;
119     std::unique_ptr<CryptoAlgorithmParameters> parameters;
120     if (m_jwkAlgorithmName == "HS256") {
121         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
122         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_256);
123     } else if (m_jwkAlgorithmName == "HS384") {
124         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
125         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_384);
126     } else if (m_jwkAlgorithmName == "HS512") {
127         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::HMAC);
128         parameters = createHMACParameters(CryptoAlgorithmIdentifier::SHA_512);
129     } else if (m_jwkAlgorithmName == "A128CBC") {
130         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
131         parameters = std::make_unique<CryptoAlgorithmParameters>();
132     } else if (m_jwkAlgorithmName == "A192CBC") {
133         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
134         parameters = std::make_unique<CryptoAlgorithmParameters>();
135     } else if (m_jwkAlgorithmName == "A256CBC") {
136         algorithm = CryptoAlgorithmRegistry::shared().create(CryptoAlgorithmIdentifier::AES_CBC);
137         parameters = std::make_unique<CryptoAlgorithmParameters>();
138     } else {
139         throwTypeError(m_exec, "Unsupported JWK algorithm " + m_jwkAlgorithmName);
140         return false;
141     }
142
143     if (!suggestedAlgorithm) {
144         suggestedAlgorithm = std::move(algorithm);
145         suggestedParameters =  std::move(parameters);
146         return true;
147     }
148
149     if (!algorithm)
150         return true;
151
152     if (algorithm->identifier() != suggestedAlgorithm->identifier())
153         return false;
154
155     // HMAC is the only algorithm that has parameters in importKey.
156     if (algorithm->identifier() != CryptoAlgorithmIdentifier::HMAC)
157         return true;
158
159     return static_cast<CryptoAlgorithmHmacParams&>(*parameters).hash == static_cast<CryptoAlgorithmHmacParams&>(*suggestedParameters).hash;
160 }
161
162 void JSCryptoKeySerializationJWK::reconcileUsages(CryptoKeyUsage& suggestedUsage) const
163 {
164     String jwkUseString;
165     if (!getStringFromJSON(m_exec, m_json.get(), "use", jwkUseString)) {
166         // "use" is optional in JWK.
167         return;
168     }
169
170     // FIXME: CryptoKeyUsageDeriveKey, CryptoKeyUsageDeriveBits - should these be implicitly allowed by any JWK use value?
171     // FIXME: There is a mismatch between specs for wrap/unwrap usages, <http://lists.w3.org/Archives/Public/public-webcrypto/2013Nov/0016.html>.
172     if (jwkUseString == "sig")
173         suggestedUsage = suggestedUsage & (CryptoKeyUsageSign | CryptoKeyUsageVerify);
174     else if (jwkUseString == "enc")
175         suggestedUsage = suggestedUsage & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
176     else if (jwkUseString == "wrap")
177         suggestedUsage = suggestedUsage & (CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey);
178     else
179         suggestedUsage = 0; // Unknown usage, better be safe.
180 }
181
182 void JSCryptoKeySerializationJWK::reconcileExtractable(bool& suggestedExtractable) const
183 {
184     bool jwkExtractable;
185     if (!getBooleanFromJSON(m_exec, m_json.get(), "extractable", jwkExtractable)) {
186         // "extractable" is a Netflix proposal that's not in any spec yet. It will certainly be optional once specified.
187         return;
188     }
189
190     suggestedExtractable = suggestedExtractable && jwkExtractable;
191 }
192
193 bool JSCryptoKeySerializationJWK::keySizeIsValid(size_t sizeInBits) const
194 {
195     if (m_jwkAlgorithmName == "HS256")
196         return sizeInBits >= 256;
197     if (m_jwkAlgorithmName == "HS384")
198         return sizeInBits >= 384;
199     if (m_jwkAlgorithmName == "HS512")
200         return sizeInBits >= 512;
201     if (m_jwkAlgorithmName == "A128CBC")
202         return sizeInBits == 128;
203     if (m_jwkAlgorithmName == "A192CBC")
204         return sizeInBits == 192;
205     if (m_jwkAlgorithmName == "A256CBC")
206         return sizeInBits == 256;
207     return true;
208 }
209
210 std::unique_ptr<CryptoKeyData> JSCryptoKeySerializationJWK::keyData() const
211 {
212     String jwkKeyType;
213     if (!getStringFromJSON(m_exec, m_json.get(), "kty", jwkKeyType)) {
214         if (!m_exec->hadException())
215             throwTypeError(m_exec, "Required JWK \"kty\" member is missing");
216         return nullptr;
217     }
218
219     if (jwkKeyType != "oct") {
220         throwTypeError(m_exec, "Unsupported JWK key type " + jwkKeyType);
221         return nullptr;
222     }
223
224     String keyBase64URL;
225     if (!getStringFromJSON(m_exec, m_json.get(), "k", keyBase64URL)) {
226         if (!m_exec->hadException())
227             throwTypeError(m_exec, "Secret key data is not present is JWK");
228         return nullptr;
229     }
230
231     Vector<char> octetSequence;
232     if (!base64URLDecode(keyBase64URL, octetSequence)) {
233         throwTypeError(m_exec, "Cannot decode base64url key data in JWK");
234         return nullptr;
235     }
236
237     if (!keySizeIsValid(octetSequence.size() * 8)) {
238         throwTypeError(m_exec, "Key size is not valid for " + m_jwkAlgorithmName);
239         return nullptr;
240     }
241
242     return CryptoKeyDataOctetSequence::create(octetSequence);
243 }
244
245 } // namespace WebCore
246
247 #endif // ENABLE(SUBTLE_CRYPTO)