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