Coverage Report

Created: 2025-10-31 09:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/src/crypto/crypto_kmac.cc
Line
Count
Source
1
#include "crypto/crypto_kmac.h"
2
#include "async_wrap-inl.h"
3
#include "node_internals.h"
4
#include "threadpoolwork-inl.h"
5
6
#if OPENSSL_VERSION_MAJOR >= 3
7
#include <openssl/core_names.h>
8
#include <openssl/params.h>
9
#include "crypto/crypto_keys.h"
10
#include "crypto/crypto_sig.h"
11
#include "ncrypto.h"
12
13
namespace node::crypto {
14
15
using ncrypto::EVPMacCtxPointer;
16
using ncrypto::EVPMacPointer;
17
using node::Utf8Value;
18
using v8::Boolean;
19
using v8::FunctionCallbackInfo;
20
using v8::JustVoid;
21
using v8::Local;
22
using v8::Maybe;
23
using v8::MaybeLocal;
24
using v8::Nothing;
25
using v8::Object;
26
using v8::Uint32;
27
using v8::Value;
28
29
KmacConfig::KmacConfig(KmacConfig&& other) noexcept
30
0
    : job_mode(other.job_mode),
31
0
      mode(other.mode),
32
0
      key(std::move(other.key)),
33
0
      data(std::move(other.data)),
34
0
      signature(std::move(other.signature)),
35
0
      customization(std::move(other.customization)),
36
0
      variant(other.variant),
37
0
      length(other.length) {}
38
39
0
KmacConfig& KmacConfig::operator=(KmacConfig&& other) noexcept {
40
0
  if (&other == this) return *this;
41
0
  this->~KmacConfig();
42
0
  return *new (this) KmacConfig(std::move(other));
43
0
}
44
45
0
void KmacConfig::MemoryInfo(MemoryTracker* tracker) const {
46
0
  tracker->TrackField("key", key);
47
  // If the job is sync, then the KmacConfig does not own the data.
48
0
  if (job_mode == kCryptoJobAsync) {
49
0
    tracker->TrackFieldWithSize("data", data.size());
50
0
    tracker->TrackFieldWithSize("signature", signature.size());
51
0
    tracker->TrackFieldWithSize("customization", customization.size());
52
0
  }
53
0
}
54
55
Maybe<void> KmacTraits::AdditionalConfig(
56
    CryptoJobMode mode,
57
    const FunctionCallbackInfo<Value>& args,
58
    unsigned int offset,
59
0
    KmacConfig* params) {
60
0
  Environment* env = Environment::GetCurrent(args);
61
62
0
  params->job_mode = mode;
63
64
0
  CHECK(args[offset]->IsUint32());  // SignConfiguration::Mode
65
0
  params->mode =
66
0
      static_cast<SignConfiguration::Mode>(args[offset].As<Uint32>()->Value());
67
68
0
  CHECK(args[offset + 1]->IsObject());  // Key
69
0
  KeyObjectHandle* key;
70
0
  ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 1], Nothing<void>());
71
0
  params->key = key->Data().addRef();
72
73
0
  CHECK(args[offset + 2]->IsString());  // Algorithm name
74
0
  Utf8Value algorithm_name(env->isolate(), args[offset + 2]);
75
0
  std::string_view algorithm_str = algorithm_name.ToStringView();
76
77
  // Convert string to enum and validate
78
0
  if (algorithm_str == OSSL_MAC_NAME_KMAC128) {
79
0
    params->variant = KmacVariant::KMAC128;
80
0
  } else if (algorithm_str == OSSL_MAC_NAME_KMAC256) {
81
0
    params->variant = KmacVariant::KMAC256;
82
0
  } else {
83
0
    UNREACHABLE();
84
0
  }
85
86
  // Customization string (may be empty or undefined).
87
0
  if (!args[offset + 3]->IsUndefined()) {
88
0
    ArrayBufferOrViewContents<char> customization(args[offset + 3]);
89
0
    if (!customization.CheckSizeInt32()) [[unlikely]] {
90
0
      THROW_ERR_OUT_OF_RANGE(env, "customization is too big");
91
0
      return Nothing<void>();
92
0
    }
93
0
    params->customization = mode == kCryptoJobAsync
94
0
                                ? customization.ToCopy()
95
0
                                : customization.ToByteSource();
96
0
  }
97
  // If undefined, params->customization remains uninitialized (size 0).
98
99
0
  CHECK(args[offset + 4]->IsUint32());  // Length
100
0
  params->length = args[offset + 4].As<Uint32>()->Value();
101
102
0
  ArrayBufferOrViewContents<char> data(args[offset + 5]);
103
0
  if (!data.CheckSizeInt32()) [[unlikely]] {
104
0
    THROW_ERR_OUT_OF_RANGE(env, "data is too big");
105
0
    return Nothing<void>();
106
0
  }
107
0
  params->data = mode == kCryptoJobAsync ? data.ToCopy() : data.ToByteSource();
108
109
0
  if (!args[offset + 6]->IsUndefined()) {
110
0
    ArrayBufferOrViewContents<char> signature(args[offset + 6]);
111
0
    if (!signature.CheckSizeInt32()) [[unlikely]] {
112
0
      THROW_ERR_OUT_OF_RANGE(env, "signature is too big");
113
0
      return Nothing<void>();
114
0
    }
115
0
    params->signature =
116
0
        mode == kCryptoJobAsync ? signature.ToCopy() : signature.ToByteSource();
117
0
  }
118
119
0
  return JustVoid();
120
0
}
121
122
bool KmacTraits::DeriveBits(Environment* env,
123
                            const KmacConfig& params,
124
                            ByteSource* out,
125
0
                            CryptoJobMode mode) {
126
0
  if (params.length == 0) {
127
0
    *out = ByteSource();
128
0
    return true;
129
0
  }
130
131
  // Get the key data.
132
0
  const void* key_data = params.key.GetSymmetricKey();
133
0
  size_t key_size = params.key.GetSymmetricKeySize();
134
135
0
  if (key_size == 0) {
136
0
    return false;
137
0
  }
138
139
  // Fetch the KMAC algorithm
140
0
  auto mac = EVPMacPointer::Fetch((params.variant == KmacVariant::KMAC128)
141
0
                                      ? OSSL_MAC_NAME_KMAC128
142
0
                                      : OSSL_MAC_NAME_KMAC256);
143
0
  if (!mac) {
144
0
    return false;
145
0
  }
146
147
  // Create MAC context
148
0
  auto mac_ctx = EVPMacCtxPointer::New(mac.get());
149
0
  if (!mac_ctx) {
150
0
    return false;
151
0
  }
152
153
  // Set up parameters.
154
0
  OSSL_PARAM params_array[3];  // Max 3: size + customization + end
155
0
  size_t params_count = 0;
156
157
  // Set output length (always required for KMAC).
158
0
  size_t outlen = params.length;
159
0
  params_array[params_count++] =
160
0
      OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen);
161
162
  // Set customization if provided.
163
0
  if (params.customization.size() > 0) {
164
0
    params_array[params_count++] = OSSL_PARAM_construct_octet_string(
165
0
        OSSL_MAC_PARAM_CUSTOM,
166
0
        const_cast<void*>(params.customization.data()),
167
0
        params.customization.size());
168
0
  }
169
170
0
  params_array[params_count] = OSSL_PARAM_construct_end();
171
172
  // Initialize the MAC context.
173
0
  if (!mac_ctx.init(ncrypto::Buffer<const void>(key_data, key_size),
174
0
                    params_array)) {
175
0
    return false;
176
0
  }
177
178
  // Update with data.
179
0
  if (!mac_ctx.update(ncrypto::Buffer<const void>(params.data.data(),
180
0
                                                  params.data.size()))) {
181
0
    return false;
182
0
  }
183
184
  // Finalize and get the result.
185
0
  auto result = mac_ctx.final(params.length);
186
0
  if (!result) {
187
0
    return false;
188
0
  }
189
190
0
  auto buffer = result.release();
191
0
  *out = ByteSource::Allocated(buffer.data, buffer.len);
192
0
  return true;
193
0
}
194
195
MaybeLocal<Value> KmacTraits::EncodeOutput(Environment* env,
196
                                           const KmacConfig& params,
197
0
                                           ByteSource* out) {
198
0
  switch (params.mode) {
199
0
    case SignConfiguration::Mode::Sign:
200
0
      return out->ToArrayBuffer(env);
201
0
    case SignConfiguration::Mode::Verify:
202
0
      return Boolean::New(
203
0
          env->isolate(),
204
0
          out->size() > 0 && out->size() == params.signature.size() &&
205
0
              memcmp(out->data(), params.signature.data(), out->size()) == 0);
206
0
  }
207
0
  UNREACHABLE();
208
0
}
209
210
0
void Kmac::Initialize(Environment* env, Local<Object> target) {
211
0
  KmacJob::Initialize(env, target);
212
0
}
213
214
0
void Kmac::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
215
0
  KmacJob::RegisterExternalReferences(registry);
216
0
}
217
218
}  // namespace node::crypto
219
220
#endif