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/v8/Dictionary.h" 35 #include "core/dom/ExecutionContext.h" 36 #include "modules/crypto/CryptoResultImpl.h" 37 #include "modules/crypto/Key.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 WebCore { 46 47 // Seems like the generated bindings should take care of these however it 48 // currently doesn't. See also http://crbug.com/264520 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(blink::WebCryptoErrorTypeType, blink::WebString(message)); 54 return false; 55 } 56 return true; 57 } 58 59 static bool ensureNotNull(Key* key, const char* paramName, CryptoResult* result) 60 { 61 if (!key) { 62 String message = String("Invalid ") + paramName + String(" argument"); 63 result->completeWithError(blink::WebCryptoErrorTypeType, blink::WebString(message)); 64 return false; 65 } 66 return true; 67 } 68 69 static bool parseAlgorithm(const Dictionary& raw, blink::WebCryptoOperation op, blink::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 78 static bool canAccessWebCrypto(ScriptState* scriptState, CryptoResult* result) 79 { 80 const SecurityOrigin* origin = scriptState->executionContext()->securityOrigin(); 81 if (!origin->canAccessFeatureRequiringSecureOrigin()) { 82 result->completeWithError(blink::WebCryptoErrorTypeNotSupported, "WebCrypto is only supported over secure origins. See http://crbug.com/373032"); 83 return false; 84 } 85 86 return true; 87 } 88 89 static ScriptPromise startCryptoOperation(ScriptState* scriptState, const Dictionary& rawAlgorithm, Key* key, blink::WebCryptoOperation operationType, const ArrayPiece& signature, const ArrayPiece& dataBuffer) 90 { 91 RefPtr<CryptoResultImpl> result = CryptoResultImpl::create(scriptState); 92 ScriptPromise promise = result->promise(); 93 94 if (!canAccessWebCrypto(scriptState, result.get())) 95 return promise; 96 97 bool requiresKey = operationType != blink::WebCryptoOperationDigest; 98 99 if (requiresKey && !ensureNotNull(key, "key", result.get())) 100 return promise; 101 if (operationType == blink::WebCryptoOperationVerify && !ensureNotNull(signature, "signature", result.get())) 102 return promise; 103 if (!ensureNotNull(dataBuffer, "dataBuffer", result.get())) 104 return promise; 105 106 blink::WebCryptoAlgorithm algorithm; 107 if (!parseAlgorithm(rawAlgorithm, operationType, algorithm, result.get())) 108 return promise; 109 110 if (requiresKey && !key->canBeUsedForAlgorithm(algorithm, operationType, result.get())) 111 return promise; 112 113 const unsigned char* data = dataBuffer.bytes(); 114 unsigned dataSize = dataBuffer.byteLength(); 115 116 switch (operationType) { 117 case blink::WebCryptoOperationEncrypt: 118 blink::Platform::current()->crypto()->encrypt(algorithm, key->key(), data, dataSize, result->result()); 119 break; 120 case blink::WebCryptoOperationDecrypt: 121 blink::Platform::current()->crypto()->decrypt(algorithm, key->key(), data, dataSize, result->result()); 122 break; 123 case blink::WebCryptoOperationSign: 124 blink::Platform::current()->crypto()->sign(algorithm, key->key(), data, dataSize, result->result()); 125 break; 126 case blink::WebCryptoOperationVerify: 127 blink::Platform::current()->crypto()->verifySignature(algorithm, key->key(), signature.bytes(), signature.byteLength(), data, dataSize, result->result()); 128 break; 129 case blink::WebCryptoOperationDigest: 130 blink::Platform::current()->crypto()->digest(algorithm, data, dataSize, result->result()); 131 break; 132 default: 133 ASSERT_NOT_REACHED(); 134 return ScriptPromise(); 135 } 136 137 return promise; 138 } 139 140 static bool copyStringProperty(const char* property, const Dictionary& source, JSONObject* destination) 141 { 142 String value; 143 if (!source.get(property, value)) 144 return false; 145 destination->setString(property, value); 146 return true; 147 } 148 149 static bool copySequenceOfStringProperty(const char* property, const Dictionary& source, JSONObject* destination) 150 { 151 Vector<String> value; 152 if (!source.get(property, value)) 153 return false; 154 RefPtr<JSONArray> jsonArray = JSONArray::create(); 155 for (unsigned i = 0; i < value.size(); ++i) 156 jsonArray->pushString(value[i]); 157 destination->setArray(property, jsonArray.release()); 158 return true; 159 } 160 161 // FIXME: At the time of writing this is not a part of the spec. It is based an 162 // an unpublished editor's draft for: 163 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=24963 164 // See http://crbug.com/373917. 165 static bool copyJwkDictionaryToJson(const Dictionary& dict, CString& jsonUtf8, CryptoResult* result) 166 { 167 RefPtr<JSONObject> jsonObject = JSONObject::create(); 168 169 if (!copyStringProperty("kty", dict, jsonObject.get())) { 170 result->completeWithError(blink::WebCryptoErrorTypeData, "The required JWK property \"kty\" was missing"); 171 return false; 172 } 173 174 copyStringProperty("use", dict, jsonObject.get()); 175 copySequenceOfStringProperty("key_ops", dict, jsonObject.get()); 176 copyStringProperty("alg", dict, jsonObject.get()); 177 178 bool ext; 179 if (dict.get("ext", ext)) 180 jsonObject->setBoolean("ext", ext); 181 182 const char* const propertyNames[] = { "d", "n", "e", "p", "q", "dp", "dq", "qi", "k" }; 183 for (unsigned i = 0; i < WTF_ARRAY_LENGTH(propertyNames); ++i) 184 copyStringProperty(propertyNames[i], dict, jsonObject.get()); 185 186 String json = jsonObject->toJSONString(); 187 jsonUtf8 = json.utf8(); 188 return true; 189 } 190 191 SubtleCrypto::SubtleCrypto() 192 { 193 ScriptWrappable::init(this); 194 } 195 196 ScriptPromise SubtleCrypto::encrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, Key* key, const ArrayPiece& data) 197 { 198 return startCryptoOperation(scriptState, rawAlgorithm, key, blink::WebCryptoOperationEncrypt, ArrayPiece(), data); 199 } 200 201 ScriptPromise SubtleCrypto::decrypt(ScriptState* scriptState, const Dictionary& rawAlgorithm, Key* key, const ArrayPiece& data) 202 { 203 return startCryptoOperation(scriptState, rawAlgorithm, key, blink::WebCryptoOperationDecrypt, ArrayPiece(), data); 204 } 205 206 ScriptPromise SubtleCrypto::sign(ScriptState* scriptState, const Dictionary& rawAlgorithm, Key* key, const ArrayPiece& data) 207 { 208 return startCryptoOperation(scriptState, rawAlgorithm, key, blink::WebCryptoOperationSign, ArrayPiece(), data); 209 } 210 211 ScriptPromise SubtleCrypto::verifySignature(ScriptState* scriptState, const Dictionary& rawAlgorithm, Key* key, const ArrayPiece& signature, const ArrayPiece& data) 212 { 213 return startCryptoOperation(scriptState, rawAlgorithm, key, blink::WebCryptoOperationVerify, signature, data); 214 } 215 216 ScriptPromise SubtleCrypto::digest(ScriptState* scriptState, const Dictionary& rawAlgorithm, const ArrayPiece& data) 217 { 218 return startCryptoOperation(scriptState, rawAlgorithm, 0, blink::WebCryptoOperationDigest, ArrayPiece(), data); 219 } 220 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 blink::WebCryptoKeyUsageMask keyUsages; 230 if (!Key::parseUsageMask(rawKeyUsages, keyUsages, result.get())) 231 return promise; 232 233 blink::WebCryptoAlgorithm algorithm; 234 if (!parseAlgorithm(rawAlgorithm, blink::WebCryptoOperationGenerateKey, algorithm, result.get())) 235 return promise; 236 237 blink::Platform::current()->crypto()->generateKey(algorithm, extractable, keyUsages, result->result()); 238 return promise; 239 } 240 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 blink::WebCryptoKeyFormat format; 253 if (!Key::parseFormat(rawFormat, format, result.get())) 254 return promise; 255 256 if (format == blink::WebCryptoKeyFormatJwk) { 257 result->completeWithError(blink::WebCryptoErrorTypeData, "Key data must be an object for JWK import"); 258 return promise; 259 } 260 261 blink::WebCryptoKeyUsageMask keyUsages; 262 if (!Key::parseUsageMask(rawKeyUsages, keyUsages, result.get())) 263 return promise; 264 265 blink::WebCryptoAlgorithm algorithm; 266 if (!parseAlgorithm(rawAlgorithm, blink::WebCryptoOperationImportKey, algorithm, result.get())) 267 return promise; 268 269 blink::Platform::current()->crypto()->importKey(format, keyData.bytes(), keyData.byteLength(), algorithm, extractable, keyUsages, result->result()); 270 return promise; 271 } 272 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 blink::WebCryptoKeyFormat format; 282 if (!Key::parseFormat(rawFormat, format, result.get())) 283 return promise; 284 285 blink::WebCryptoKeyUsageMask keyUsages; 286 if (!Key::parseUsageMask(rawKeyUsages, keyUsages, result.get())) 287 return promise; 288 289 if (format != blink::WebCryptoKeyFormatJwk) { 290 result->completeWithError(blink::WebCryptoErrorTypeData, "Key data must be a buffer for non-JWK formats"); 291 return promise; 292 } 293 294 blink::WebCryptoAlgorithm algorithm; 295 if (!parseAlgorithm(rawAlgorithm, blink::WebCryptoOperationImportKey, algorithm, result.get())) 296 return promise; 297 298 CString jsonUtf8; 299 if (!copyJwkDictionaryToJson(keyData, jsonUtf8, result.get())) 300 return promise; 301 302 blink::Platform::current()->crypto()->importKey(format, reinterpret_cast<const unsigned char*>(jsonUtf8.data()), jsonUtf8.length(), algorithm, extractable, keyUsages, result->result()); 303 return promise; 304 } 305 306 ScriptPromise SubtleCrypto::exportKey(ScriptState* scriptState, const String& rawFormat, Key* 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 blink::WebCryptoKeyFormat format; 318 if (!Key::parseFormat(rawFormat, format, result.get())) 319 return promise; 320 321 if (!key->extractable()) { 322 result->completeWithError(blink::WebCryptoErrorTypeInvalidAccess, "key is not extractable"); 323 return promise; 324 } 325 326 blink::Platform::current()->crypto()->exportKey(format, key->key(), result->result()); 327 return promise; 328 } 329 330 ScriptPromise SubtleCrypto::wrapKey(ScriptState* scriptState, const String& rawFormat, Key* key, Key* 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 blink::WebCryptoKeyFormat format; 345 if (!Key::parseFormat(rawFormat, format, result.get())) 346 return promise; 347 348 blink::WebCryptoAlgorithm wrapAlgorithm; 349 if (!parseAlgorithm(rawWrapAlgorithm, blink::WebCryptoOperationWrapKey, wrapAlgorithm, result.get())) 350 return promise; 351 352 if (!key->extractable()) { 353 result->completeWithError(blink::WebCryptoErrorTypeInvalidAccess, "key is not extractable"); 354 return promise; 355 } 356 357 if (!wrappingKey->canBeUsedForAlgorithm(wrapAlgorithm, blink::WebCryptoOperationWrapKey, result.get())) 358 return promise; 359 360 blink::Platform::current()->crypto()->wrapKey(format, key->key(), wrappingKey->key(), wrapAlgorithm, result->result()); 361 return promise; 362 } 363 364 ScriptPromise SubtleCrypto::unwrapKey(ScriptState* scriptState, const String& rawFormat, const ArrayPiece& wrappedKey, Key* 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 blink::WebCryptoKeyFormat format; 378 if (!Key::parseFormat(rawFormat, format, result.get())) 379 return promise; 380 381 blink::WebCryptoKeyUsageMask keyUsages; 382 if (!Key::parseUsageMask(rawKeyUsages, keyUsages, result.get())) 383 return promise; 384 385 blink::WebCryptoAlgorithm unwrapAlgorithm; 386 if (!parseAlgorithm(rawUnwrapAlgorithm, blink::WebCryptoOperationUnwrapKey, unwrapAlgorithm, result.get())) 387 return promise; 388 389 blink::WebCryptoAlgorithm unwrappedKeyAlgorithm; 390 if (!parseAlgorithm(rawUnwrappedKeyAlgorithm, blink::WebCryptoOperationImportKey, unwrappedKeyAlgorithm, result.get())) 391 return promise; 392 393 if (!unwrappingKey->canBeUsedForAlgorithm(unwrapAlgorithm, blink::WebCryptoOperationUnwrapKey, result.get())) 394 return promise; 395 396 blink::Platform::current()->crypto()->unwrapKey(format, wrappedKey.bytes(), wrappedKey.byteLength(), unwrappingKey->key(), unwrapAlgorithm, unwrappedKeyAlgorithm, extractable, keyUsages, result->result()); 397 return promise; 398 } 399 400 } // namespace WebCore 401