[GTK] Support latest SubtleCrypto HMAC APIs
[WebKit-https.git] / Source / WebCore / crypto / gcrypt / CryptoAlgorithmHMACGCrypt.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  * Copyright (C) 2016 SoftAtHome
4  * Copyright (C) 2016 Apple Inc.
5  * Copyright (C) 2016 Yusuke Suzuki <utatane.tea@gmail.com>.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
26  * THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "config.h"
30 #include "CryptoAlgorithmHMAC.h"
31
32 #if ENABLE(SUBTLE_CRYPTO)
33
34 #include "CryptoAlgorithmHmacParamsDeprecated.h"
35 #include "CryptoKeyHMAC.h"
36 #include "ExceptionCode.h"
37 #include "ScriptExecutionContext.h"
38 #include <gcrypt.h>
39 #include <wtf/CryptographicUtilities.h>
40
41 namespace WebCore {
42
43 static int getGCryptDigestAlgorithm(CryptoAlgorithmIdentifier hashFunction)
44 {
45     switch (hashFunction) {
46     case CryptoAlgorithmIdentifier::SHA_1:
47         return GCRY_MAC_HMAC_SHA1;
48     case CryptoAlgorithmIdentifier::SHA_224:
49         return GCRY_MAC_HMAC_SHA224;
50     case CryptoAlgorithmIdentifier::SHA_256:
51         return GCRY_MAC_HMAC_SHA256;
52     case CryptoAlgorithmIdentifier::SHA_384:
53         return GCRY_MAC_HMAC_SHA384;
54     case CryptoAlgorithmIdentifier::SHA_512:
55         return GCRY_MAC_HMAC_SHA512;
56     default:
57         return GCRY_MAC_NONE;
58     }
59 }
60
61 static std::optional<Vector<uint8_t>> calculateSignature(int algorithm, const Vector<uint8_t>& key, const uint8_t* data, size_t dataLength)
62 {
63     size_t digestLength = gcry_mac_get_algo_maclen(algorithm);
64     const void* keyData = key.data() ? key.data() : reinterpret_cast<const uint8_t*>("");
65
66     bool result = false;
67     Vector<uint8_t> signature;
68
69     gcry_mac_hd_t hd;
70     gcry_error_t err;
71
72     err = gcry_mac_open(&hd, algorithm, 0, nullptr);
73     if (err)
74         goto cleanup;
75
76     err = gcry_mac_setkey(hd, keyData, key.size());
77     if (err)
78         goto cleanup;
79
80     err = gcry_mac_write(hd, data, dataLength);
81     if (err)
82         goto cleanup;
83
84     signature.resize(digestLength);
85     err = gcry_mac_read(hd, signature.data(), &digestLength);
86     if (err)
87         goto cleanup;
88
89     signature.resize(digestLength);
90     result = true;
91
92 cleanup:
93     if (hd)
94         gcry_mac_close(hd);
95
96     if (!result)
97         return std::nullopt;
98
99     return WTFMove(signature);
100 }
101
102 static std::optional<Vector<uint8_t>> calculateSignature(int algorithm, const Vector<uint8_t>& key, const CryptoOperationData& data)
103 {
104     return calculateSignature(algorithm, key, data.first, data.second);
105 }
106
107 void CryptoAlgorithmHMAC::platformSign(Ref<CryptoKey>&& key, Vector<uint8_t>&& data, VectorCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
108 {
109     context.ref();
110     workQueue.dispatch([key = WTFMove(key), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
111         auto& hmacKey = downcast<CryptoKeyHMAC>(key.get());
112         auto algorithm = getGCryptDigestAlgorithm(hmacKey.hashAlgorithmIdentifier());
113         if (algorithm != GCRY_MAC_NONE) {
114             auto result = calculateSignature(algorithm, hmacKey.key(), data.data(), data.size());
115             if (result) {
116                 // We should only dereference callbacks after being back to the Document/Worker threads.
117                 context.postTask([callback = WTFMove(callback), result = WTFMove(*result), exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
118                     callback(result);
119                     context.deref();
120                 });
121                 return;
122             }
123         }
124         // We should only dereference callbacks after being back to the Document/Worker threads.
125         context.postTask([exceptionCallback = WTFMove(exceptionCallback), callback = WTFMove(callback)](ScriptExecutionContext& context) {
126             exceptionCallback(OperationError);
127             context.deref();
128         });
129     });
130 }
131
132 void CryptoAlgorithmHMAC::platformVerify(Ref<CryptoKey>&& key, Vector<uint8_t>&& signature, Vector<uint8_t>&& data, BoolCallback&& callback, ExceptionCallback&& exceptionCallback, ScriptExecutionContext& context, WorkQueue& workQueue)
133 {
134     context.ref();
135     workQueue.dispatch([key = WTFMove(key), signature = WTFMove(signature), data = WTFMove(data), callback = WTFMove(callback), exceptionCallback = WTFMove(exceptionCallback), &context]() mutable {
136         auto& hmacKey = downcast<CryptoKeyHMAC>(key.get());
137         auto algorithm = getGCryptDigestAlgorithm(hmacKey.hashAlgorithmIdentifier());
138         if (algorithm != GCRY_MAC_NONE) {
139             auto expectedSignature = calculateSignature(algorithm, hmacKey.key(), data.data(), data.size());
140             if (expectedSignature) {
141                 // Using a constant time comparison to prevent timing attacks.
142                 bool result = signature.size() == expectedSignature->size() && !constantTimeMemcmp(expectedSignature->data(), signature.data(), expectedSignature->size());
143                 // We should only dereference callbacks after being back to the Document/Worker threads.
144                 context.postTask([callback = WTFMove(callback), result, exceptionCallback = WTFMove(exceptionCallback)](ScriptExecutionContext& context) {
145                     callback(result);
146                     context.deref();
147                 });
148                 return;
149             }
150         }
151         // We should only dereference callbacks after being back to the Document/Worker threads.
152         context.postTask([exceptionCallback = WTFMove(exceptionCallback), callback = WTFMove(callback)](ScriptExecutionContext& context) {
153             exceptionCallback(OperationError);
154             context.deref();
155         });
156     });
157 }
158
159 ExceptionOr<void> CryptoAlgorithmHMAC::platformSign(const CryptoAlgorithmHmacParamsDeprecated& parameters, const CryptoKeyHMAC& key, const CryptoOperationData& data, VectorCallback&& callback, VoidCallback&& failureCallback)
160 {
161     int algorithm = getGCryptDigestAlgorithm(parameters.hash);
162     if (algorithm == GCRY_MAC_NONE)
163         return Exception { NOT_SUPPORTED_ERR };
164
165     auto signature = calculateSignature(algorithm, key.key(), data);
166     if (signature)
167         callback(*signature);
168     else
169         failureCallback();
170     return { };
171 }
172
173 ExceptionOr<void> CryptoAlgorithmHMAC::platformVerify(const CryptoAlgorithmHmacParamsDeprecated& parameters, const CryptoKeyHMAC& key, const CryptoOperationData& expectedSignature, const CryptoOperationData& data, BoolCallback&& callback, VoidCallback&& failureCallback)
174 {
175     int algorithm = getGCryptDigestAlgorithm(parameters.hash);
176     if (algorithm == GCRY_MAC_NONE)
177         return Exception { NOT_SUPPORTED_ERR };
178
179     auto signature = calculateSignature(algorithm, key.key(), data);
180     if (!signature) {
181         failureCallback();
182         return { };
183     }
184
185     // Using a constant time comparison to prevent timing attacks.
186     bool result = signature.value().size() == expectedSignature.second && !constantTimeMemcmp(signature.value().data(), expectedSignature.first, signature.value().size());
187
188     callback(result);
189     return { };
190 }
191
192 }
193
194 #endif // ENABLE(SUBTLE_CRYPTO)