[GCrypt] Fix PK verification for ECDSA
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoAlgorithmHKDFGCrypt.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 "CryptoAlgorithmHKDF.h"
30
31 #if ENABLE(SUBTLE_CRYPTO)
32
33 #include "CryptoAlgorithmHkdfParams.h"
34 #include "CryptoKeyRaw.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 // libgcrypt doesn't provide HKDF functionality, so we have to implement it manually.
43 // We should switch to the libgcrypt-provided implementation once it's available.
44 // https://bugs.webkit.org/show_bug.cgi?id=171536
45
46 static std::optional<int> macAlgorithmForHashFunction(CryptoAlgorithmIdentifier identifier)
47 {
48     switch (identifier) {
49     case CryptoAlgorithmIdentifier::SHA_1:
50         return GCRY_MAC_HMAC_SHA1;
51     case CryptoAlgorithmIdentifier::SHA_224:
52         return GCRY_MAC_HMAC_SHA224;
53     case CryptoAlgorithmIdentifier::SHA_256:
54         return GCRY_MAC_HMAC_SHA256;
55     case CryptoAlgorithmIdentifier::SHA_384:
56         return GCRY_MAC_HMAC_SHA384;
57     case CryptoAlgorithmIdentifier::SHA_512:
58         return GCRY_MAC_HMAC_SHA512;
59     default:
60         return std::nullopt;
61     }
62 }
63
64 static std::optional<Vector<uint8_t>> gcryptDeriveBits(const Vector<uint8_t>& key, const Vector<uint8_t>& salt, const Vector<uint8_t>& info, size_t lengthInBytes, CryptoAlgorithmIdentifier identifier)
65 {
66     // libgcrypt doesn't provide HKDF support, so we have to implement
67     // the functionality ourselves as specified in RFC5869.
68     // https://www.ietf.org/rfc/rfc5869.txt
69
70     auto macAlgorithm = macAlgorithmForHashFunction(identifier);
71     if (!macAlgorithm)
72         return std::nullopt;
73
74     // We can immediately discard invalid output lengths, otherwise needed for the expand step.
75     size_t macLength = gcry_mac_get_algo_maclen(*macAlgorithm);
76     if (lengthInBytes > macLength * 255)
77         return std::nullopt;
78
79     PAL::GCrypt::Handle<gcry_mac_hd_t> handle;
80     gcry_error_t error = gcry_mac_open(&handle, *macAlgorithm, 0, nullptr);
81     if (error != GPG_ERR_NO_ERROR) {
82         PAL::GCrypt::logError(error);
83         return std::nullopt;
84     }
85
86     // Step 1 -- Extract. A pseudo-random key is generated with the specified algorithm
87     // for the given salt value (used as a key) and the 'input keying material'.
88     Vector<uint8_t> pseudoRandomKey(macLength);
89     {
90         // If the salt vector is empty, a zeroed-out key of macLength size should be used.
91         if (salt.isEmpty()) {
92             Vector<uint8_t> zeroedKey(macLength, 0);
93             error = gcry_mac_setkey(handle, zeroedKey.data(), zeroedKey.size());
94         } else
95             error = gcry_mac_setkey(handle, salt.data(), salt.size());
96         if (error != GPG_ERR_NO_ERROR) {
97             PAL::GCrypt::logError(error);
98             return std::nullopt;
99         }
100
101         error = gcry_mac_write(handle, key.data(), key.size());
102         if (error != GPG_ERR_NO_ERROR) {
103             PAL::GCrypt::logError(error);
104             return std::nullopt;
105         }
106
107         size_t pseudoRandomKeySize = pseudoRandomKey.size();
108         error = gcry_mac_read(handle, pseudoRandomKey.data(), &pseudoRandomKeySize);
109         if (error != GPG_ERR_NO_ERROR) {
110             PAL::GCrypt::logError(error);
111             return std::nullopt;
112         }
113
114         // Something went wrong if libgcrypt didn't write out the proper amount of data.
115         if (pseudoRandomKeySize != macLength)
116             return std::nullopt;
117     }
118
119     // Step #2 -- Expand.
120     Vector<uint8_t> output;
121     {
122         // Deduce the number of needed iterations to retrieve the necessary amount of data.
123         size_t numIterations = (lengthInBytes + macLength) / macLength;
124         // Block from the previous iteration is used in the current one, except
125         // in the first iteration when it's empty.
126         Vector<uint8_t> lastBlock(macLength);
127
128         for (size_t i = 0; i < numIterations; ++i) {
129             error = gcry_mac_reset(handle);
130             if (error != GPG_ERR_NO_ERROR) {
131                 PAL::GCrypt::logError(error);
132                 return std::nullopt;
133             }
134
135             error = gcry_mac_setkey(handle, pseudoRandomKey.data(), pseudoRandomKey.size());
136             if (error != GPG_ERR_NO_ERROR) {
137                 PAL::GCrypt::logError(error);
138                 return std::nullopt;
139             }
140
141             // T(0) = empty string (zero length) -- i.e. empty lastBlock
142             // T(i) = HMAC-Hash(PRK, T(i-1) | info | hex(i)) -- | represents concatenation
143             Vector<uint8_t> blockData;
144             if (i)
145                 blockData.appendVector(lastBlock);
146             blockData.appendVector(info);
147             blockData.append(i + 1);
148
149             error = gcry_mac_write(handle, blockData.data(), blockData.size());
150             if (error != GPG_ERR_NO_ERROR) {
151                 PAL::GCrypt::logError(error);
152                 return std::nullopt;
153             }
154
155             size_t blockSize = lastBlock.size();
156             error = gcry_mac_read(handle, lastBlock.data(), &blockSize);
157             if (error != GPG_ERR_NO_ERROR) {
158                 PAL::GCrypt::logError(error);
159                 return std::nullopt;
160             }
161
162             // Something went wrong if libgcrypt didn't write out the proper amount of data.
163             if (blockSize != lastBlock.size())
164                 return std::nullopt;
165
166             // Append the current block data to the output vector.
167             output.appendVector(lastBlock);
168         }
169     }
170
171     // Clip output vector to the requested size.
172     output.resize(lengthInBytes);
173     return output;
174 }
175
176 void CryptoAlgorithmHKDF::platformDeriveBits(std::unique_ptr<CryptoAlgorithmParameters>&& parameters, Ref<CryptoKey>&& baseKey, size_t length, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
177 {
178     context.ref();
179     workQueue.dispatch(
180         [parameters = WTFMove(parameters), baseKey = WTFMove(baseKey), length, callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
181             auto& hkdfParameters = downcast<CryptoAlgorithmHkdfParams>(*parameters);
182             auto& rawKey = downcast<CryptoKeyRaw>(baseKey.get());
183
184             auto output = gcryptDeriveBits(rawKey.key(), hkdfParameters.saltVector(), hkdfParameters.infoVector(), length / 8, hkdfParameters.hashIdentifier);
185             if (!output) {
186                 // We should only dereference callbacks after being back to the Document/Worker threads.
187                 context.postTask(
188                     [callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
189                         exceptionCallback(OperationError);
190                         context.deref();
191                     });
192                 return;
193             }
194
195             // We should only dereference callbacks after being back to the Document/Worker threads.
196             context.postTask(
197                 [output = WTFMove(*output), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
198                     callback(output);
199                     context.deref();
200                 });
201         });
202 }
203
204 } // namespace WebCore
205
206 #endif // ENABLE(SUBTLE_CRYPTO)