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 "CryptoAlgorithmRSA_PSS.h"
28
29#if ENABLE(WEB_CRYPTO) && HAVE(RSA_PSS)
30
31#include "CryptoAlgorithmRsaHashedImportParams.h"
32#include "CryptoAlgorithmRsaHashedKeyGenParams.h"
33#include "CryptoAlgorithmRsaPssParams.h"
34#include "CryptoKeyPair.h"
35#include "CryptoKeyRSA.h"
36#include <wtf/CrossThreadCopier.h>
37#include <wtf/Variant.h>
38
39namespace WebCore {
40
41namespace CryptoAlgorithmRSA_PSSInternal {
42static const char* const ALG1 = "PS1";
43static const char* const ALG224 = "PS224";
44static const char* const ALG256 = "PS256";
45static const char* const ALG384 = "PS384";
46static const char* const ALG512 = "PS512";
47}
48
49Ref<CryptoAlgorithm> CryptoAlgorithmRSA_PSS::create()
50{
51 return adoptRef(*new CryptoAlgorithmRSA_PSS);
52}
53
54CryptoAlgorithmIdentifier CryptoAlgorithmRSA_PSS::identifier() const
55{
56 return s_identifier;
57}
58
59void CryptoAlgorithmRSA_PSS::sign(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
60{
61 if (key->type() != CryptoKeyType::Private) {
62 exceptionCallback(InvalidAccessError);
63 return;
64 }
65
66 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
67 [parameters = crossThreadCopy(downcast<CryptoAlgorithmRsaPssParams>(parameters)), key = WTFMove(key), data = WTFMove(data)] {
68 return platformSign(parameters, downcast<CryptoKeyRSA>(key.get()), data);
69 });
70}
71
72void CryptoAlgorithmRSA_PSS::verify(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
73{
74 if (key->type() != CryptoKeyType::Public) {
75 exceptionCallback(InvalidAccessError);
76 return;
77 }
78
79 dispatchOperationInWorkQueue(workQueue, context, WTFMove(callback), WTFMove(exceptionCallback),
80 [parameters = crossThreadCopy(downcast<CryptoAlgorithmRsaPssParams>(parameters)), key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data)] {
81 return platformVerify(parameters, downcast<CryptoKeyRSA>(key.get()), signature, data);
82 });
83}
84
85void CryptoAlgorithmRSA_PSS::generateKey(const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context)
86{
87 const auto& rsaParameters = downcast<CryptoAlgorithmRsaHashedKeyGenParams>(parameters);
88
89 if (usages & (CryptoKeyUsageDecrypt | CryptoKeyUsageEncrypt | CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey)) {
90 exceptionCallback(SyntaxError);
91 return;
92 }
93
94 auto keyPairCallback = [capturedCallback = WTFMove(callback)](CryptoKeyPair&& pair) {
95 pair.publicKey->setUsagesBitmap(pair.publicKey->usagesBitmap() & CryptoKeyUsageVerify);
96 pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & CryptoKeyUsageSign);
97 capturedCallback(WTFMove(pair));
98 };
99 auto failureCallback = [capturedCallback = WTFMove(exceptionCallback)]() {
100 capturedCallback(OperationError);
101 };
102 CryptoKeyRSA::generatePair(CryptoAlgorithmIdentifier::RSA_PSS, rsaParameters.hashIdentifier, true, rsaParameters.modulusLength, rsaParameters.publicExponentVector(), extractable, usages, WTFMove(keyPairCallback), WTFMove(failureCallback), &context);
103}
104
105void CryptoAlgorithmRSA_PSS::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters& parameters, bool extractable, CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback)
106{
107 using namespace CryptoAlgorithmRSA_PSSInternal;
108
109 const auto& rsaParameters = downcast<CryptoAlgorithmRsaHashedImportParams>(parameters);
110
111 RefPtr<CryptoKeyRSA> result;
112 switch (format) {
113 case CryptoKeyFormat::Jwk: {
114 JsonWebKey key = WTFMove(WTF::get<JsonWebKey>(data));
115
116 if (usages && ((!key.d.isNull() && (usages ^ CryptoKeyUsageSign)) || (key.d.isNull() && (usages ^ CryptoKeyUsageVerify)))) {
117 exceptionCallback(SyntaxError);
118 return;
119 }
120 if (usages && !key.use.isNull() && key.use != "sig") {
121 exceptionCallback(DataError);
122 return;
123 }
124
125 bool isMatched = false;
126 switch (rsaParameters.hashIdentifier) {
127 case CryptoAlgorithmIdentifier::SHA_1:
128 isMatched = key.alg.isNull() || key.alg == ALG1;
129 break;
130 case CryptoAlgorithmIdentifier::SHA_224:
131 isMatched = key.alg.isNull() || key.alg == ALG224;
132 break;
133 case CryptoAlgorithmIdentifier::SHA_256:
134 isMatched = key.alg.isNull() || key.alg == ALG256;
135 break;
136 case CryptoAlgorithmIdentifier::SHA_384:
137 isMatched = key.alg.isNull() || key.alg == ALG384;
138 break;
139 case CryptoAlgorithmIdentifier::SHA_512:
140 isMatched = key.alg.isNull() || key.alg == ALG512;
141 break;
142 default:
143 break;
144 }
145 if (!isMatched) {
146 exceptionCallback(DataError);
147 return;
148 }
149
150 result = CryptoKeyRSA::importJwk(rsaParameters.identifier, rsaParameters.hashIdentifier, WTFMove(key), extractable, usages);
151 break;
152 }
153 case CryptoKeyFormat::Spki: {
154 if (usages && (usages ^ CryptoKeyUsageVerify)) {
155 exceptionCallback(SyntaxError);
156 return;
157 }
158 // FIXME: <webkit.org/b/165436>
159 result = CryptoKeyRSA::importSpki(rsaParameters.identifier, rsaParameters.hashIdentifier, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
160 break;
161 }
162 case CryptoKeyFormat::Pkcs8: {
163 if (usages && (usages ^ CryptoKeyUsageSign)) {
164 exceptionCallback(SyntaxError);
165 return;
166 }
167 // FIXME: <webkit.org/b/165436>
168 result = CryptoKeyRSA::importPkcs8(parameters.identifier, rsaParameters.hashIdentifier, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages);
169 break;
170 }
171 default:
172 exceptionCallback(NotSupportedError);
173 return;
174 }
175 if (!result) {
176 exceptionCallback(DataError);
177 return;
178 }
179
180 callback(*result);
181}
182
183void CryptoAlgorithmRSA_PSS::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback)
184{
185 using namespace CryptoAlgorithmRSA_PSSInternal;
186 const auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
187
188 if (!rsaKey.keySizeInBits()) {
189 exceptionCallback(OperationError);
190 return;
191 }
192
193 KeyData result;
194 switch (format) {
195 case CryptoKeyFormat::Jwk: {
196 JsonWebKey jwk = rsaKey.exportJwk();
197 switch (rsaKey.hashAlgorithmIdentifier()) {
198 case CryptoAlgorithmIdentifier::SHA_1:
199 jwk.alg = String(ALG1);
200 break;
201 case CryptoAlgorithmIdentifier::SHA_224:
202 jwk.alg = String(ALG224);
203 break;
204 case CryptoAlgorithmIdentifier::SHA_256:
205 jwk.alg = String(ALG256);
206 break;
207 case CryptoAlgorithmIdentifier::SHA_384:
208 jwk.alg = String(ALG384);
209 break;
210 case CryptoAlgorithmIdentifier::SHA_512:
211 jwk.alg = String(ALG512);
212 break;
213 default:
214 ASSERT_NOT_REACHED();
215 }
216 result = WTFMove(jwk);
217 break;
218 }
219 case CryptoKeyFormat::Spki: {
220 auto spki = rsaKey.exportSpki();
221 if (spki.hasException()) {
222 exceptionCallback(spki.releaseException().code());
223 return;
224 }
225 result = spki.releaseReturnValue();
226 break;
227 }
228 case CryptoKeyFormat::Pkcs8: {
229 auto pkcs8 = rsaKey.exportPkcs8();
230 if (pkcs8.hasException()) {
231 exceptionCallback(pkcs8.releaseException().code());
232 return;
233 }
234 result = pkcs8.releaseReturnValue();
235 break;
236 }
237 default:
238 exceptionCallback(NotSupportedError);
239 return;
240 }
241
242 callback(format, WTFMove(result));
243}
244
245}
246
247#endif // ENABLE(WEB_CRYPTO) && HAVE(RSA_PSS)
248