Source/WebCore/ChangeLog

 12017-04-20 Zan Dobersek <zdobersek@igalia.com>
 2
 3 [GCrypt] HKDF bit derivation support
 4 https://bugs.webkit.org/show_bug.cgi?id=171074
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Implement bit derivation support for the HKDF algorithm for configurations
 9 that use libgcrypt.
 10
 11 libgcrypt doesn't provide HKDF support out of the box, so we have to
 12 implement the two steps manually. In the first one, we retrieve the
 13 pseudo-random key by using the specified MAC algorithm with the salt data
 14 as the key and the key data as the input keying material.
 15
 16 In the expand step, we do the required amount of iterations to derive
 17 a sufficient amount of data, using the same MAC algorithm with the
 18 pseudo-random key from the previous step on the data we compose from the
 19 previous block data, the info data, and the current iteration value. The
 20 resulting blocks are appended together until they can be clipped to the
 21 desired output length.
 22
 23 * crypto/gcrypt/CryptoAlgorithmHKDFGCrypt.cpp:
 24 (WebCore::macAlgorithmForHashFunction):
 25 (WebCore::gcryptDeriveBits):
 26 (WebCore::CryptoAlgorithmHKDF::platformDeriveBits):
 27
1282017-04-20 Youenn Fablet <youenn@apple.com>
229
330 RTCPeerConnection is stopping its backend twice sometimes

Source/WebCore/crypto/gcrypt/CryptoAlgorithmHKDFGCrypt.cpp

11/*
22 * Copyright (C) 2017 Apple Inc. All rights reserved.
 3 * Copyright (C) 2017 Metrological Group B.V.
 4 * Copyright (C) 2017 Igalia S.L.
35 *
46 * Redistribution and use in source and binary forms, with or without
57 * modification, are permitted provided that the following conditions

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