[GCrypt] Gather SUBTLE_CRYPTO utility functions in a single header
[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 "GCryptUtilities.h"
34 #include "NotImplemented.h"
35 #include "ScriptExecutionContext.h"
36
37 namespace WebCore {
38
39 static std::optional<Vector<uint8_t>> gcryptEncrypt(gcry_sexp_t keySexp, Vector<uint8_t>&& plainText)
40 {
41     // Embed the plain-text data in a `data` s-expression using PKCS#1 padding.
42     PAL::GCrypt::Handle<gcry_sexp_t> dataSexp;
43     gcry_error_t error = gcry_sexp_build(&dataSexp, nullptr, "(data(flags pkcs1)(value %b))",
44         plainText.size(), plainText.data());
45     if (error != GPG_ERR_NO_ERROR) {
46         PAL::GCrypt::logError(error);
47         return std::nullopt;
48     }
49
50     // Encrypt data with the provided key. The returned s-expression is of this form:
51     // (enc-val
52     //   (rsa
53     //     (a a-mpi)))
54     PAL::GCrypt::Handle<gcry_sexp_t> cipherSexp;
55     error = gcry_pk_encrypt(&cipherSexp, dataSexp, keySexp);
56     if (error != GPG_ERR_NO_ERROR) {
57         PAL::GCrypt::logError(error);
58         return std::nullopt;
59     }
60
61     // Return MPI data of the embedded `a` integer.
62     PAL::GCrypt::Handle<gcry_sexp_t> aSexp(gcry_sexp_find_token(cipherSexp, "a", 0));
63     if (!aSexp)
64         return std::nullopt;
65
66     return mpiData(aSexp);
67 }
68
69 static std::optional<Vector<uint8_t>> gcryptDecrypt(gcry_sexp_t keySexp, Vector<uint8_t>&& cipherText)
70 {
71     // Embed the cipher-text data in an `enc-val` s-expression using PKCS#1 padding.
72     PAL::GCrypt::Handle<gcry_sexp_t> encValSexp;
73     gcry_error_t error = gcry_sexp_build(&encValSexp, nullptr, "(enc-val(flags pkcs1)(rsa(a %b)))",
74         cipherText.size(), cipherText.data());
75     if (error != GPG_ERR_NO_ERROR) {
76         PAL::GCrypt::logError(error);
77         return std::nullopt;
78     }
79
80     // Decrypt data with the provided key. The returned s-expression is of this form:
81     // (data
82     //   (flags pkcs1)
83     //   (value block))
84     PAL::GCrypt::Handle<gcry_sexp_t> plainSexp;
85     error = gcry_pk_decrypt(&plainSexp, encValSexp, keySexp);
86     if (error != GPG_ERR_NO_ERROR) {
87         PAL::GCrypt::logError(error);
88         return std::nullopt;
89     }
90
91     // Return MPI data of the embedded `value` integer.
92     PAL::GCrypt::Handle<gcry_sexp_t> valueSexp(gcry_sexp_find_token(plainSexp, "value", 0));
93     if (!valueSexp)
94         return std::nullopt;
95
96     return mpiData(valueSexp);
97 }
98
99 void CryptoAlgorithmRSAES_PKCS1_v1_5::platformEncrypt(Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
100 {
101     context.ref();
102     workQueue.dispatch(
103         [key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
104             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
105
106             auto output = gcryptEncrypt(rsaKey.platformKey(), WTFMove(plainText));
107             if (!output) {
108                 // We should only dereference callbacks after being back to the Document/Worker threads.
109                 context.postTask(
110                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
111                         exceptionCallback(OperationError);
112                         context.deref();
113                     });
114                 return;
115             }
116
117             // We should only dereference callbacks after being back to the Document/Worker threads.
118             context.postTask(
119                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
120                     callback(WTFMove(output));
121                     context.deref();
122                 });
123         });
124 }
125
126 void CryptoAlgorithmRSAES_PKCS1_v1_5::platformDecrypt(Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
127 {
128     context.ref();
129     workQueue.dispatch(
130         [key = WTFMove(key), cipherText = WTFMove(cipherText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
131             auto& rsaKey = downcast<CryptoKeyRSA>(key.get());
132
133             auto output = gcryptDecrypt(rsaKey.platformKey(), WTFMove(cipherText));
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 ExceptionOr<void> CryptoAlgorithmRSAES_PKCS1_v1_5::platformEncrypt(const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
154 {
155     notImplemented();
156     return Exception { NOT_SUPPORTED_ERR };
157 }
158
159 ExceptionOr<void> CryptoAlgorithmRSAES_PKCS1_v1_5::platformDecrypt(const CryptoKeyRSA&, const CryptoOperationData&, VectorCallback&&, VoidCallback&&)
160 {
161     notImplemented();
162     return Exception { NOT_SUPPORTED_ERR };
163 }
164
165 } // namespace WebCore
166
167 #endif // ENABLE(SUBTLE_CRYPTO)