• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "modules/crypto/SubtleCrypto.h"
33 
34 #include "bindings/core/v8/Dictionary.h"
35 #include "core/dom/ExecutionContext.h"
36 #include "modules/crypto/CryptoKey.h"
37 #include "modules/crypto/CryptoResultImpl.h"
38 #include "modules/crypto/NormalizeAlgorithm.h"
39 #include "platform/JSONValues.h"
40 #include "public/platform/Platform.h"
41 #include "public/platform/WebCrypto.h"
42 #include "public/platform/WebCryptoAlgorithm.h"
43 #include "wtf/ArrayBufferView.h"
44 
45 namespace blink {
46 
47 // Seems like the generated bindings should take care of these however it
48 // currently doesn't. See also http://crbug.com/264520
ensureNotNull(const ArrayPiece & x,const char * paramName,CryptoResult * result)49 static bool ensureNotNull(const ArrayPiece& x, const char* paramName, CryptoResult* result)
50 {
51     if (x.isNull()) {
52         String message = String("Invalid ") + paramName + String(" argument");
53         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
54         return false;
55     }
56     return true;
57 }
58 
ensureNotNull(CryptoKey * key,const char * paramName,CryptoResult * result)59 static bool ensureNotNull(CryptoKey* key, const char* paramName, CryptoResult* result)
60 {
61     if (!key) {
62         String message = String("Invalid ") + paramName + String(" argument");
63         result->completeWithError(WebCryptoErrorTypeType, WebString(message));
64         return false;
65     }
66     return true;
67 }
68 
parseAlgorithm(const Dictionary & raw,WebCryptoOperation op,WebCryptoAlgorithm & algorithm,CryptoResult * result)69 static bool parseAlgorithm(const Dictionary& raw, WebCryptoOperation op, WebCryptoAlgorithm& algorithm, CryptoResult* result)
70 {
71     AlgorithmError error;
72     bool success = normalizeAlgorithm(raw, op, algorithm, &error);
73     if (!success)
74         result->completeWithError(error.errorType, error.errorDetails);
75     return success;
76 }
77 
canAccessWebCrypto(ScriptState * scriptState,CryptoResult * result)78 static bool canAccessWebCrypto(ScriptState* scriptState, CryptoResult* result)
79 {
80     const SecurityOrigin* origin = scriptState->executionContext()->securityOrigin();
81     String errorMessage;
82     if (!origin->canAccessFeatureRequiringSecureOrigin(errorMessage)) {
83         result->completeWithError(WebCryptoErrorTypeNotSupported, errorMessage);
84         return false;
85     }
86 
87     return true;
88 }
89 
startCryptoOperation(ScriptState * scriptState,const Dictionary & rawAlgorithm,CryptoKey * key,WebCryptoOperation operationType,const ArrayPiece & signature,const ArrayPiece & dataBuffer)90 static ScriptPromise startCryptoOperation(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, WebCryptoOperation operationType, const ArrayPiece& signature, const ArrayPiece& dataBuffer)
91 {
92     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
93     ScriptPromise promise = result->promise();
94 
95     if (!canAccessWebCrypto(scriptState, result.get()))
96         return promise;
97 
98     bool requiresKey = operationType != WebCryptoOperationDigest;
99 
100     if (requiresKey && !ensureNotNull(key, "key", result.get()))
101         return promise;
102     if (operationType == WebCryptoOperationVerify && !ensureNotNull(signature, "signature", result.get()))
103         return promise;
104     if (!ensureNotNull(dataBuffer, "dataBuffer", result.get()))
105         return promise;
106 
107     WebCryptoAlgorithm algorithm;
108     if (!parseAlgorithm(rawAlgorithm, operationType, algorithm, result.get()))
109         return promise;
110 
111     if (requiresKey && !key->canBeUsedForAlgorithm(algorithm, operationType, result.get()))
112         return promise;
113 
114     const unsigned char* data = dataBuffer.bytes();
115     unsigned dataSize = dataBuffer.byteLength();
116 
117     switch (operationType) {
118     case WebCryptoOperationEncrypt:
119         Platform::current()->crypto()->encrypt(algorithm, key->key(), data, dataSize, result->result());
120         break;
121     case WebCryptoOperationDecrypt:
122         Platform::current()->crypto()->decrypt(algorithm, key->key(), data, dataSize, result->result());
123         break;
124     case WebCryptoOperationSign:
125         Platform::current()->crypto()->sign(algorithm, key->key(), data, dataSize, result->result());
126         break;
127     case WebCryptoOperationVerify:
128         Platform::current()->crypto()->verifySignature(algorithm, key->key(), signature.bytes(), signature.byteLength(), data, dataSize, result->result());
129         break;
130     case WebCryptoOperationDigest:
131         Platform::current()->crypto()->digest(algorithm, data, dataSize, result->result());
132         break;
133     default:
134         ASSERT_NOT_REACHED();
135         return ScriptPromise();
136     }
137 
138     return promise;
139 }
140 
copyStringProperty(const char * property,const Dictionary & source,JSONObject * destination)141 static bool copyStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
142 {
143     String value;
144     if (!DictionaryHelper::get(source, property, value))
145         return false;
146     destination->setString(property, value);
147     return true;
148 }
149 
copySequenceOfStringProperty(const char * property,const Dictionary & source,JSONObject * destination)150 static bool copySequenceOfStringProperty(const char* property, const Dictionary& source, JSONObject* destination)
151 {
152     Vector<String> value;
153     if (!DictionaryHelper::get(source, property, value))
154         return false;
155     RefPtr<JSONArray> jsonArray = JSONArray::create();
156     for (unsigned i = 0; i < value.size(); ++i)
157         jsonArray->pushString(value[i]);
158     destination->setArray(property, jsonArray.release());
159     return true;
160 }
161 
162 // FIXME: At the time of writing this is not a part of the spec. It is based an
163 // an unpublished editor's draft for:
164 //   https://www.w3.org/Bugs/Public/show_bug.cgi?id=24963
165 // See http://crbug.com/373917.
copyJwkDictionaryToJson(const Dictionary & dict,CString & jsonUtf8,CryptoResult * result)166 static bool copyJwkDictionaryToJson(const Dictionary& dict, CString& jsonUtf8, CryptoResult* result)
167 {
168     RefPtr<JSONObject> jsonObject = JSONObject::create();
169 
170     if (!copyStringProperty("kty", dict, jsonObject.get())) {
171         result->completeWithError(WebCryptoErrorTypeData, "The required JWK property \"kty\" was missing");
172         return false;
173     }
174 
175     copyStringProperty("use", dict, jsonObject.get());
176     copySequenceOfStringProperty("key_ops", dict, jsonObject.get());
177     copyStringProperty("alg", dict, jsonObject.get());
178 
179     bool ext;
180     if (DictionaryHelper::get(dict, "ext", ext))
181         jsonObject->setBoolean("ext", ext);
182 
183     const char* const propertyNames[] = { "d", "n", "e", "p", "q", "dp", "dq", "qi", "k" };
184     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(propertyNames); ++i)
185         copyStringProperty(propertyNames[i], dict, jsonObject.get());
186 
187     String json = jsonObject->toJSONString();
188     jsonUtf8 = json.utf8();
189     return true;
190 }
191 
SubtleCrypto()192 SubtleCrypto::SubtleCrypto()
193 {
194 }
195 
encrypt(ScriptState * scriptState,const Dictionary & rawAlgorithm,CryptoKey * key,const ArrayPiece & data)196 ScriptPromise SubtleCrypto::encrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
197 {
198     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationEncrypt, ArrayPiece(), data);
199 }
200 
decrypt(ScriptState * scriptState,const Dictionary & rawAlgorithm,CryptoKey * key,const ArrayPiece & data)201 ScriptPromise SubtleCrypto::decrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
202 {
203     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationDecrypt, ArrayPiece(), data);
204 }
205 
sign(ScriptState * scriptState,const Dictionary & rawAlgorithm,CryptoKey * key,const ArrayPiece & data)206 ScriptPromise SubtleCrypto::sign(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& data)
207 {
208     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationSign, ArrayPiece(), data);
209 }
210 
verifySignature(ScriptState * scriptState,const Dictionary & rawAlgorithm,CryptoKey * key,const ArrayPiece & signature,const ArrayPiece & data)211 ScriptPromise SubtleCrypto::verifySignature(ScriptState* scriptState, const Dictionary& rawAlgorithm, CryptoKey* key, const ArrayPiece& signature, const ArrayPiece& data)
212 {
213     return startCryptoOperation(scriptState, rawAlgorithm, key, WebCryptoOperationVerify, signature, data);
214 }
215 
digest(ScriptState * scriptState,const Dictionary & rawAlgorithm,const ArrayPiece & data)216 ScriptPromise SubtleCrypto::digest(ScriptState* scriptState, const Dictionary& rawAlgorithm, const ArrayPiece& data)
217 {
218     return startCryptoOperation(scriptState, rawAlgorithm, 0, WebCryptoOperationDigest, ArrayPiece(), data);
219 }
220 
generateKey(ScriptState * scriptState,const Dictionary & rawAlgorithm,bool extractable,const Vector<String> & rawKeyUsages)221 ScriptPromise SubtleCrypto::generateKey(ScriptState* scriptState, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
222 {
223     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
224     ScriptPromise promise = result->promise();
225 
226     if (!canAccessWebCrypto(scriptState, result.get()))
227         return promise;
228 
229     WebCryptoKeyUsageMask keyUsages;
230     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
231         return promise;
232 
233     WebCryptoAlgorithm algorithm;
234     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationGenerateKey, algorithm, result.get()))
235         return promise;
236 
237     Platform::current()->crypto()->generateKey(algorithm, extractable, keyUsages, result->result());
238     return promise;
239 }
240 
importKey(ScriptState * scriptState,const String & rawFormat,const ArrayPiece & keyData,const Dictionary & rawAlgorithm,bool extractable,const Vector<String> & rawKeyUsages)241 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const ArrayPiece& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
242 {
243     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
244     ScriptPromise promise = result->promise();
245 
246     if (!canAccessWebCrypto(scriptState, result.get()))
247         return promise;
248 
249     if (!ensureNotNull(keyData, "keyData", result.get()))
250         return promise;
251 
252     WebCryptoKeyFormat format;
253     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
254         return promise;
255 
256     if (format == WebCryptoKeyFormatJwk) {
257         result->completeWithError(WebCryptoErrorTypeData, "Key data must be an object for JWK import");
258         return promise;
259     }
260 
261     WebCryptoKeyUsageMask keyUsages;
262     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
263         return promise;
264 
265     WebCryptoAlgorithm algorithm;
266     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
267         return promise;
268 
269     Platform::current()->crypto()->importKey(format, keyData.bytes(), keyData.byteLength(), algorithm, extractable, keyUsages, result->result());
270     return promise;
271 }
272 
importKey(ScriptState * scriptState,const String & rawFormat,const Dictionary & keyData,const Dictionary & rawAlgorithm,bool extractable,const Vector<String> & rawKeyUsages)273 ScriptPromise SubtleCrypto::importKey(ScriptState* scriptState, const String& rawFormat, const Dictionary& keyData, const Dictionary& rawAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
274 {
275     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
276     ScriptPromise promise = result->promise();
277 
278     if (!canAccessWebCrypto(scriptState, result.get()))
279         return promise;
280 
281     WebCryptoKeyFormat format;
282     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
283         return promise;
284 
285     WebCryptoKeyUsageMask keyUsages;
286     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
287         return promise;
288 
289     if (format != WebCryptoKeyFormatJwk) {
290         result->completeWithError(WebCryptoErrorTypeData, "Key data must be a buffer for non-JWK formats");
291         return promise;
292     }
293 
294     WebCryptoAlgorithm algorithm;
295     if (!parseAlgorithm(rawAlgorithm, WebCryptoOperationImportKey, algorithm, result.get()))
296         return promise;
297 
298     CString jsonUtf8;
299     if (!copyJwkDictionaryToJson(keyData, jsonUtf8, result.get()))
300         return promise;
301 
302     Platform::current()->crypto()->importKey(format, reinterpret_cast<const unsigned char*>(jsonUtf8.data()), jsonUtf8.length(), algorithm, extractable, keyUsages, result->result());
303     return promise;
304 }
305 
exportKey(ScriptState * scriptState,const String & rawFormat,CryptoKey * key)306 ScriptPromise SubtleCrypto::exportKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key)
307 {
308     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
309     ScriptPromise promise = result->promise();
310 
311     if (!canAccessWebCrypto(scriptState, result.get()))
312         return promise;
313 
314     if (!ensureNotNull(key, "key", result.get()))
315         return promise;
316 
317     WebCryptoKeyFormat format;
318     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
319         return promise;
320 
321     if (!key->extractable()) {
322         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
323         return promise;
324     }
325 
326     Platform::current()->crypto()->exportKey(format, key->key(), result->result());
327     return promise;
328 }
329 
wrapKey(ScriptState * scriptState,const String & rawFormat,CryptoKey * key,CryptoKey * wrappingKey,const Dictionary & rawWrapAlgorithm)330 ScriptPromise SubtleCrypto::wrapKey(ScriptState* scriptState, const String& rawFormat, CryptoKey* key, CryptoKey* wrappingKey, const Dictionary& rawWrapAlgorithm)
331 {
332     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
333     ScriptPromise promise = result->promise();
334 
335     if (!canAccessWebCrypto(scriptState, result.get()))
336         return promise;
337 
338     if (!ensureNotNull(key, "key", result.get()))
339         return promise;
340 
341     if (!ensureNotNull(wrappingKey, "wrappingKey", result.get()))
342         return promise;
343 
344     WebCryptoKeyFormat format;
345     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
346         return promise;
347 
348     WebCryptoAlgorithm wrapAlgorithm;
349     if (!parseAlgorithm(rawWrapAlgorithm, WebCryptoOperationWrapKey, wrapAlgorithm, result.get()))
350         return promise;
351 
352     if (!key->extractable()) {
353         result->completeWithError(WebCryptoErrorTypeInvalidAccess, "key is not extractable");
354         return promise;
355     }
356 
357     if (!wrappingKey->canBeUsedForAlgorithm(wrapAlgorithm, WebCryptoOperationWrapKey, result.get()))
358         return promise;
359 
360     Platform::current()->crypto()->wrapKey(format, key->key(), wrappingKey->key(), wrapAlgorithm, result->result());
361     return promise;
362 }
363 
unwrapKey(ScriptState * scriptState,const String & rawFormat,const ArrayPiece & wrappedKey,CryptoKey * unwrappingKey,const Dictionary & rawUnwrapAlgorithm,const Dictionary & rawUnwrappedKeyAlgorithm,bool extractable,const Vector<String> & rawKeyUsages)364 ScriptPromise SubtleCrypto::unwrapKey(ScriptState* scriptState, const String& rawFormat, const ArrayPiece& wrappedKey, CryptoKey* unwrappingKey, const Dictionary& rawUnwrapAlgorithm, const Dictionary& rawUnwrappedKeyAlgorithm, bool extractable, const Vector<String>& rawKeyUsages)
365 {
366     RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState);
367     ScriptPromise promise = result->promise();
368 
369     if (!canAccessWebCrypto(scriptState, result.get()))
370         return promise;
371 
372     if (!ensureNotNull(wrappedKey, "wrappedKey", result.get()))
373         return promise;
374     if (!ensureNotNull(unwrappingKey, "unwrappingKey", result.get()))
375         return promise;
376 
377     WebCryptoKeyFormat format;
378     if (!CryptoKey::parseFormat(rawFormat, format, result.get()))
379         return promise;
380 
381     WebCryptoKeyUsageMask keyUsages;
382     if (!CryptoKey::parseUsageMask(rawKeyUsages, keyUsages, result.get()))
383         return promise;
384 
385     WebCryptoAlgorithm unwrapAlgorithm;
386     if (!parseAlgorithm(rawUnwrapAlgorithm, WebCryptoOperationUnwrapKey, unwrapAlgorithm, result.get()))
387         return promise;
388 
389     WebCryptoAlgorithm unwrappedKeyAlgorithm;
390     if (!parseAlgorithm(rawUnwrappedKeyAlgorithm, WebCryptoOperationImportKey, unwrappedKeyAlgorithm, result.get()))
391         return promise;
392 
393     if (!unwrappingKey->canBeUsedForAlgorithm(unwrapAlgorithm, WebCryptoOperationUnwrapKey, result.get()))
394         return promise;
395 
396     Platform::current()->crypto()->unwrapKey(format, wrappedKey.bytes(), wrappedKey.byteLength(), unwrappingKey->key(), unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages, result->result());
397     return promise;
398 }
399 
400 } // namespace blink
401