1 #include "crypto/crypto_hmac.h"
2 #include "async_wrap-inl.h"
3 #include "base_object-inl.h"
4 #include "crypto/crypto_keys.h"
5 #include "crypto/crypto_sig.h"
6 #include "crypto/crypto_util.h"
7 #include "env-inl.h"
8 #include "memory_tracker-inl.h"
9 #include "node_buffer.h"
10 #include "string_bytes.h"
11 #include "threadpoolwork-inl.h"
12 #include "v8.h"
13
14 namespace node {
15
16 using v8::Boolean;
17 using v8::FunctionCallbackInfo;
18 using v8::FunctionTemplate;
19 using v8::HandleScope;
20 using v8::Isolate;
21 using v8::Just;
22 using v8::Local;
23 using v8::Maybe;
24 using v8::MaybeLocal;
25 using v8::Nothing;
26 using v8::Object;
27 using v8::Uint32;
28 using v8::Value;
29
30 namespace crypto {
Hmac(Environment* env, Local<Object> wrap)31 Hmac::Hmac(Environment* env, Local<Object> wrap)
32 : BaseObject(env, wrap),
33 ctx_(nullptr) {
34 MakeWeak();
35 }
36
MemoryInfo(MemoryTracker* tracker) const37 void Hmac::MemoryInfo(MemoryTracker* tracker) const {
38 tracker->TrackFieldWithSize("context", ctx_ ? kSizeOf_HMAC_CTX : 0);
39 }
40
Initialize(Environment* env, Local<Object> target)41 void Hmac::Initialize(Environment* env, Local<Object> target) {
42 Isolate* isolate = env->isolate();
43 Local<FunctionTemplate> t = NewFunctionTemplate(isolate, New);
44
45 t->InstanceTemplate()->SetInternalFieldCount(
46 Hmac::kInternalFieldCount);
47 t->Inherit(BaseObject::GetConstructorTemplate(env));
48
49 SetProtoMethod(isolate, t, "init", HmacInit);
50 SetProtoMethod(isolate, t, "update", HmacUpdate);
51 SetProtoMethod(isolate, t, "digest", HmacDigest);
52
53 SetConstructorFunction(env->context(), target, "Hmac", t);
54
55 HmacJob::Initialize(env, target);
56 }
57
RegisterExternalReferences(ExternalReferenceRegistry* registry)58 void Hmac::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
59 registry->Register(New);
60 registry->Register(HmacInit);
61 registry->Register(HmacUpdate);
62 registry->Register(HmacDigest);
63 HmacJob::RegisterExternalReferences(registry);
64 }
65
New(const FunctionCallbackInfo<Value>& args)66 void Hmac::New(const FunctionCallbackInfo<Value>& args) {
67 Environment* env = Environment::GetCurrent(args);
68 new Hmac(env, args.This());
69 }
70
HmacInit(const char* hash_type, const char* key, int key_len)71 void Hmac::HmacInit(const char* hash_type, const char* key, int key_len) {
72 HandleScope scope(env()->isolate());
73
74 const EVP_MD* md = EVP_get_digestbyname(hash_type);
75 if (md == nullptr)
76 return THROW_ERR_CRYPTO_INVALID_DIGEST(
77 env(), "Invalid digest: %s", hash_type);
78 if (key_len == 0) {
79 key = "";
80 }
81 ctx_.reset(HMAC_CTX_new());
82 if (!ctx_ || !HMAC_Init_ex(ctx_.get(), key, key_len, md, nullptr)) {
83 ctx_.reset();
84 return ThrowCryptoError(env(), ERR_get_error());
85 }
86 }
87
HmacInit(const FunctionCallbackInfo<Value>& args)88 void Hmac::HmacInit(const FunctionCallbackInfo<Value>& args) {
89 Hmac* hmac;
90 ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
91 Environment* env = hmac->env();
92
93 const node::Utf8Value hash_type(env->isolate(), args[0]);
94 ByteSource key = ByteSource::FromSecretKeyBytes(env, args[1]);
95 hmac->HmacInit(*hash_type, key.data<char>(), key.size());
96 }
97
HmacUpdate(const char* data, size_t len)98 bool Hmac::HmacUpdate(const char* data, size_t len) {
99 return ctx_ && HMAC_Update(ctx_.get(),
100 reinterpret_cast<const unsigned char*>(data),
101 len) == 1;
102 }
103
HmacUpdate(const FunctionCallbackInfo<Value>& args)104 void Hmac::HmacUpdate(const FunctionCallbackInfo<Value>& args) {
105 Decode<Hmac>(args, [](Hmac* hmac, const FunctionCallbackInfo<Value>& args,
106 const char* data, size_t size) {
107 Environment* env = Environment::GetCurrent(args);
108 if (UNLIKELY(size > INT_MAX))
109 return THROW_ERR_OUT_OF_RANGE(env, "data is too long");
110 bool r = hmac->HmacUpdate(data, size);
111 args.GetReturnValue().Set(r);
112 });
113 }
114
HmacDigest(const FunctionCallbackInfo<Value>& args)115 void Hmac::HmacDigest(const FunctionCallbackInfo<Value>& args) {
116 Environment* env = Environment::GetCurrent(args);
117
118 Hmac* hmac;
119 ASSIGN_OR_RETURN_UNWRAP(&hmac, args.Holder());
120
121 enum encoding encoding = BUFFER;
122 if (args.Length() >= 1) {
123 encoding = ParseEncoding(env->isolate(), args[0], BUFFER);
124 }
125
126 unsigned char md_value[EVP_MAX_MD_SIZE];
127 unsigned int md_len = 0;
128
129 if (hmac->ctx_) {
130 bool ok = HMAC_Final(hmac->ctx_.get(), md_value, &md_len);
131 hmac->ctx_.reset();
132 if (!ok) {
133 return ThrowCryptoError(env, ERR_get_error(), "Failed to finalize HMAC");
134 }
135 }
136
137 Local<Value> error;
138 MaybeLocal<Value> rc =
139 StringBytes::Encode(env->isolate(),
140 reinterpret_cast<const char*>(md_value),
141 md_len,
142 encoding,
143 &error);
144 if (rc.IsEmpty()) {
145 CHECK(!error.IsEmpty());
146 env->isolate()->ThrowException(error);
147 return;
148 }
149 args.GetReturnValue().Set(rc.FromMaybe(Local<Value>()));
150 }
151
152 HmacConfig::HmacConfig(HmacConfig&& other) noexcept
153 : job_mode(other.job_mode),
154 mode(other.mode),
155 key(std::move(other.key)),
156 data(std::move(other.data)),
157 signature(std::move(other.signature)),
digest(other.digest)158 digest(other.digest) {}
159
160 HmacConfig& HmacConfig::operator=(HmacConfig&& other) noexcept {
161 if (&other == this) return *this;
162 this->~HmacConfig();
163 return *new (this) HmacConfig(std::move(other));
164 }
165
MemoryInfo(MemoryTracker* tracker) const166 void HmacConfig::MemoryInfo(MemoryTracker* tracker) const {
167 tracker->TrackField("key", key.get());
168 // If the job is sync, then the HmacConfig does not own the data
169 if (job_mode == kCryptoJobAsync) {
170 tracker->TrackFieldWithSize("data", data.size());
171 tracker->TrackFieldWithSize("signature", signature.size());
172 }
173 }
174
AdditionalConfig( CryptoJobMode mode, const FunctionCallbackInfo<Value>& args, unsigned int offset, HmacConfig* params)175 Maybe<bool> HmacTraits::AdditionalConfig(
176 CryptoJobMode mode,
177 const FunctionCallbackInfo<Value>& args,
178 unsigned int offset,
179 HmacConfig* params) {
180 Environment* env = Environment::GetCurrent(args);
181
182 params->job_mode = mode;
183
184 CHECK(args[offset]->IsUint32()); // SignConfiguration::Mode
185 params->mode =
186 static_cast<SignConfiguration::Mode>(args[offset].As<Uint32>()->Value());
187
188 CHECK(args[offset + 1]->IsString()); // Hash
189 CHECK(args[offset + 2]->IsObject()); // Key
190
191 Utf8Value digest(env->isolate(), args[offset + 1]);
192 params->digest = EVP_get_digestbyname(*digest);
193 if (params->digest == nullptr) {
194 THROW_ERR_CRYPTO_INVALID_DIGEST(env, "Invalid digest: %s", *digest);
195 return Nothing<bool>();
196 }
197
198 KeyObjectHandle* key;
199 ASSIGN_OR_RETURN_UNWRAP(&key, args[offset + 2], Nothing<bool>());
200 params->key = key->Data();
201
202 ArrayBufferOrViewContents<char> data(args[offset + 3]);
203 if (UNLIKELY(!data.CheckSizeInt32())) {
204 THROW_ERR_OUT_OF_RANGE(env, "data is too big");
205 return Nothing<bool>();
206 }
207 params->data = mode == kCryptoJobAsync
208 ? data.ToCopy()
209 : data.ToByteSource();
210
211 if (!args[offset + 4]->IsUndefined()) {
212 ArrayBufferOrViewContents<char> signature(args[offset + 4]);
213 if (UNLIKELY(!signature.CheckSizeInt32())) {
214 THROW_ERR_OUT_OF_RANGE(env, "signature is too big");
215 return Nothing<bool>();
216 }
217 params->signature = mode == kCryptoJobAsync
218 ? signature.ToCopy()
219 : signature.ToByteSource();
220 }
221
222 return Just(true);
223 }
224
DeriveBits( Environment* env, const HmacConfig& params, ByteSource* out)225 bool HmacTraits::DeriveBits(
226 Environment* env,
227 const HmacConfig& params,
228 ByteSource* out) {
229 HMACCtxPointer ctx(HMAC_CTX_new());
230
231 if (!ctx ||
232 !HMAC_Init_ex(
233 ctx.get(),
234 params.key->GetSymmetricKey(),
235 params.key->GetSymmetricKeySize(),
236 params.digest,
237 nullptr)) {
238 return false;
239 }
240
241 if (!HMAC_Update(
242 ctx.get(),
243 params.data.data<unsigned char>(),
244 params.data.size())) {
245 return false;
246 }
247
248 ByteSource::Builder buf(EVP_MAX_MD_SIZE);
249 unsigned int len;
250
251 if (!HMAC_Final(ctx.get(), buf.data<unsigned char>(), &len)) {
252 return false;
253 }
254
255 *out = std::move(buf).release(len);
256
257 return true;
258 }
259
EncodeOutput( Environment* env, const HmacConfig& params, ByteSource* out, Local<Value>* result)260 Maybe<bool> HmacTraits::EncodeOutput(
261 Environment* env,
262 const HmacConfig& params,
263 ByteSource* out,
264 Local<Value>* result) {
265 switch (params.mode) {
266 case SignConfiguration::kSign:
267 *result = out->ToArrayBuffer(env);
268 break;
269 case SignConfiguration::kVerify:
270 *result = Boolean::New(
271 env->isolate(),
272 out->size() > 0 && out->size() == params.signature.size() &&
273 memcmp(out->data(), params.signature.data(), out->size()) == 0);
274 break;
275 default:
276 UNREACHABLE();
277 }
278 return Just(!result->IsEmpty());
279 }
280
281 } // namespace crypto
282 } // namespace node
283