blob: 19a780048c09bc6f16121e55dd54349c92b9cbbf [file] [log] [blame]
/*
* Copyright (C) 2013 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "JSSubtleCrypto.h"
#if ENABLE(SUBTLE_CRYPTO)
#include "CryptoAlgorithm.h"
#include "CryptoAlgorithmParameters.h"
#include "CryptoAlgorithmRegistry.h"
#include "CryptoKeyData.h"
#include "CryptoKeySerializationRaw.h"
#include "Document.h"
#include "ExceptionCode.h"
#include "JSCryptoAlgorithmDictionary.h"
#include "JSCryptoKeySerializationJWK.h"
#include "JSCryptoOperationData.h"
#include "JSDOMPromise.h"
#include <runtime/Error.h>
using namespace JSC;
namespace WebCore {
enum class CryptoKeyFormat {
// An unformatted sequence of bytes. Intended for secret keys.
Raw,
// The DER encoding of the PrivateKeyInfo structure from RFC 5208.
PKCS8,
// The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
SPKI,
// The key is represented as JSON according to the JSON Web Key format.
JWK
};
static std::unique_ptr<CryptoAlgorithm> createAlgorithmFromJSValue(ExecState* exec, JSValue value)
{
CryptoAlgorithmIdentifier algorithmIdentifier;
if (!JSCryptoAlgorithmDictionary::getAlgorithmIdentifier(exec, value, algorithmIdentifier)) {
ASSERT(exec->hadException());
return nullptr;
}
auto result = CryptoAlgorithmRegistry::shared().create(algorithmIdentifier);
if (!result)
setDOMException(exec, NOT_SUPPORTED_ERR);
return result;
}
static bool cryptoKeyFormatFromJSValue(ExecState* exec, JSValue value, CryptoKeyFormat& result)
{
String keyFormatString = value.toString(exec)->value(exec);
if (exec->hadException())
return false;
if (keyFormatString == "raw")
result = CryptoKeyFormat::Raw;
else if (keyFormatString == "pkcs8")
result = CryptoKeyFormat::PKCS8;
else if (keyFormatString == "spki")
result = CryptoKeyFormat::SPKI;
else if (keyFormatString == "jwk")
result = CryptoKeyFormat::JWK;
else {
throwTypeError(exec, "Unknown key format");
return false;
}
return true;
}
static bool cryptoKeyUsagesFromJSValue(ExecState* exec, JSValue value, CryptoKeyUsage& result)
{
if (!isJSArray(value)) {
throwTypeError(exec);
return false;
}
result = 0;
JSArray* array = asArray(value);
for (size_t i = 0; i < array->length(); ++i) {
JSValue element = array->getIndex(exec, i);
String usageString = element.toString(exec)->value(exec);
if (exec->hadException())
return false;
if (usageString == "encrypt")
result |= CryptoKeyUsageEncrypt;
else if (usageString == "decrypt")
result |= CryptoKeyUsageDecrypt;
else if (usageString == "sign")
result |= CryptoKeyUsageSign;
else if (usageString == "verify")
result |= CryptoKeyUsageVerify;
else if (usageString == "deriveKey")
result |= CryptoKeyUsageDeriveKey;
else if (usageString == "deriveBits")
result |= CryptoKeyUsageDeriveBits;
else if (usageString == "wrapKey")
result |= CryptoKeyUsageWrapKey;
else if (usageString == "unwrapKey")
result |= CryptoKeyUsageUnwrapKey;
}
return true;
}
JSValue JSSubtleCrypto::encrypt(ExecState* exec)
{
if (exec->argumentCount() < 3)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
if (!key->allows(CryptoKeyUsageEncrypt)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'encrypt'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->encrypt(*parameters, *key, data, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::decrypt(ExecState* exec)
{
if (exec->argumentCount() < 3)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
if (!key->allows(CryptoKeyUsageDecrypt)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'decrypt'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->decrypt(*parameters, *key, data, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::sign(ExecState* exec)
{
if (exec->argumentCount() < 3)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForSign(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
if (!key->allows(CryptoKeyUsageSign)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'sign'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->sign(*parameters, *key, data, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::verify(ExecState* exec)
{
if (exec->argumentCount() < 4)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForVerify(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
if (!key->allows(CryptoKeyUsageVerify)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'verify'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
CryptoOperationData signature;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(2), signature)) {
ASSERT(exec->hadException());
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(3), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](bool result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->verify(*parameters, *key, signature, data, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::digest(ExecState* exec)
{
if (exec->argumentCount() < 2)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForDigest(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->digest(*parameters, data, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::generateKey(ExecState* exec)
{
if (exec->argumentCount() < 1)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(0));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForGenerateKey(exec, algorithm->identifier(), exec->uncheckedArgument(0));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
bool extractable = false;
if (exec->argumentCount() >= 2) {
extractable = exec->uncheckedArgument(1).toBoolean(exec);
if (exec->hadException())
return jsUndefined();
}
CryptoKeyUsage keyUsages = 0;
if (exec->argumentCount() >= 3) {
if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(2), keyUsages)) {
ASSERT(exec->hadException());
return jsUndefined();
}
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](CryptoKey* key, CryptoKeyPair* keyPair) mutable {
ASSERT(key || keyPair);
ASSERT(!key || !keyPair);
if (key)
wrapper.resolve(key);
else
wrapper.resolve(keyPair);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithm->generateKey(*parameters, extractable, keyUsages, std::move(successCallback), std::move(failureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
static void importKey(ExecState* exec, CryptoKeyFormat keyFormat, CryptoOperationData data, CryptoAlgorithm* algorithmPtr, CryptoAlgorithmParameters* parametersPtr, bool extractable, CryptoKeyUsage keyUsages, CryptoAlgorithm::KeyCallback callback, CryptoAlgorithm::VoidCallback failureCallback)
{
std::unique_ptr<CryptoAlgorithm> algorithm(algorithmPtr);
std::unique_ptr<CryptoAlgorithmParameters> parameters(parametersPtr);
std::unique_ptr<CryptoKeySerialization> keySerialization;
switch (keyFormat) {
case CryptoKeyFormat::Raw:
keySerialization = CryptoKeySerializationRaw::create(data);
break;
case CryptoKeyFormat::JWK: {
String jwkString = String::fromUTF8(data.first, data.second);
if (jwkString.isNull()) {
throwTypeError(exec, "JWK JSON serialization is not valid UTF-8");
return;
}
keySerialization = JSCryptoKeySerializationJWK::create(exec, jwkString);
if (exec->hadException())
return;
break;
}
default:
throwTypeError(exec, "Unsupported key format for import");
return;
}
ASSERT(keySerialization);
if (!keySerialization->reconcileAlgorithm(algorithm, parameters)) {
if (!exec->hadException())
throwTypeError(exec, "Algorithm specified in key is not compatible with one passed to importKey as argument");
return;
}
if (exec->hadException())
return;
if (!algorithm) {
throwTypeError(exec, "Neither key nor function argument has crypto algorithm specified");
return;
}
ASSERT(parameters);
keySerialization->reconcileExtractable(extractable);
if (exec->hadException())
return;
keySerialization->reconcileUsages(keyUsages);
if (exec->hadException())
return;
auto keyData = keySerialization->keyData();
if (exec->hadException())
return;
ExceptionCode ec = 0;
algorithm->importKey(*parameters, *keyData, extractable, keyUsages, std::move(callback), std::move(failureCallback), ec);
if (ec)
setDOMException(exec, ec);
}
JSValue JSSubtleCrypto::importKey(ExecState* exec)
{
if (exec->argumentCount() < 3)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
CryptoKeyFormat keyFormat;
if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
ASSERT(exec->hadException());
return jsUndefined();
}
CryptoOperationData data;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), data)) {
ASSERT(exec->hadException());
return jsUndefined();
}
std::unique_ptr<CryptoAlgorithm> algorithm;
std::unique_ptr<CryptoAlgorithmParameters> parameters;
if (!exec->uncheckedArgument(2).isNull()) {
algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(2));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
parameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(exec, algorithm->identifier(), exec->uncheckedArgument(2));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
}
bool extractable = false;
if (exec->argumentCount() >= 4) {
extractable = exec->uncheckedArgument(3).toBoolean(exec);
if (exec->hadException())
return jsUndefined();
}
CryptoKeyUsage keyUsages = 0;
if (exec->argumentCount() >= 5) {
if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(4), keyUsages)) {
ASSERT(exec->hadException());
return jsUndefined();
}
}
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](CryptoKey& result) mutable {
wrapper.resolve(&result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
WebCore::importKey(exec, keyFormat, data, algorithm.release(), parameters.release(), extractable, keyUsages, std::move(successCallback), std::move(failureCallback));
if (exec->hadException())
return jsUndefined();
return wrapper.promise();
}
static void exportKey(ExecState* exec, CryptoKeyFormat keyFormat, const CryptoKey& key, CryptoAlgorithm::VectorCallback callback, CryptoAlgorithm::VoidCallback failureCallback)
{
if (!key.extractable()) {
throwTypeError(exec, "Key is not extractable");
return;
}
switch (keyFormat) {
case CryptoKeyFormat::Raw: {
Vector<uint8_t> result;
if (CryptoKeySerializationRaw::serialize(key, result))
callback(result);
else
failureCallback();
break;
}
case CryptoKeyFormat::JWK: {
String result = JSCryptoKeySerializationJWK::serialize(exec, key);
if (exec->hadException())
return;
CString utf8String = result.utf8(StrictConversion);
Vector<uint8_t> resultBuffer;
resultBuffer.append(utf8String.data(), utf8String.length());
callback(resultBuffer);
break;
}
default:
throwTypeError(exec, "Unsupported key format for export");
break;
}
}
JSValue JSSubtleCrypto::exportKey(ExecState* exec)
{
if (exec->argumentCount() < 2)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
CryptoKeyFormat keyFormat;
if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
DeferredWrapper wrapper(exec, globalObject());
auto successCallback = [wrapper](const Vector<uint8_t>& result) mutable {
wrapper.resolve(result);
};
auto failureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
WebCore::exportKey(exec, keyFormat, *key, std::move(successCallback), std::move(failureCallback));
if (exec->hadException())
return jsUndefined();
return wrapper.promise();
}
JSValue JSSubtleCrypto::wrapKey(ExecState* exec)
{
if (exec->argumentCount() < 4)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
CryptoKeyFormat keyFormat;
if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> key = toCryptoKey(exec->uncheckedArgument(1));
if (!key)
return throwTypeError(exec);
RefPtr<CryptoKey> wrappingKey = toCryptoKey(exec->uncheckedArgument(2));
if (!key)
return throwTypeError(exec);
if (!wrappingKey->allows(CryptoKeyUsageWrapKey)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'wrapKey'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
auto algorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(3));
if (!algorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
auto parameters = JSCryptoAlgorithmDictionary::createParametersForEncrypt(exec, algorithm->identifier(), exec->uncheckedArgument(3));
if (!parameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
DeferredWrapper wrapper(exec, globalObject());
CryptoAlgorithm* algorithmPtr = algorithm.release();
CryptoAlgorithmParameters* parametersPtr = parameters.release();
auto exportSuccessCallback = [keyFormat, algorithmPtr, parametersPtr, wrappingKey, wrapper](const Vector<uint8_t>& exportedKeyData) mutable {
auto encryptSuccessCallback = [wrapper, algorithmPtr, parametersPtr](const Vector<uint8_t>& encryptedData) mutable {
delete algorithmPtr;
delete parametersPtr;
wrapper.resolve(encryptedData);
};
auto encryptFailureCallback = [wrapper, algorithmPtr, parametersPtr]() mutable {
delete algorithmPtr;
delete parametersPtr;
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
algorithmPtr->encryptForWrapKey(*parametersPtr, *wrappingKey, std::make_pair(exportedKeyData.data(), exportedKeyData.size()), std::move(encryptSuccessCallback), std::move(encryptFailureCallback), ec);
if (ec) {
// FIXME: Report failure details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
encryptFailureCallback();
}
};
auto exportFailureCallback = [wrapper, algorithmPtr, parametersPtr]() mutable {
delete algorithmPtr;
delete parametersPtr;
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
WebCore::exportKey(exec, keyFormat, *key, std::move(exportSuccessCallback), std::move(exportFailureCallback));
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
JSValue JSSubtleCrypto::unwrapKey(ExecState* exec)
{
if (exec->argumentCount() < 5)
return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
CryptoKeyFormat keyFormat;
if (!cryptoKeyFormatFromJSValue(exec, exec->argument(0), keyFormat)) {
ASSERT(exec->hadException());
return jsUndefined();
}
CryptoOperationData wrappedKeyData;
if (!cryptoOperationDataFromJSValue(exec, exec->uncheckedArgument(1), wrappedKeyData)) {
ASSERT(exec->hadException());
return jsUndefined();
}
RefPtr<CryptoKey> unwrappingKey = toCryptoKey(exec->uncheckedArgument(2));
if (!unwrappingKey)
return throwTypeError(exec);
if (!unwrappingKey->allows(CryptoKeyUsageUnwrapKey)) {
m_impl->document()->addConsoleMessage(JSMessageSource, ErrorMessageLevel, "Key usages do not include 'unwrapKey'");
setDOMException(exec, NOT_SUPPORTED_ERR);
return jsUndefined();
}
std::unique_ptr<CryptoAlgorithm> unwrapAlgorithm;
std::unique_ptr<CryptoAlgorithmParameters> unwrapAlgorithmParameters;
unwrapAlgorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(3));
if (!unwrapAlgorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
unwrapAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForDecrypt(exec, unwrapAlgorithm->identifier(), exec->uncheckedArgument(3));
if (!unwrapAlgorithmParameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
std::unique_ptr<CryptoAlgorithm> unwrappedKeyAlgorithm;
std::unique_ptr<CryptoAlgorithmParameters> unwrappedKeyAlgorithmParameters;
if (!exec->uncheckedArgument(4).isNull()) {
unwrappedKeyAlgorithm = createAlgorithmFromJSValue(exec, exec->uncheckedArgument(4));
if (!unwrappedKeyAlgorithm) {
ASSERT(exec->hadException());
return jsUndefined();
}
unwrappedKeyAlgorithmParameters = JSCryptoAlgorithmDictionary::createParametersForImportKey(exec, unwrappedKeyAlgorithm->identifier(), exec->uncheckedArgument(4));
if (!unwrappedKeyAlgorithmParameters) {
ASSERT(exec->hadException());
return jsUndefined();
}
}
bool extractable = false;
if (exec->argumentCount() >= 6) {
extractable = exec->uncheckedArgument(5).toBoolean(exec);
if (exec->hadException())
return jsUndefined();
}
CryptoKeyUsage keyUsages = 0;
if (exec->argumentCount() >= 7) {
if (!cryptoKeyUsagesFromJSValue(exec, exec->argument(6), keyUsages)) {
ASSERT(exec->hadException());
return jsUndefined();
}
}
DeferredWrapper wrapper(exec, globalObject());
Strong<JSDOMGlobalObject> domGlobalObject(exec->vm(), globalObject());
CryptoAlgorithm* unwrappedKeyAlgorithmPtr = unwrappedKeyAlgorithm.release();
CryptoAlgorithmParameters* unwrappedKeyAlgorithmParametersPtr = unwrappedKeyAlgorithmParameters.release();
auto decryptSuccessCallback = [domGlobalObject, keyFormat, unwrappedKeyAlgorithmPtr, unwrappedKeyAlgorithmParametersPtr, extractable, keyUsages, wrapper](const Vector<uint8_t>& result) mutable {
auto importSuccessCallback = [wrapper](CryptoKey& key) mutable {
wrapper.resolve(&key);
};
auto importFailureCallback = [wrapper]() mutable {
wrapper.reject(nullptr);
};
ExecState* exec = domGlobalObject->globalExec();
WebCore::importKey(exec, keyFormat, std::make_pair(result.data(), result.size()), unwrappedKeyAlgorithmPtr, unwrappedKeyAlgorithmParametersPtr, extractable, keyUsages, std::move(importSuccessCallback), std::move(importFailureCallback));
if (exec->hadException()) {
// FIXME: Report exception details to console, and possibly to calling script once there is a standardized way to pass errors to WebCrypto promise reject functions.
exec->clearException();
importFailureCallback();
}
};
auto decryptFailureCallback = [wrapper, unwrappedKeyAlgorithmPtr, unwrappedKeyAlgorithmParametersPtr]() mutable {
delete unwrappedKeyAlgorithmPtr;
delete unwrappedKeyAlgorithmParametersPtr;
wrapper.reject(nullptr);
};
ExceptionCode ec = 0;
unwrapAlgorithm->decryptForUnwrapKey(*unwrapAlgorithmParameters, *unwrappingKey, wrappedKeyData, std::move(decryptSuccessCallback), std::move(decryptFailureCallback), ec);
if (ec) {
setDOMException(exec, ec);
return jsUndefined();
}
return wrapper.promise();
}
} // namespace WebCore
#endif