[GCrypt] Fix PK verification for ECDSA
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoAlgorithmRSA_OAEPGCrypt.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L. 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_OAEP.h"
28
29 #if ENABLE(SUBTLE_CRYPTO)
30
31 #include "CryptoAlgorithmRsaOaepParams.h"
32 #include "CryptoKeyRSA.h"
33 #include "ExceptionCode.h"
34 #include "NotImplemented.h"
35 #include "ScriptExecutionContext.h"
36 #include <pal/crypto/gcrypt/Handle.h>
37 #include <pal/crypto/gcrypt/Utilities.h>
38
39 namespace WebCore {
40
41 static std::optional<const char*> hashAlgorithmName(CryptoAlgorithmIdentifier identifier)
42 {
43     switch (identifier) {
44     case CryptoAlgorithmIdentifier::SHA_1:
45         return "sha1";
46     case CryptoAlgorithmIdentifier::SHA_224:
47         return "sha224";
48     case CryptoAlgorithmIdentifier::SHA_256:
49         return "sha256";
50     case CryptoAlgorithmIdentifier::SHA_384:
51         return "sha384";
52     case CryptoAlgorithmIdentifier::SHA_512:
53         return "sha512";
54     default:
55         return std::nullopt;
56     }
57 }
58
59 static std::optional<Vector<uint8_t>> mpiData(gcry_sexp_t paramSexp)
60 {
61     // Retrieve the MPI value stored in the s-expression: (name mpi-data)
62     PAL::GCrypt::Handle<gcry_mpi_t> paramMPI(gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG));
63     if (!paramMPI)
64         return std::nullopt;
65
66     // Query the data length first to properly prepare the buffer.
67     size_t dataLength = 0;
68     gcry_error_t error = gcry_mpi_print(GCRYMPI_FMT_USG, nullptr, 0, &dataLength, paramMPI);
69     if (error != GPG_ERR_NO_ERROR) {
70         PAL::GCrypt::logError(error);
71         return std::nullopt;
72     }
73
74     // Finally, copy the MPI data into a properly-sized buffer.
75     Vector<uint8_t> output(dataLength);
76     error = gcry_mpi_print(GCRYMPI_FMT_USG, output.data(), output.size(), nullptr, paramMPI);
77     if (error != GPG_ERR_NO_ERROR) {
78         PAL::GCrypt::logError(error);
79         return std::nullopt;
80     }
81
82     return output;
83 }
84
85 static std::optional<Vector<uint8_t>> gcryptEncrypt(CryptoAlgorithmIdentifier hashAlgorithmIdentifier, gcry_sexp_t keySexp, const Vector<uint8_t>& labelVector, const Vector<uint8_t>& plainText)
86 {
87     // Embed the plain-text data in a data s-expression using OAEP padding.
88     // Empty label data is properly handled by gcry_sexp_build().
89     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
90     {
91         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
92         if (!shaAlgorithm)
93             return std::nullopt;
94
95         gcry_error_t error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags oaep)(hash-algo %s)(label %b)(value %b))",
96             *shaAlgorithm, labelVector.size(), labelVector.data(), plainText.size(), plainText.data());
97         if (error != GPG_ERR_NO_ERROR) {
98             PAL::GCrypt::logError(error);
99             return std::nullopt;
100         }
101     }
102
103     // Encrypt data with the provided key. The returned s-expression is of this form:
104     // (enc-val
105     //   (flags oaep)
106     //   (rsa
107     //     (a a-mpi)))
108     PAL::GCrypt::Handle<gcry_sexp_t> cipherSexp;
109     gcry_error_t error = gcry_pk_encrypt(&cipherSexp, dataSexp, keySexp);
110     if (error != GPG_ERR_NO_ERROR) {
111         PAL::GCrypt::logError(error);
112         return std::nullopt;
113     }
114
115     // Return MPI data of the embedded a integer.
116     PAL::GCrypt::Handle<gcry_sexp_t> aSexp(gcry_sexp_find_token(cipherSexp, "a", 0));
117     if (!aSexp)
118         return std::nullopt;
119
120     return mpiData(aSexp);
121 }
122
123 static std::optional<Vector<uint8_t>> gcryptDecrypt(CryptoAlgorithmIdentifier hashAlgorithmIdentifier, gcry_sexp_t keySexp, const Vector<uint8_t>& labelVector, const Vector<uint8_t>& cipherText)
124 {
125     // Embed the cipher-text data in an enc-val s-expression using OAEP padding.
126     // Empty label data is properly handled by gcry_sexp_build().
127     PAL::GCrypt::Handle<gcry_sexp_t> encValSexp;
128     {
129         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
130         if (!shaAlgorithm)
131             return std::nullopt;
132
133         gcry_error_t error = gcry_sexp_build(&encValSexp, nullptr, "(enc-val(flags oaep)(hash-algo %s)(label %b)(rsa(a %b)))",
134             *shaAlgorithm, labelVector.size(), labelVector.data(), cipherText.size(), cipherText.data());
135         if (error != GPG_ERR_NO_ERROR) {
136             PAL::GCrypt::logError(error);
137             return std::nullopt;
138         }
139     }
140
141     // Decrypt data with the provided key. The returned s-expression is of this form:
142     // (data
143     //   (flags oaep)
144     //   (value block))
145     PAL::GCrypt::Handle<gcry_sexp_t> plainSexp;
146     gcry_error_t error = gcry_pk_decrypt(&plainSexp, encValSexp, keySexp);
147     if (error != GPG_ERR_NO_ERROR) {
148         PAL::GCrypt::logError(error);
149         return std::nullopt;
150     }
151
152     // Return MPI data of the embedded value integer.
153     PAL::GCrypt::Handle<gcry_sexp_t> valueSexp(gcry_sexp_find_token(plainSexp, "value", 0));
154     if (!valueSexp)
155         return std::nullopt;
156
157     return mpiData(valueSexp);
158 }
159
160 void CryptoAlgorithmRSA_OAEP::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
161 {
162     context.ref();
163     workQueue.dispatch(
164         [parameters = WTFMove(parameters), key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
165             auto& rsaParameters = downcast<CryptoAlgorithmRsaOaepParams>(*parameters);
166             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
167
168             auto output = gcryptEncrypt(rsaKey.hashAlgorithmIdentifier(), rsaKey.platformKey(), rsaParameters.labelVector(), plainText);
169             if (!output) {
170                 // We should only dereference callbacks after being back to the Document/Worker threads.
171                 context.postTask(
172                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
173                         exceptionCallback(OperationError);
174                         context.deref();
175                     });
176                 return;
177             }
178
179             // We should only dereference callbacks after being back to the Document/Worker threads.
180             context.postTask(
181                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
182                     callback(output);
183                     context.deref();
184                 });
185         });
186 }
187
188 void CryptoAlgorithmRSA_OAEP::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
189 {
190     context.ref();
191     workQueue.dispatch(
192         [parameters = WTFMove(parameters), key = WTFMove(key), cipherText = WTFMove(cipherText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
193             auto& rsaParameters = downcast<CryptoAlgorithmRsaOaepParams>(*parameters);
194             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
195
196             auto output = gcryptDecrypt(rsaKey.hashAlgorithmIdentifier(), rsaKey.platformKey(), rsaParameters.labelVector(), cipherText);
197             if (!output) {
198                 // We should only dereference callbacks after being back to the Document/Worker threads.
199                 context.postTask(
200                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
201                         exceptionCallback(OperationError);
202                         context.deref();
203                     });
204                 return;
205             }
206
207             // We should only dereference callbacks after being back to the Document/Worker threads.
208             context.postTask(
209                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
210                     callback(output);
211                     context.deref();
212                 });
213         });
214 }
215
216 ExceptionOr<void> CryptoAlgorithmRSA_OAEP::platformEncrypt(const CryptoAlgorithmRsaOaepParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
217 {
218     notImplemented();
219     return Exception { NOT_SUPPORTED_ERR };
220 }
221
222 ExceptionOr<void> CryptoAlgorithmRSA_OAEP::platformDecrypt(const CryptoAlgorithmRsaOaepParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
223 {
224     notImplemented();
225     return Exception { NOT_SUPPORTED_ERR };
226 }
227
228 } // namespace WebCore
229
230 #endif // ENABLE(SUBTLE_CRYPTO)