[GCrypt] Gather SUBTLE_CRYPTO utility functions in a single header
[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 "GCryptUtilities.h"
36 #include "NotImplemented.h"
37 #include "ScriptExecutionContext.h"
38
39 namespace WebCore {
40
41 static std::optional<Vector<uint8_t>> gcryptSign(gcry_sexp_t keySexp, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier)
42 {
43     // Perform digest operation with the specified algorithm on the given data.
44     Vector<uint8_t> dataHash;
45     {
46         auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
47         if (!digestAlgorithm)
48             return std::nullopt;
49
50         auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
51         if (!digest)
52             return std::nullopt;
53
54         digest->addBytes(data.data(), data.size());
55         dataHash = digest->computeHash();
56     }
57
58     // Construct the data s-expression that contains PKCS#1-padded hashed data.
59     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
60     {
61         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
62         if (!shaAlgorithm)
63             return std::nullopt;
64
65         gcry_error_t error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
66             *shaAlgorithm, dataHash.size(), dataHash.data());
67         if (error != GPG_ERR_NO_ERROR) {
68             PAL::GCrypt::logError(error);
69             return std::nullopt;
70         }
71     }
72
73     // Perform the PK signing, retrieving a sig-val s-expression of the following form:
74     // (sig-val
75     //   (rsa
76     //     (s s-mpi)))
77     PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
78     gcry_error_t error = gcry_pk_sign(&signatureSexp, dataSexp, keySexp);
79     if (error != GPG_ERR_NO_ERROR) {
80         PAL::GCrypt::logError(error);
81         return std::nullopt;
82     }
83
84     // Return MPI data of the embedded s integer.
85     PAL::GCrypt::Handle<gcry_sexp_t> sSexp(gcry_sexp_find_token(signatureSexp, "s", 0));
86     if (!sSexp)
87         return std::nullopt;
88
89     return mpiData(sSexp);
90 }
91
92 static std::optional<bool> gcryptVerify(gcry_sexp_t keySexp, const Vector<uint8_t>& signature, const Vector<uint8_t>& data, CryptoAlgorithmIdentifier hashAlgorithmIdentifier)
93 {
94     // Perform digest operation with the specified algorithm on the given data.
95     Vector<uint8_t> dataHash;
96     {
97         auto digestAlgorithm = hashCryptoDigestAlgorithm(hashAlgorithmIdentifier);
98         if (!digestAlgorithm)
99             return std::nullopt;
100
101         auto digest = PAL::CryptoDigest::create(*digestAlgorithm);
102         if (!digest)
103             return std::nullopt;
104
105         digest->addBytes(data.data(), data.size());
106         dataHash = digest->computeHash();
107     }
108
109     // Construct the sig-val s-expression that contains the signature data.
110     PAL::GCrypt::Handle<gcry_sexp_t> signatureSexp;
111     gcry_error_t error = gcry_sexp_build(&signatureSexp, nullptr, "(sig-val(rsa(s %b)))",
112         signature.size(), signature.data());
113     if (error != GPG_ERR_NO_ERROR) {
114         PAL::GCrypt::logError(error);
115         return std::nullopt;
116     }
117
118     // Construct the data s-expression that contains PKCS#1-padded hashed data.
119     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
120     {
121         auto shaAlgorithm = hashAlgorithmName(hashAlgorithmIdentifier);
122         if (!shaAlgorithm)
123             return std::nullopt;
124
125         error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(hash %s %b))",
126             *shaAlgorithm, dataHash.size(), dataHash.data());
127         if (error != GPG_ERR_NO_ERROR) {
128             PAL::GCrypt::logError(error);
129             return std::nullopt;
130         }
131     }
132
133     // Perform the PK verification. We report success if there's no error returned, or
134     // a failure in any other case. OperationError should not be returned at this point.
135     error = gcry_pk_verify(signatureSexp, dataSexp, keySexp);
136     return { error == GPG_ERR_NO_ERROR };
137 }
138
139 void CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
140 {
141     context.ref();
142     workQueue.dispatch(
143         [key = WTFMove(key), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
144             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
145
146             auto output = gcryptSign(rsaKey.platformKey(), data, rsaKey.hashAlgorithmIdentifier());
147             if (!output) {
148                 // We should only dereference callbacks after being back to the Document/Worker threads.
149                 context.postTask(
150                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
151                         exceptionCallback(OperationError);
152                         context.deref();
153                     });
154                 return;
155             }
156
157             // We should only dereference callbacks after being back to the Document/Worker threads.
158             context.postTask(
159                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
160                     callback(output);
161                     context.deref();
162                 });
163         });
164 }
165
166 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)
167 {
168     context.ref();
169     workQueue.dispatch(
170         [key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
171             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
172
173             auto output = gcryptVerify(rsaKey.platformKey(), signature, data, rsaKey.hashAlgorithmIdentifier());
174             if (!output) {
175                 // We should only dereference callbacks after being back to the Document/Worker threads.
176                 context.postTask(
177                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
178                         exceptionCallback(OperationError);
179                         context.deref();
180                     });
181                 return;
182             }
183
184             // We should only dereference callbacks after being back to the Document/Worker threads.
185             context.postTask(
186                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
187                     callback(output);
188                     context.deref();
189                 });
190         });
191 }
192
193 ExceptionOr<void> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformSign(const CryptoAlgorithmRsaSsaParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
194 {
195     notImplemented();
196     return Exception { NOT_SUPPORTED_ERR };
197 }
198
199 ExceptionOr<void> CryptoAlgorithmRSASSA_PKCS1_v1_5::platformVerify(const CryptoAlgorithmRsaSsaParamsDeprecated&, const CryptoKeyRSA&, const CryptoOperationData&, const CryptoOperationData&, BoolCallback&&, VoidCallback&&)
200 {
201     notImplemented();
202     return Exception { NOT_SUPPORTED_ERR };
203 }
204
205 } // namespace WebCore
206
207 #endif // ENABLE(SUBTLE_CRYPTO)