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 "CryptoAlgorithmECDH.h" |
28 | |
29 | #if ENABLE(WEB_CRYPTO) |
30 | |
31 | #include "CryptoAlgorithmEcKeyParams.h" |
32 | #include "CryptoAlgorithmEcdhKeyDeriveParams.h" |
33 | #include "CryptoKeyEC.h" |
34 | #include "ScriptExecutionContext.h" |
35 | |
36 | namespace WebCore { |
37 | |
38 | Ref<CryptoAlgorithm> CryptoAlgorithmECDH::create() |
39 | { |
40 | return adoptRef(*new CryptoAlgorithmECDH); |
41 | } |
42 | |
43 | CryptoAlgorithmIdentifier CryptoAlgorithmECDH::identifier() const |
44 | { |
45 | return s_identifier; |
46 | } |
47 | |
48 | void CryptoAlgorithmECDH::generateKey(const CryptoAlgorithmParameters& parameters, bool , CryptoKeyUsageBitmap usages, KeyOrKeyPairCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext&) |
49 | { |
50 | const auto& ecParameters = downcast<CryptoAlgorithmEcKeyParams>(parameters); |
51 | |
52 | if (usages & (CryptoKeyUsageEncrypt | CryptoKeyUsageDecrypt | CryptoKeyUsageSign | CryptoKeyUsageVerify | CryptoKeyUsageWrapKey | CryptoKeyUsageUnwrapKey)) { |
53 | exceptionCallback(SyntaxError); |
54 | return; |
55 | } |
56 | |
57 | auto result = CryptoKeyEC::generatePair(CryptoAlgorithmIdentifier::ECDH, ecParameters.namedCurve, extractable, usages); |
58 | if (result.hasException()) { |
59 | exceptionCallback(result.releaseException().code()); |
60 | return; |
61 | } |
62 | |
63 | auto pair = result.releaseReturnValue(); |
64 | pair.publicKey->setUsagesBitmap(0); |
65 | pair.privateKey->setUsagesBitmap(pair.privateKey->usagesBitmap() & (CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits)); |
66 | callback(WTFMove(pair)); |
67 | } |
68 | |
69 | void CryptoAlgorithmECDH::deriveBits(const CryptoAlgorithmParameters& parameters, Ref<CryptoKey>&& baseKey, size_t length, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue) |
70 | { |
71 | // We only accept length that is a multiple of 8. |
72 | if (length % 8) { |
73 | exceptionCallback(OperationError); |
74 | return; |
75 | } |
76 | |
77 | auto& ecParameters = downcast<CryptoAlgorithmEcdhKeyDeriveParams>(parameters); |
78 | |
79 | if (baseKey->type() != CryptoKey::Type::Private) { |
80 | exceptionCallback(InvalidAccessError); |
81 | return; |
82 | } |
83 | ASSERT(ecParameters.publicKey); |
84 | if (ecParameters.publicKey->type() != CryptoKey::Type::Public) { |
85 | exceptionCallback(InvalidAccessError); |
86 | return; |
87 | } |
88 | if (baseKey->algorithmIdentifier() != ecParameters.publicKey->algorithmIdentifier()) { |
89 | exceptionCallback(InvalidAccessError); |
90 | return; |
91 | } |
92 | auto& ecBaseKey = downcast<CryptoKeyEC>(baseKey.get()); |
93 | auto& ecPublicKey = downcast<CryptoKeyEC>(*(ecParameters.publicKey.get())); |
94 | if (ecBaseKey.namedCurve() != ecPublicKey.namedCurve()) { |
95 | exceptionCallback(InvalidAccessError); |
96 | return; |
97 | } |
98 | |
99 | auto unifiedCallback = [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](Optional<Vector<uint8_t>>&& derivedKey, size_t length) mutable { |
100 | if (!derivedKey) { |
101 | exceptionCallback(OperationError); |
102 | return; |
103 | } |
104 | if (!length) { |
105 | callback(WTFMove(*derivedKey)); |
106 | return; |
107 | } |
108 | if (length / 8 > (*derivedKey).size()) { |
109 | exceptionCallback(OperationError); |
110 | return; |
111 | } |
112 | (*derivedKey).shrink(length / 8); |
113 | callback(WTFMove(*derivedKey)); |
114 | }; |
115 | |
116 | // This is a special case that can't use dispatchOperation() because it bundles |
117 | // the result validation and callback dispatch into unifiedCallback. |
118 | workQueue.dispatch( |
119 | [baseKey = WTFMove(baseKey), publicKey = ecParameters.publicKey, length, unifiedCallback = WTFMove(unifiedCallback), contextIdentifier = context.contextIdentifier()]() mutable { |
120 | auto derivedKey = platformDeriveBits(downcast<CryptoKeyEC>(baseKey.get()), downcast<CryptoKeyEC>(*publicKey)); |
121 | ScriptExecutionContext::postTaskTo(contextIdentifier, [derivedKey = WTFMove(derivedKey), length, unifiedCallback = WTFMove(unifiedCallback)](auto&) mutable { |
122 | unifiedCallback(WTFMove(derivedKey), length); |
123 | }); |
124 | }); |
125 | } |
126 | |
127 | void CryptoAlgorithmECDH::importKey(CryptoKeyFormat format, KeyData&& data, const CryptoAlgorithmParameters& parameters, bool , CryptoKeyUsageBitmap usages, KeyCallback&& callback, ExceptionCallback&& exceptionCallback) |
128 | { |
129 | const auto& ecParameters = downcast<CryptoAlgorithmEcKeyParams>(parameters); |
130 | |
131 | RefPtr<CryptoKeyEC> result; |
132 | switch (format) { |
133 | case CryptoKeyFormat::Jwk: { |
134 | JsonWebKey key = WTFMove(WTF::get<JsonWebKey>(data)); |
135 | |
136 | bool isUsagesAllowed = false; |
137 | if (!key.d.isNull()) { |
138 | isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageDeriveKey); |
139 | isUsagesAllowed = isUsagesAllowed || !(usages ^ CryptoKeyUsageDeriveBits); |
140 | isUsagesAllowed = isUsagesAllowed || !(usages ^ (CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits)); |
141 | } |
142 | isUsagesAllowed = isUsagesAllowed || !usages; |
143 | if (!isUsagesAllowed) { |
144 | exceptionCallback(SyntaxError); |
145 | return; |
146 | } |
147 | |
148 | if (usages && !key.use.isNull() && key.use != "enc" ) { |
149 | exceptionCallback(DataError); |
150 | return; |
151 | } |
152 | |
153 | result = CryptoKeyEC::importJwk(ecParameters.identifier, ecParameters.namedCurve, WTFMove(key), extractable, usages); |
154 | break; |
155 | } |
156 | case CryptoKeyFormat::Raw: |
157 | if (usages) { |
158 | exceptionCallback(SyntaxError); |
159 | return; |
160 | } |
161 | result = CryptoKeyEC::importRaw(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages); |
162 | break; |
163 | case CryptoKeyFormat::Spki: |
164 | if (usages) { |
165 | exceptionCallback(SyntaxError); |
166 | return; |
167 | } |
168 | result = CryptoKeyEC::importSpki(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages); |
169 | break; |
170 | case CryptoKeyFormat::Pkcs8: |
171 | if (usages && (usages ^ CryptoKeyUsageDeriveKey) && (usages ^ CryptoKeyUsageDeriveBits) && (usages ^ (CryptoKeyUsageDeriveKey | CryptoKeyUsageDeriveBits))) { |
172 | exceptionCallback(SyntaxError); |
173 | return; |
174 | } |
175 | result = CryptoKeyEC::importPkcs8(ecParameters.identifier, ecParameters.namedCurve, WTFMove(WTF::get<Vector<uint8_t>>(data)), extractable, usages); |
176 | break; |
177 | } |
178 | if (!result) { |
179 | exceptionCallback(DataError); |
180 | return; |
181 | } |
182 | |
183 | callback(*result); |
184 | } |
185 | |
186 | void CryptoAlgorithmECDH::exportKey(CryptoKeyFormat format, Ref<CryptoKey>&& key, KeyDataCallback&& callback, ExceptionCallback&& exceptionCallback) |
187 | { |
188 | const auto& ecKey = downcast<CryptoKeyEC>(key.get()); |
189 | |
190 | if (!ecKey.keySizeInBits()) { |
191 | exceptionCallback(OperationError); |
192 | return; |
193 | } |
194 | |
195 | KeyData result; |
196 | switch (format) { |
197 | case CryptoKeyFormat::Jwk: { |
198 | auto jwk = ecKey.exportJwk(); |
199 | if (jwk.hasException()) { |
200 | exceptionCallback(jwk.releaseException().code()); |
201 | return; |
202 | } |
203 | result = jwk.releaseReturnValue(); |
204 | break; |
205 | } |
206 | case CryptoKeyFormat::Raw: { |
207 | auto raw = ecKey.exportRaw(); |
208 | if (raw.hasException()) { |
209 | exceptionCallback(raw.releaseException().code()); |
210 | return; |
211 | } |
212 | result = raw.releaseReturnValue(); |
213 | break; |
214 | } |
215 | case CryptoKeyFormat::Spki: { |
216 | auto spki = ecKey.exportSpki(); |
217 | if (spki.hasException()) { |
218 | exceptionCallback(spki.releaseException().code()); |
219 | return; |
220 | } |
221 | result = spki.releaseReturnValue(); |
222 | break; |
223 | } |
224 | case CryptoKeyFormat::Pkcs8: { |
225 | auto pkcs8 = ecKey.exportPkcs8(); |
226 | if (pkcs8.hasException()) { |
227 | exceptionCallback(pkcs8.releaseException().code()); |
228 | return; |
229 | } |
230 | result = pkcs8.releaseReturnValue(); |
231 | break; |
232 | } |
233 | } |
234 | |
235 | callback(format, WTFMove(result)); |
236 | } |
237 | |
238 | } // namespace WebCore |
239 | |
240 | #endif // ENABLE(WEB_CRYPTO) |
241 | |