GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: crypto/crypto_hkdf.cc Lines: 69 88 78.4 %
Date: 2022-08-14 04:19:53 Branches: 45 82 54.9 %

Line Branch Exec Source
1
#include "crypto/crypto_hkdf.h"
2
#include "async_wrap-inl.h"
3
#include "base_object-inl.h"
4
#include "crypto/crypto_keys.h"
5
#include "env-inl.h"
6
#include "memory_tracker-inl.h"
7
#include "threadpoolwork-inl.h"
8
#include "v8.h"
9
10
namespace node {
11
12
using v8::FunctionCallbackInfo;
13
using v8::Just;
14
using v8::Maybe;
15
using v8::Nothing;
16
using v8::Uint32;
17
using v8::Value;
18
19
namespace crypto {
20
1284
HKDFConfig::HKDFConfig(HKDFConfig&& other) noexcept
21
1284
    : mode(other.mode),
22
1284
      length(other.length),
23
1284
      digest(other.digest),
24
1284
      key(other.key),
25
1284
      salt(std::move(other.salt)),
26
1284
      info(std::move(other.info)) {}
27
28
HKDFConfig& HKDFConfig::operator=(HKDFConfig&& other) noexcept {
29
  if (&other == this) return *this;
30
  this->~HKDFConfig();
31
  return *new (this) HKDFConfig(std::move(other));
32
}
33
34
1284
Maybe<bool> HKDFTraits::EncodeOutput(
35
    Environment* env,
36
    const HKDFConfig& params,
37
    ByteSource* out,
38
    v8::Local<v8::Value>* result) {
39
2568
  *result = out->ToArrayBuffer(env);
40
1284
  return Just(!result->IsEmpty());
41
}
42
43
1288
Maybe<bool> HKDFTraits::AdditionalConfig(
44
    CryptoJobMode mode,
45
    const FunctionCallbackInfo<Value>& args,
46
    unsigned int offset,
47
    HKDFConfig* params) {
48
1288
  Environment* env = Environment::GetCurrent(args);
49
50
1288
  params->mode = mode;
51
52

3864
  CHECK(args[offset]->IsString());  // Hash
53

2576
  CHECK(args[offset + 1]->IsObject());  // Key
54

2576
  CHECK(IsAnyByteSource(args[offset + 2]));  // Salt
55

2576
  CHECK(IsAnyByteSource(args[offset + 3]));  // Info
56

2576
  CHECK(args[offset + 4]->IsUint32());  // Length
57
58
3864
  Utf8Value hash(env->isolate(), args[offset]);
59
1288
  params->digest = EVP_get_digestbyname(*hash);
60
1288
  if (params->digest == nullptr) {
61
2
    THROW_ERR_CRYPTO_INVALID_DIGEST(env);
62
2
    return Nothing<bool>();
63
  }
64
65
  KeyObjectHandle* key;
66

2572
  ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<bool>());
67
1286
  params->key = key->Data();
68
69
2572
  ArrayBufferOrViewContents<char> salt(args[offset + 2]);
70
2572
  ArrayBufferOrViewContents<char> info(args[offset + 3]);
71
72
1286
  if (UNLIKELY(!salt.CheckSizeInt32())) {
73
    THROW_ERR_OUT_OF_RANGE(env, "salt is too big");
74
    return Nothing<bool>();
75
  }
76
1286
  if (UNLIKELY(!info.CheckSizeInt32())) {
77
    THROW_ERR_OUT_OF_RANGE(env, "info is too big");
78
    return Nothing<bool>();
79
  }
80
81
  params->salt = mode == kCryptoJobAsync
82
2572
      ? salt.ToCopy()
83
1286
      : salt.ToByteSource();
84
85
  params->info = mode == kCryptoJobAsync
86
2572
      ? info.ToCopy()
87
1286
      : info.ToByteSource();
88
89
3858
  params->length = args[offset + 4].As<Uint32>()->Value();
90
1286
  size_t max_length = EVP_MD_size(params->digest) * kMaxDigestMultiplier;
91
1286
  if (params->length > max_length) {
92
2
    THROW_ERR_CRYPTO_INVALID_KEYLEN(env);
93
2
    return Nothing<bool>();
94
  }
95
96
1284
  return Just(true);
97
}
98
99
1284
bool HKDFTraits::DeriveBits(
100
    Environment* env,
101
    const HKDFConfig& params,
102
    ByteSource* out) {
103
  EVPKeyCtxPointer ctx =
104
2568
      EVPKeyCtxPointer(EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr));
105

3852
  if (!ctx || !EVP_PKEY_derive_init(ctx.get()) ||
106

3852
      !EVP_PKEY_CTX_set_hkdf_md(ctx.get(), params.digest) ||
107
1284
      !EVP_PKEY_CTX_add1_hkdf_info(
108
1284
          ctx.get(), params.info.data<unsigned char>(), params.info.size())) {
109
    return false;
110
  }
111
112
  // TODO(panva): Once support for OpenSSL 1.1.1 is dropped the whole
113
  // of HKDFTraits::DeriveBits can be refactored to use
114
  // EVP_KDF which does handle zero length key.
115
1284
  if (params.key->GetSymmetricKeySize() != 0) {
116
988
    if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(),
117
988
                                EVP_PKEY_HKDEF_MODE_EXTRACT_AND_EXPAND) ||
118
988
        !EVP_PKEY_CTX_set1_hkdf_salt(
119

1976
            ctx.get(), params.salt.data<unsigned char>(), params.salt.size()) ||
120
988
        !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(),
121
                                    reinterpret_cast<const unsigned char*>(
122
988
                                        params.key->GetSymmetricKey()),
123
988
                                    params.key->GetSymmetricKeySize())) {
124
      return false;
125
    }
126
  } else {
127
    // Workaround for EVP_PKEY_derive HKDF not handling zero length keys.
128
    unsigned char temp_key[EVP_MAX_MD_SIZE];
129
296
    unsigned int len = sizeof(temp_key);
130
296
    if (params.salt.size() != 0) {
131
146
      if (HMAC(params.digest,
132
               params.salt.data(),
133
146
               params.salt.size(),
134
               nullptr,
135
               0,
136
               temp_key,
137
146
               &len) == nullptr) {
138
        return false;
139
      }
140
    } else {
141
150
      char salt[EVP_MAX_MD_SIZE] = {0};
142
150
      if (HMAC(params.digest,
143
               salt,
144
150
               EVP_MD_size(params.digest),
145
               nullptr,
146
               0,
147
               temp_key,
148
150
               &len) == nullptr) {
149
        return false;
150
      }
151
    }
152

592
    if (!EVP_PKEY_CTX_hkdf_mode(ctx.get(), EVP_PKEY_HKDEF_MODE_EXPAND_ONLY) ||
153
296
        !EVP_PKEY_CTX_set1_hkdf_key(ctx.get(), temp_key, len)) {
154
      return false;
155
    }
156
  }
157
158
1284
  size_t length = params.length;
159
2568
  ByteSource::Builder buf(length);
160
1284
  if (EVP_PKEY_derive(ctx.get(), buf.data<unsigned char>(), &length) <= 0)
161
    return false;
162
163
1284
  *out = std::move(buf).release();
164
1284
  return true;
165
}
166
167
void HKDFConfig::MemoryInfo(MemoryTracker* tracker) const {
168
  tracker->TrackField("key", key);
169
  // If the job is sync, then the HKDFConfig does not own the data
170
  if (mode == kCryptoJobAsync) {
171
    tracker->TrackFieldWithSize("salt", salt.size());
172
    tracker->TrackFieldWithSize("info", info.size());
173
  }
174
}
175
176
}  // namespace crypto
177
}  // namespace node