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