Unreviewed build fix after r218484.
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoAlgorithmAES_CTRGCrypt.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2017 Metrological Group B.V.
4  * Copyright (C) 2017 Igalia S.L.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25  * THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "CryptoAlgorithmAES_CTR.h"
30
31 #if ENABLE(SUBTLE_CRYPTO)
32
33 #include "CryptoAlgorithmAesCtrParams.h"
34 #include "CryptoKeyAES.h"
35 #include "ExceptionCode.h"
36 #include "ScriptExecutionContext.h"
37 #include <pal/crypto/gcrypt/Handle.h>
38 #include <pal/crypto/gcrypt/Utilities.h>
39
40 namespace WebCore {
41
42 // This is a helper function that resets the cipher object, sets the provided counter data,
43 // and executes the encrypt or decrypt operation, retrieving and returning the output data.
44 static std::optional<Vector<uint8_t>> callOperation(PAL::GCrypt::CipherOperation operation, gcry_cipher_hd_t handle, const Vector<uint8_t>& counter, const uint8_t* data, const size_t size)
45 {
46     gcry_error_t error = gcry_cipher_reset(handle);
47     if (error != GPG_ERR_NO_ERROR) {
48         PAL::GCrypt::logError(error);
49         return std::nullopt;
50     }
51
52     error = gcry_cipher_setctr(handle, counter.data(), counter.size());
53     if (error != GPG_ERR_NO_ERROR) {
54         PAL::GCrypt::logError(error);
55         return std::nullopt;
56     }
57
58     error = gcry_cipher_final(handle);
59     if (error != GPG_ERR_NO_ERROR) {
60         PAL::GCrypt::logError(error);
61         return std::nullopt;
62     }
63
64     Vector<uint8_t> output(size);
65     error = operation(handle, output.data(), output.size(), data, size);
66     if (error != GPG_ERR_NO_ERROR) {
67         PAL::GCrypt::logError(error);
68         return std::nullopt;
69     }
70
71     return output;
72 }
73
74 static std::optional<Vector<uint8_t>> gcryptAES_CTR(PAL::GCrypt::CipherOperation operation, const Vector<uint8_t>& key, const Vector<uint8_t>& counter, size_t counterLength, const Vector<uint8_t>& inputText)
75 {
76     constexpr size_t blockSize = 16;
77     auto algorithm = PAL::GCrypt::aesAlgorithmForKeySize(key.size() * 8);
78     if (!algorithm)
79         return std::nullopt;
80
81     // Construct the libgcrypt cipher object and attach the key to it. Key information on this
82     // cipher object will live through any gcry_cipher_reset() calls.
83     PAL::GCrypt::Handle<gcry_cipher_hd_t> handle;
84     gcry_error_t error = gcry_cipher_open(&handle, *algorithm, GCRY_CIPHER_MODE_CTR, 0);
85     if (error != GPG_ERR_NO_ERROR) {
86         PAL::GCrypt::logError(error);
87         return std::nullopt;
88     }
89
90     error = gcry_cipher_setkey(handle, key.data(), key.size());
91     if (error != GPG_ERR_NO_ERROR) {
92         PAL::GCrypt::logError(error);
93         return std::nullopt;
94     }
95
96     // Calculate the block count: ((inputText.size() + blockSize - 1) / blockSize), remainder discarded.
97     PAL::GCrypt::Handle<gcry_mpi_t> blockCountMPI(gcry_mpi_new(0));
98     {
99         PAL::GCrypt::Handle<gcry_mpi_t> blockSizeMPI(gcry_mpi_set_ui(nullptr, blockSize));
100         PAL::GCrypt::Handle<gcry_mpi_t> roundedUpSize(gcry_mpi_set_ui(nullptr, inputText.size()));
101
102         gcry_mpi_add_ui(roundedUpSize, roundedUpSize, blockSize - 1);
103         gcry_mpi_div(blockCountMPI, nullptr, roundedUpSize, blockSizeMPI, 0);
104     }
105
106     // Calculate the counter limit for the specified counter length: (2 << counterLength).
107     // (counterLimitMPI - 1) is the maximum value the counter can hold -- essentially it's
108     // a bit-mask for valid counter values.
109     PAL::GCrypt::Handle<gcry_mpi_t> counterLimitMPI(gcry_mpi_set_ui(nullptr, 1));
110     gcry_mpi_mul_2exp(counterLimitMPI, counterLimitMPI, counterLength);
111
112     // Counter values must not repeat for a given cipher text. If the counter limit (i.e.
113     // the number of unique counter values we could produce for the specified counter
114     // length) is lower than the deduced block count, we bail.
115     if (gcry_mpi_cmp(counterLimitMPI, blockCountMPI) < 0)
116         return std::nullopt;
117
118     // If the counter length, in bits, matches the size of the counter data, we don't have to
119     // use any part of the counter Vector<> as nonce. This allows us to directly encrypt or
120     // decrypt all the provided data in a single step.
121     if (counterLength == counter.size() * 8)
122         return callOperation(operation, handle, counter, inputText.data(), inputText.size());
123
124     // Scan the counter data into the MPI format. We'll do all the counter computations with
125     // the MPI API.
126     PAL::GCrypt::Handle<gcry_mpi_t> counterDataMPI;
127     error = gcry_mpi_scan(&counterDataMPI, GCRYMPI_FMT_USG, counter.data(), counter.size(), nullptr);
128     if (error != GPG_ERR_NO_ERROR) {
129         PAL::GCrypt::logError(error);
130         return std::nullopt;
131     }
132
133     // Extract the counter MPI from the counterDataMPI: (counterDataMPI % counterLimitMPI).
134     // This MPI represents solely the counter value, as initially provided.
135     PAL::GCrypt::Handle<gcry_mpi_t> counterMPI(gcry_mpi_new(0));
136     gcry_mpi_mod(counterMPI, counterDataMPI, counterLimitMPI);
137
138     {
139         // Calculate the leeway of the initially-provided counter: counterLimitMPI - counterMPI.
140         // This is essentially the number of blocks we can encrypt/decrypt with that counter
141         // (incrementing it after each operation) before the counter wraps around to 0.
142         PAL::GCrypt::Handle<gcry_mpi_t> counterLeewayMPI(gcry_mpi_new(0));
143         gcry_mpi_sub(counterLeewayMPI, counterLimitMPI, counterMPI);
144
145         // If counterLeewayMPI is larger or equal to the deduced block count, we can directly
146         // encrypt or decrypt the provided data in a single step since it's ensured that the
147         // counter won't overflow.
148         if (gcry_mpi_cmp(counterLeewayMPI, blockCountMPI) >= 0)
149             return callOperation(operation, handle, counter, inputText.data(), inputText.size());
150     }
151
152     // From here onwards we're dealing with a counter of which the length doesn't match the
153     // provided data, meaning we'll also have to manage the nonce data. The counter will also
154     // wrap around, so we'll have to address that too.
155
156     // Determine the nonce MPI that we'll use to reconstruct the counter data for each block:
157     // (counterDataMPI - counterMPI). This is equivalent to counterDataMPI with the lowest
158     // counterLength bits cleared.
159     PAL::GCrypt::Handle<gcry_mpi_t> nonceMPI(gcry_mpi_new(0));
160     gcry_mpi_sub(nonceMPI, counterDataMPI, counterMPI);
161
162     // FIXME: This should be optimized further by first encrypting the amount of blocks for
163     // which the counter won't yet wrap around, and then encrypting the rest of the blocks
164     // starting from the counter set to 0.
165
166     Vector<uint8_t> output;
167     Vector<uint8_t> blockCounterData(16);
168     size_t inputTextSize = inputText.size();
169
170     for (size_t i = 0; i < inputTextSize; i += 16) {
171         size_t blockInputSize = std::min<size_t>(16, inputTextSize - i);
172
173         // Construct the block-specific counter: (nonceMPI + counterMPI).
174         PAL::GCrypt::Handle<gcry_mpi_t> blockCounterMPI(gcry_mpi_new(0));
175         gcry_mpi_add(blockCounterMPI, nonceMPI, counterMPI);
176
177         error = gcry_mpi_print(GCRYMPI_FMT_USG, blockCounterData.data(), blockCounterData.size(), nullptr, blockCounterMPI);
178         if (error != GPG_ERR_NO_ERROR) {
179             PAL::GCrypt::logError(error);
180             return std::nullopt;
181         }
182
183         // Encrypt/decrypt this single block with the block-specific counter. Output for this
184         // single block is appended to the general output vector.
185         auto blockOutput = callOperation(operation, handle, blockCounterData, inputText.data() + i, blockInputSize);
186         if (!blockOutput)
187             return std::nullopt;
188
189         output.appendVector(*blockOutput);
190
191         // Increment the counter. The modulus operation takes care of any wrap-around.
192         PAL::GCrypt::Handle<gcry_mpi_t> counterIncrementMPI(gcry_mpi_new(0));
193         gcry_mpi_add_ui(counterIncrementMPI, counterMPI, 1);
194         gcry_mpi_mod(counterMPI, counterIncrementMPI, counterLimitMPI);
195     }
196
197     return output;
198 }
199
200 void CryptoAlgorithmAES_CTR::platformEncrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& plainText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
201 {
202     context.ref();
203     workQueue.dispatch(
204         [parameters = WTFMove(parameters), key = WTFMove(key), plainText = WTFMove(plainText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
205             auto& aesParameters = downcast<CryptoAlgorithmAesCtrParams>(*parameters);
206             auto& aesKey = downcast<CryptoKeyAES>(key.get());
207
208             auto output = gcryptAES_CTR(gcry_cipher_encrypt, aesKey.key(), aesParameters.counterVector(), aesParameters.length, plainText);
209             if (!output) {
210                 // We should only dereference callbacks after being back to the Document/Worker threads.
211                 context.postTask(
212                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
213                         exceptionCallback(OperationError);
214                         context.deref();
215                     });
216                 return;
217             }
218
219             // We should only dereference callbacks after being back to the Document/Worker threads.
220             context.postTask(
221                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
222                     callback(WTFMove(output));
223                     context.deref();
224                 });
225         });
226 }
227
228 void CryptoAlgorithmAES_CTR::platformDecrypt(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& key, Vector<uint8_t>&& cipherText, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
229 {
230     context.ref();
231     workQueue.dispatch(
232         [parameters = WTFMove(parameters), key = WTFMove(key), cipherText = WTFMove(cipherText), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
233             auto& aesParameters = downcast<CryptoAlgorithmAesCtrParams>(*parameters);
234             auto& aesKey = downcast<CryptoKeyAES>(key.get());
235
236             auto output = gcryptAES_CTR(gcry_cipher_decrypt, aesKey.key(), aesParameters.counterVector(), aesParameters.length, cipherText);
237             if (!output) {
238                 // We should only dereference callbacks after being back to the Document/Worker threads.
239                 context.postTask(
240                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
241                         exceptionCallback(OperationError);
242                         context.deref();
243                     });
244                 return;
245             }
246
247             // We should only dereference callbacks after being back to the Document/Worker threads.
248             context.postTask(
249                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) mutable {
250                     callback(WTFMove(output));
251                     context.deref();
252                 });
253         });
254 }
255
256 } // namespace WebCore
257
258 #endif // ENABLE(SUBTLE_CRYPTO)