1/*
2 * Copyright (C) 2017 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 "CryptoAlgorithmECDSA.h"
28
29#if ENABLE(WEB_CRYPTO)
30
31#include "CryptoAlgorithmEcKeyParams.h"
32#include "CryptoAlgorithmEcdsaParams.h"
33#include "CryptoKeyEC.h"
34#include <JavaScriptCore/JSCJSValueInlines.h>
35#include <wtf/CrossThreadCopier.h>
36
37namespace WebCore {
38
39namespace CryptoAlgorithmECDSAInternal {
40static const char* const ALG256 = "ES256";
41static const char* const ALG384 = "ES384";
42static const char* const ALG512 = "ES512";
43static const char* const P256 = "P-256";
44static const char* const P384 = "P-384";
45static const char* const P521 = "P-521";
46}
47
48Ref<CryptoAlgorithm> CryptoAlgorithmECDSA::create()
49{
50 return adoptRef(*new CryptoAlgorithmECDSA);
51}
52
53CryptoAlgorithmIdentifier CryptoAlgorithmECDSA::identifier() const
54{
55 return s_identifier;
56}
57
58void CryptoAlgorithmECDSA::sign(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
59{
60 if (key->type() != CryptoKeyType::Private) {
61 exceptionCallback(InvalidAccessError);
62 return;
63 }
64
65 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
66 [parameters = crossThreadCopy(downcast<CryptoAlgorithmEcdsaParams>(parameters)), key = WTFMove(key), data = WTFMove(data)] {
67 return platformSign(parameters, downcast<CryptoKeyEC>(key.get()), data);
68 });
69}
70
71void CryptoAlgorithmECDSA::verify(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
72{
73 if (key->type() != CryptoKeyType::Public) {
74 exceptionCallback(InvalidAccessError);
75 return;
76 }
77
78 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
79 [parameters = crossThreadCopy(downcast<CryptoAlgorithmEcdsaParams>(parameters)), key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data)] {
80 return platformVerify(parameters, downcast<CryptoKeyEC>(key.get()), signature, data);
81 });
82}
83
84void CryptoAlgorithmECDSA::generateKey(const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext&)
85{
86 const auto& ecParameters = downcast<CryptoAlgorithmEcKeyParams>(parameters);
87
88 if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey)) {
89 exceptionCallback(SyntaxError);
90 return;
91 }
92
93 auto result = CryptoKeyEC::generatePair(CryptoAlgorithmIdentifier::ECDSA, ecParameters.namedCurve, extractable, usages);
94 if (result.hasException()) {
95 exceptionCallback(result.releaseException().code());
96 return;
97 }
98
99 auto pair = result.releaseReturnValue();
100 pair.publicKey->setUsagesBitmap(pair.publicKey->usagesBitmap() & CryptoKeyUsageVerify);
101 pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & CryptoKeyUsageSign);
102 callback(WTFMove(pair));
103}
104
105void CryptoAlgorithmECDSA::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback)
106{
107 using namespace CryptoAlgorithmECDSAInternal;
108 const auto& ecParameters = downcast<CryptoAlgorithmEcKeyParams>(parameters);
109
110 RefPtr<CryptoKeyEC> result;
111 switch (format) {
112 case CryptoKeyFormat::Jwk: {
113 JsonWebKey key = WTFMove(WTF::get<JsonWebKey>(data));
114
115 if (usages && ((!key.d.isNull() && (usages ^ CryptoKeyUsageSign)) || (key.d.isNull() && (usages ^ CryptoKeyUsageVerify)))) {
116 exceptionCallback(SyntaxError);
117 return;
118 }
119 if (usages && !key.use.isNull() && key.use != "sig") {
120 exceptionCallback(DataError);
121 return;
122 }
123
124 bool isMatched = false;
125 if (key.crv == P256)
126 isMatched = key.alg.isNull() || key.alg == ALG256;
127 if (key.crv == P384)
128 isMatched = key.alg.isNull() || key.alg == ALG384;
129 if (key.crv == P521)
130 isMatched = key.alg.isNull() || key.alg == ALG512;
131 if (!isMatched) {
132 exceptionCallback(DataError);
133 return;
134 }
135
136 result = CryptoKeyEC::importJwk(ecParameters.identifier, ecParameters.namedCurve, WTFMove(key), extractable, usages);
137 break;
138 }
139 case CryptoKeyFormat::Raw:
140 if (usages && (usages ^ CryptoKeyUsageVerify)) {
141 exceptionCallback(SyntaxError);
142 return;
143 }
144 result = CryptoKeyEC::importRaw(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
145 break;
146 case CryptoKeyFormat::Spki:
147 if (usages && (usages ^ CryptoKeyUsageVerify)) {
148 exceptionCallback(SyntaxError);
149 return;
150 }
151 result = CryptoKeyEC::importSpki(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
152 break;
153 case CryptoKeyFormat::Pkcs8:
154 if (usages && (usages ^ CryptoKeyUsageSign)) {
155 exceptionCallback(SyntaxError);
156 return;
157 }
158 result = CryptoKeyEC::importPkcs8(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
159 break;
160 }
161 if (!result) {
162 exceptionCallback(DataError);
163 return;
164 }
165
166 callback(*result);
167}
168
169void CryptoAlgorithmECDSA::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback)
170{
171 const auto& ecKey = downcast<CryptoKeyEC>(key.get());
172
173 if (!ecKey.keySizeInBits()) {
174 exceptionCallback(OperationError);
175 return;
176 }
177
178 KeyData result;
179 switch (format) {
180 case CryptoKeyFormat::Jwk: {
181 auto jwk = ecKey.exportJwk();
182 if (jwk.hasException()) {
183 exceptionCallback(jwk.releaseException().code());
184 return;
185 }
186 result = jwk.releaseReturnValue();
187 break;
188 }
189 case CryptoKeyFormat::Raw: {
190 auto raw = ecKey.exportRaw();
191 if (raw.hasException()) {
192 exceptionCallback(raw.releaseException().code());
193 return;
194 }
195 result = raw.releaseReturnValue();
196 break;
197 }
198 case CryptoKeyFormat::Spki: {
199 auto spki = ecKey.exportSpki();
200 if (spki.hasException()) {
201 exceptionCallback(spki.releaseException().code());
202 return;
203 }
204 result = spki.releaseReturnValue();
205 break;
206 }
207 case CryptoKeyFormat::Pkcs8: {
208 auto pkcs8 = ecKey.exportPkcs8();
209 if (pkcs8.hasException()) {
210 exceptionCallback(pkcs8.releaseException().code());
211 return;
212 }
213 result = pkcs8.releaseReturnValue();
214 break;
215 }
216 }
217
218 callback(format, WTFMove(result));
219}
220
221} // namespace WebCore
222
223#endif // ENABLE(WEB_CRYPTO)
224