[GCrypt] Fix PK verification for ECDSA
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoAlgorithmRSASSA_PKCS1_v1_5GCrypt.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L. All rights reserved.
3  * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
18  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
24  * THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "CryptoAlgorithmRSASSA_PKCS1_v1_5.h"
29
30 #if ENABLE(SUBTLE_CRYPTO)
31
32 #include "CryptoAlgorithmRsaSsaParamsDeprecated.h"
33 #include "CryptoKeyRSA.h"
34 #include "ExceptionCode.h"
35 #include "NotImplemented.h"
36 #include "ScriptExecutionContext.h"
37 #include <pal/crypto/CryptoDigest.h>
38 #include <pal/crypto/gcrypt/Handle.h>
39 #include <pal/crypto/gcrypt/Utilities.h>
40
41 namespace WebCore {
42
43 static std::optional<PAL::CryptoDigest::Algorithm> hashCryptoDigestAlgorithm(CryptoAlgorithmIdentifier identifier)
44 {
45     switch (identifier) {
46     case CryptoAlgorithmIdentifier::SHA_1:
47         return PAL::CryptoDigest::Algorithm::SHA_1;
48     case CryptoAlgorithmIdentifier::SHA_224:
49         return PAL::CryptoDigest::Algorithm::SHA_224;
50     case CryptoAlgorithmIdentifier::SHA_256:
51         return PAL::CryptoDigest::Algorithm::SHA_256;
52     case CryptoAlgorithmIdentifier::SHA_384:
53         return PAL::CryptoDigest::Algorithm::SHA_384;
54     case CryptoAlgorithmIdentifier::SHA_512:
55         return PAL::CryptoDigest::Algorithm::SHA_512;
56     default:
57         return std::nullopt;
58     }
59 }
60
61 static std::optional<const char*> hashAlgorithmName(CryptoAlgorithmIdentifier identifier)
62 {
63     switch (identifier) {
64     case CryptoAlgorithmIdentifier::SHA_1:
65         return "sha1";
66     case CryptoAlgorithmIdentifier::SHA_224:
67         return "sha224";
68     case CryptoAlgorithmIdentifier::SHA_256:
69         return "sha256";
70     case CryptoAlgorithmIdentifier::SHA_384:
71         return "sha384";
72     case CryptoAlgorithmIdentifier::SHA_512:
73         return "sha512";
74     default:
75         return std::nullopt;
76     }
77 }
78
79 static std::optional<Vector<uint8_t>> mpiData(gcry_sexp_t paramSexp)
80 {
81     // Retrieve the MPI value stored in the s-expression: (name mpi-data)
82     PAL::GCrypt::Handle<gcry_mpi_t> paramMPI(gcry_sexp_nth_mpi(paramSexp, 1, GCRYMPI_FMT_USG));
83     if (!paramMPI)
84         return std::nullopt;
85
86     // Query the data length first to properly prepare the buffer.
87     size_t dataLength = 0;
88     gcry_error_t error = gcry_mpi_print(GCRYMPI_FMT_USG, nullptr, 0, &dataLength, paramMPI);
89     if (error != GPG_ERR_NO_ERROR) {
90         PAL::GCrypt::logError(error);
91         return std::nullopt;
92     }
93
94     // Finally, copy the MPI data into a properly-sized buffer.
95     Vector<uint8_t> output(dataLength);
96     error = gcry_mpi_print(GCRYMPI_FMT_USG, output.data(), output.size(), nullptr, paramMPI);
97     if (error != GPG_ERR_NO_ERROR) {
98         PAL::GCrypt::logError(error);
99         return std::nullopt;
100     }
101
102     return output;
103 }
104
105 static std::optional<Vector<uint8_t>> gcryptSign(gcry_sexp_t keySexp, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier)
106 {
107     // Perform digest operation with the specified algorithm on the given data.
108     Vector<uint8_t> dataHash;
109     {
110         auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
111         if (!digestAlgorithm)
112             return std::nullopt;
113
114         auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
115         if (!digest)
116             return std::nullopt;
117
118         digest->addBytes(data.data(), data.size());
119         dataHash = digest->computeHash();
120     }
121
122     // Construct the data s-expression that contains PKCS#1-padded hashed data.
123     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
124     {
125         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
126         if (!shaAlgorithm)
127             return std::nullopt;
128
129         gcry_error_t error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
130             *shaAlgorithm, dataHash.size(), dataHash.data());
131         if (error != GPG_ERR_NO_ERROR) {
132             PAL::GCrypt::logError(error);
133             return std::nullopt;
134         }
135     }
136
137     // Perform the PK signing, retrieving a sig-val s-expression of the following form:
138     // (sig-val
139     //   (rsa
140     //     (s s-mpi)))
141     PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
142     gcry_error_t error = gcry_pk_sign(&signatureSexp, dataSexp, keySexp);
143     if (error != GPG_ERR_NO_ERROR) {
144         PAL::GCrypt::logError(error);
145         return std::nullopt;
146     }
147
148     // Return MPI data of the embedded s integer.
149     PAL::GCrypt::Handle<gcry_sexp_t> sSexp(gcry_sexp_find_token(signatureSexp, "s", 0));
150     if (!sSexp)
151         return std::nullopt;
152
153     return mpiData(sSexp);
154 }
155
156 static std::optional<bool> gcryptVerify(gcry_sexp_t keySexp, const Vector<uint8_t>& signature, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier)
157 {
158     // Perform digest operation with the specified algorithm on the given data.
159     Vector<uint8_t> dataHash;
160     {
161         auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
162         if (!digestAlgorithm)
163             return std::nullopt;
164
165         auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
166         if (!digest)
167             return std::nullopt;
168
169         digest->addBytes(data.data(), data.size());
170         dataHash = digest->computeHash();
171     }
172
173     // Construct the sig-val s-expression that contains the signature data.
174     PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
175     gcry_error_t error = gcry_sexp_build(&signatureSexp, nullptr, "(sig-val(rsa(s %b)))",
176         signature.size(), signature.data());
177     if (error != GPG_ERR_NO_ERROR) {
178         PAL::GCrypt::logError(error);
179         return std::nullopt;
180     }
181
182     // Construct the data s-expression that contains PKCS#1-padded hashed data.
183     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
184     {
185         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
186         if (!shaAlgorithm)
187             return std::nullopt;
188
189         error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
190             *shaAlgorithm, dataHash.size(), dataHash.data());
191         if (error != GPG_ERR_NO_ERROR) {
192             PAL::GCrypt::logError(error);
193             return std::nullopt;
194         }
195     }
196
197     // Perform the PK verification. We report success if there's no error returned, or
198     // a failure in any other case. OperationError should not be returned at this point.
199     error = gcry_pk_verify(signatureSexp, dataSexp, keySexp);
200     return { error == GPG_ERR_NO_ERROR };
201 }
202
203 void CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
204 {
205     context.ref();
206     workQueue.dispatch(
207         [key = WTFMove(key), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
208             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
209
210             auto output = gcryptSign(rsaKey.platformKey(), data, rsaKey.hashAlgorithmIdentifier());
211             if (!output) {
212                 // We should only dereference callbacks after being back to the Document/Worker threads.
213                 context.postTask(
214                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
215                         exceptionCallback(OperationError);
216                         context.deref();
217                     });
218                 return;
219             }
220
221             // We should only dereference callbacks after being back to the Document/Worker threads.
222             context.postTask(
223                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
224                     callback(output);
225                     context.deref();
226                 });
227         });
228 }
229
230 void CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
231 {
232     context.ref();
233     workQueue.dispatch(
234         [key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
235             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
236
237             auto output = gcryptVerify(rsaKey.platformKey(), signature, data, rsaKey.hashAlgorithmIdentifier());
238             if (!output) {
239                 // We should only dereference callbacks after being back to the Document/Worker threads.
240                 context.postTask(
241                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
242                         exceptionCallback(OperationError);
243                         context.deref();
244                     });
245                 return;
246             }
247
248             // We should only dereference callbacks after being back to the Document/Worker threads.
249             context.postTask(
250                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
251                     callback(output);
252                     context.deref();
253                 });
254         });
255 }
256
257 ExceptionOr<void> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(const CryptoAlgorithmRsaSsaParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
258 {
259     notImplemented();
260     return Exception { NOT_SUPPORTED_ERR };
261 }
262
263 ExceptionOr<void> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoAlgorithmRsaSsaParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, const CryptoOperationData&, BoolCallback&&, VoidCallback&&)
264 {
265     notImplemented();
266     return Exception { NOT_SUPPORTED_ERR };
267 }
268
269 } // namespace WebCore
270
271 #endif // ENABLE(SUBTLE_CRYPTO)