]> git.kaiwu.me - njs.git/commitdiff
Adding support for Buffer objects in crypto methods.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 22 Sep 2020 18:42:21 +0000 (18:42 +0000)
committerDmitry Volyntsev <xeioex@nginx.com>
Tue, 22 Sep 2020 18:42:21 +0000 (18:42 +0000)
src/njs_buffer.c
src/njs_buffer.h
src/njs_crypto.c
src/test/njs_unit_test.c

index d71fa1dd2a9f541da4c2d750c05a8a342cb63be9..0996f6563f4224c1c82e659caa863e97b81dd550 100644 (file)
     ((size << 2) | (sign << 1) | little)
 
 
-typedef njs_int_t (*njs_buffer_encode)(njs_vm_t *vm, njs_value_t *value,
-    const njs_str_t *src);
-typedef size_t (*njs_buffer_encode_length)(const njs_str_t *src,
-                                           size_t *out_size);
-
-typedef struct {
-    njs_str_t                 name;
-    njs_buffer_encode         encode;
-    njs_buffer_encode         decode;
-    njs_buffer_encode_length  decode_length;
-} njs_buffer_encoding_t;
-
-
 static njs_buffer_encoding_t  njs_buffer_encodings[] =
 {
     {
@@ -83,18 +70,15 @@ static njs_int_t njs_buffer_write_string(njs_vm_t *vm, njs_value_t *value,
     njs_typed_array_t *array, const njs_buffer_encoding_t *encoding,
     uint64_t offset, uint64_t length);
 static njs_int_t njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array,
-    njs_value_t *value, const njs_buffer_encoding_t *encoding, uint64_t offset,
+    const njs_value_t *fill, const njs_value_t *encoding, uint64_t offset,
     uint64_t end);
-static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, njs_value_t *value,
+static njs_int_t njs_buffer_fill_string(njs_vm_t *vm, const njs_value_t *value,
     njs_typed_array_t *array, const njs_buffer_encoding_t *encoding,
     uint8_t *start, uint8_t *end);
-static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm, njs_value_t *value,
-    njs_typed_array_t *array, uint8_t *start, uint8_t *end);
-static const njs_buffer_encoding_t *njs_buffer_encoding(njs_vm_t *vm,
-    njs_value_t *value);
-static njs_int_t njs_buffer_decode_string(njs_vm_t *vm, njs_value_t *value,
-    njs_value_t *dst, const njs_buffer_encoding_t *encoding);
-static void njs_buffer_decode_destroy(njs_vm_t *vm, njs_value_t *source,
+static njs_int_t njs_buffer_fill_typed_array(njs_vm_t *vm,
+    const njs_value_t *value, njs_typed_array_t *array, uint8_t *start,
+    uint8_t *end);
+static void njs_buffer_decode_destroy(njs_vm_t *vm, const njs_value_t *source,
     njs_value_t *target);
 
 
@@ -149,8 +133,8 @@ njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
 }
 
 
-static njs_typed_array_t *
-njs_buffer_alloc_array(njs_vm_t *vm, size_t size, njs_bool_t zeroing)
+njs_typed_array_t *
+njs_buffer_alloc(njs_vm_t *vm, size_t size, njs_bool_t zeroing)
 {
     njs_value_t        value;
     njs_typed_array_t  *array;
@@ -169,6 +153,25 @@ njs_buffer_alloc_array(njs_vm_t *vm, size_t size, njs_bool_t zeroing)
 }
 
 
+njs_int_t
+njs_buffer_new(njs_vm_t *vm, njs_value_t *value, const u_char *start,
+    uint32_t size)
+{
+    njs_typed_array_t  *buffer;
+
+    buffer = njs_buffer_alloc(vm, size, 0);
+    if (njs_slow_path(buffer == NULL)) {
+        return NJS_ERROR;
+    }
+
+    memcpy(njs_typed_array_buffer(buffer)->u.u8, start, size);
+
+    njs_set_typed_array(value, buffer);
+
+    return NJS_OK;
+}
+
+
 static njs_int_t
 njs_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
@@ -180,13 +183,13 @@ njs_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 
 static njs_int_t
-njs_buffer_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+njs_buffer_alloc_safe(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t safe)
 {
-    double                       size;
-    njs_int_t                    ret;
-    njs_typed_array_t            *array;
-    const njs_buffer_encoding_t  *encoding;
+    double             size;
+    njs_int_t          ret;
+    njs_typed_array_t  *array;
+    const njs_value_t  *fill;
 
     if (njs_slow_path(!njs_is_number(njs_arg(args, nargs, 1)))) {
         njs_type_error(vm, "\"size\" argument must be of type number");
@@ -199,22 +202,15 @@ njs_buffer_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    array = njs_buffer_alloc_array(vm, size, safe || nargs <= 2);
+    array = njs_buffer_alloc(vm, size, safe || nargs <= 2);
     if (njs_slow_path(array == NULL)) {
         return NJS_ERROR;
     }
 
-    if (safe && nargs > 2) {
-        encoding = njs_buffer_utf8_encoding();
-
-        if (nargs > 3 && njs_is_string(njs_argument(args, 2))) {
-            encoding = njs_buffer_encoding(vm, njs_argument(args, 3));
-            if (njs_slow_path(encoding == NULL)) {
-                return NJS_ERROR;
-            }
-        }
+    fill = njs_arg(args, nargs, 2);
 
-        ret = njs_buffer_fill(vm, array, njs_argument(args, 2), encoding, 0,
+    if (safe && njs_is_defined(fill)) {
+        ret = njs_buffer_fill(vm, array, fill, njs_arg(args, nargs, 3), 0,
                               array->byte_length);
         if (njs_slow_path(ret != NJS_OK)) {
             return NJS_ERROR;
@@ -246,13 +242,9 @@ njs_buffer_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
                                             njs_arg(args, nargs, 3));
 
     case NJS_STRING:
-        encoding = njs_buffer_utf8_encoding();
-
-        if (nargs > 2) {
-            encoding = njs_buffer_encoding(vm, njs_argument(args, 2));
-            if (njs_slow_path(encoding == NULL)) {
-                return NJS_ERROR;
-            }
+        encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2));
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
         }
 
         return njs_buffer_from_string(vm, value, encoding);
@@ -334,7 +326,7 @@ next:
         return ret;
     }
 
-    buffer = njs_buffer_alloc_array(vm, len, 0);
+    buffer = njs_buffer_alloc(vm, len, 0);
     if (njs_slow_path(buffer == NULL)) {
         return NJS_ERROR;
     }
@@ -448,7 +440,7 @@ njs_buffer_from_typed_array(njs_vm_t *vm, njs_value_t *value)
 
     length = njs_typed_array_length(array);
 
-    buffer = njs_buffer_alloc_array(vm, length, 0);
+    buffer = njs_buffer_alloc(vm, length, 0);
     if (njs_slow_path(buffer == NULL)) {
         return NJS_ERROR;
     }
@@ -481,7 +473,7 @@ njs_buffer_from_string(njs_vm_t *vm, njs_value_t *value,
 
     njs_string_get(&dst, &str);
 
-    buffer = njs_buffer_alloc_array(vm, str.length, 0);
+    buffer = njs_buffer_alloc(vm, str.length, 0);
     if (njs_slow_path(buffer == NULL)) {
         return NJS_ERROR;
     }
@@ -812,7 +804,7 @@ njs_buffer_concat(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    buffer = njs_buffer_alloc_array(vm, len, 0);
+    buffer = njs_buffer_alloc(vm, len, 0);
     if (njs_slow_path(buffer == NULL)) {
         return NJS_ERROR;
     }
@@ -884,8 +876,11 @@ static njs_int_t
 njs_buffer_is_encoding(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    njs_set_boolean(&vm->retval,
-                    njs_buffer_encoding(vm, njs_arg(args, nargs, 1)) != NULL);
+    const njs_value_t  *value;
+
+    value = njs_arg(args, nargs, 1);
+    njs_set_boolean(&vm->retval, njs_is_defined(value)
+                                 && njs_buffer_encoding(vm, value) != NULL);
 
     return NJS_OK;
 }
@@ -1563,12 +1558,11 @@ static njs_int_t
 njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    uint64_t                     offset, end;
-    njs_int_t                    ret;
-    njs_value_t                  *this, *value, *value_offset, *value_end,
-                                 *encode;
-    njs_typed_array_t            *array;
-    const njs_buffer_encoding_t  *encoding;
+    uint64_t           offset, end;
+    njs_int_t          ret;
+    njs_value_t        *this, *value, *value_offset, *value_end,
+                       *encode;
+    njs_typed_array_t  *array;
 
     this = njs_argument(args, 0);
     if (njs_slow_path(nargs < 2)) {
@@ -1584,8 +1578,6 @@ njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     value_offset = njs_arg(args, nargs, 2);
     value_end = njs_arg(args, nargs, 3);
     encode = njs_arg(args, nargs, 4);
-    encoding = njs_buffer_utf8_encoding();
-
 
     offset = 0;
     end = array->byte_length;
@@ -1593,7 +1585,7 @@ njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     if (njs_is_defined(value_offset)) {
         if (njs_is_string(value) && njs_is_string(value_offset)) {
             encode = value_offset;
-            goto encoding;
+            goto fill;
         }
 
         ret = njs_value_to_index(vm, value_offset, &offset);
@@ -1605,7 +1597,7 @@ njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     if (njs_is_defined(value_end)) {
         if (njs_is_string(value) && njs_is_string(value_end)) {
             encode = value_end;
-            goto encoding;
+            goto fill;
         }
 
         ret = njs_value_to_index(vm, value_end, &end);
@@ -1614,21 +1606,9 @@ njs_buffer_prototype_fill(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         }
     }
 
-    if (njs_is_defined(encode)) {
-        if (njs_slow_path(!njs_is_string(encode))) {
-            njs_type_error(vm, "\"encoding\" argument must be of type string");
-            return NJS_ERROR;
-        }
-
-    encoding:
-
-        encoding = njs_buffer_encoding(vm, encode);
-        if (njs_slow_path(encoding == NULL)) {
-            return NJS_ERROR;
-        }
-    }
+fill:
 
-    ret = njs_buffer_fill(vm, array, value, encoding, offset, end);
+    ret = njs_buffer_fill(vm, array, value, encode, offset, end);
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
@@ -1642,13 +1622,14 @@ done:
 
 
 static njs_int_t
-njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, njs_value_t *value,
-    const njs_buffer_encoding_t *encoding, uint64_t offset, uint64_t end)
+njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, const njs_value_t *fill,
+    const njs_value_t *encode, uint64_t offset, uint64_t end)
 {
-    double              num;
-    uint8_t             *start, *stop;
-    njs_int_t           ret;
-    njs_array_buffer_t  *buffer;
+    double                       num;
+    uint8_t                      *start, *stop;
+    njs_int_t                    ret;
+    njs_array_buffer_t           *buffer;
+    const njs_buffer_encoding_t  *encoding;
 
     buffer = njs_typed_array_writable(vm, array);
     if (njs_slow_path(buffer == NULL)) {
@@ -1672,15 +1653,20 @@ njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, njs_value_t *value,
     start = &buffer->u.u8[array->offset + offset];
     stop = &buffer->u.u8[array->offset + end];
 
-    switch (value->type) {
+    switch (fill->type) {
     case NJS_STRING:
-        return njs_buffer_fill_string(vm, value, array, encoding, start, stop);
+        encoding = njs_buffer_encoding(vm, encode);
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
+
+        return njs_buffer_fill_string(vm, fill, array, encoding, start, stop);
 
     case NJS_TYPED_ARRAY:
-        return njs_buffer_fill_typed_array(vm, value, array, start, stop);
+        return njs_buffer_fill_typed_array(vm, fill, array, start, stop);
 
     default:
-        ret = njs_value_to_number(vm, value, &num);
+        ret = njs_value_to_number(vm, njs_value_arg(fill), &num);
         if (njs_slow_path(ret != NJS_OK)) {
             return ret;
         }
@@ -1698,7 +1684,7 @@ njs_buffer_fill(njs_vm_t *vm, njs_typed_array_t *array, njs_value_t *value,
 
 
 static njs_int_t
-njs_buffer_fill_string(njs_vm_t *vm, njs_value_t *value,
+njs_buffer_fill_string(njs_vm_t *vm, const njs_value_t *value,
     njs_typed_array_t *array, const njs_buffer_encoding_t *encoding,
     uint8_t *start, uint8_t *end)
 {
@@ -1733,7 +1719,7 @@ done:
 
 
 static njs_int_t
-njs_buffer_fill_typed_array(njs_vm_t *vm, njs_value_t *value,
+njs_buffer_fill_typed_array(njs_vm_t *vm, const njs_value_t *value,
     njs_typed_array_t *array, uint8_t *to, uint8_t *end)
 {
     size_t              byte_length;
@@ -1773,7 +1759,7 @@ njs_buffer_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
     uint64_t                     start, end;
     njs_int_t                    ret;
     njs_str_t                    str;
-    njs_value_t                  *this, *enc, *value_start, *value_end;
+    njs_value_t                  *this, *value_start, *value_end;
     njs_typed_array_t            *array;
     const njs_buffer_encoding_t  *encoding;
 
@@ -1783,7 +1769,6 @@ njs_buffer_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
         return NJS_ERROR;
     }
 
-    enc = njs_arg(args, nargs, 1);
     value_start = njs_arg(args, nargs, 2);
     value_end = njs_arg(args, nargs, 3);
 
@@ -1791,11 +1776,9 @@ njs_buffer_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
     end = array->byte_length;
     encoding = njs_buffer_utf8_encoding();
 
-    if (njs_is_defined(enc)) {
-        encoding = njs_buffer_encoding(vm, njs_argument(args, 1));
-        if (njs_slow_path(encoding == NULL)) {
-            return NJS_ERROR;
-        }
+    encoding = njs_buffer_encoding(vm,  njs_arg(args, nargs, 1));
+    if (njs_slow_path(encoding == NULL)) {
+        return NJS_ERROR;
     }
 
     if (njs_is_defined(value_start)) {
@@ -2265,40 +2248,41 @@ njs_buffer_prototype_to_json(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-static const njs_buffer_encoding_t *
-njs_buffer_encoding(njs_vm_t *vm, njs_value_t *value)
+const njs_buffer_encoding_t *
+njs_buffer_encoding(njs_vm_t *vm, const njs_value_t *value)
 {
-    njs_str_t              str;
-    njs_int_t              ret;
+    njs_str_t              name;
     njs_buffer_encoding_t  *encoding;
 
     if (njs_slow_path(!njs_is_string(value))) {
-        ret = njs_value_to_string(vm, value, value);
-        if (njs_slow_path(ret != NJS_OK)) {
+        if (njs_is_defined(value)) {
+            njs_type_error(vm, "encoding must be a string");
             return NULL;
         }
+
+        return &njs_buffer_encodings[0];
     }
 
-    njs_string_get(value, &str);
+    njs_string_get(value, &name);
 
     for (encoding = &njs_buffer_encodings[0];
          encoding->name.length != 0;
          encoding++)
     {
-        if (njs_strstr_eq(&str, &encoding->name)) {
+        if (njs_strstr_eq(&name, &encoding->name)) {
             return encoding;
         }
     }
 
-    njs_type_error(vm, "\"%V\" encoding is not supported", &str);
+    njs_type_error(vm, "\"%V\" encoding is not supported", &name);
 
     return NULL;
 }
 
 
-static njs_int_t
-njs_buffer_decode_string(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst,
-    const njs_buffer_encoding_t *encoding)
+njs_int_t
+njs_buffer_decode_string(njs_vm_t *vm, const njs_value_t *value,
+    njs_value_t *dst, const njs_buffer_encoding_t *encoding)
 {
     njs_int_t          ret;
     njs_str_t          str;
@@ -2325,7 +2309,7 @@ njs_buffer_decode_string(njs_vm_t *vm, njs_value_t *value, njs_value_t *dst,
 
 
 static void
-njs_buffer_decode_destroy(njs_vm_t *vm, njs_value_t *source,
+njs_buffer_decode_destroy(njs_vm_t *vm, const njs_value_t *source,
     njs_value_t *target)
 {
     njs_str_t  src, trg;
@@ -2833,7 +2817,7 @@ static const njs_object_prop_t  njs_buffer_constructor_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("alloc"),
-        .value = njs_native_function2(njs_buffer_alloc, 0, 1),
+        .value = njs_native_function2(njs_buffer_alloc_safe, 0, 1),
         .writable = 1,
         .configurable = 1,
     },
@@ -2841,7 +2825,7 @@ static const njs_object_prop_t  njs_buffer_constructor_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("allocUnsafe"),
-        .value = njs_native_function2(njs_buffer_alloc, 1, 0),
+        .value = njs_native_function2(njs_buffer_alloc_safe, 1, 0),
         .writable = 1,
         .configurable = 1,
     },
@@ -2849,7 +2833,7 @@ static const njs_object_prop_t  njs_buffer_constructor_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_long_string("allocUnsafeSlow"),
-        .value = njs_native_function2(njs_buffer_alloc, 1, 0),
+        .value = njs_native_function2(njs_buffer_alloc_safe, 1, 0),
         .writable = 1,
         .configurable = 1,
     },
index e9b94a685fc2df11606e6db7b0542254b67b8125..4633ebe1f434b1f1997e8627db008c31916b627a 100644 (file)
@@ -8,8 +8,30 @@
 #define _NJS_BUFFER_H_INCLUDED_
 
 
+typedef njs_int_t (*njs_buffer_encode_t)(njs_vm_t *vm, njs_value_t *value,
+    const njs_str_t *src);
+typedef size_t (*njs_buffer_encode_length_t)(const njs_str_t *src,
+                                             size_t *out_size);
+
+typedef struct {
+    njs_str_t                   name;
+    njs_buffer_encode_t         encode;
+    njs_buffer_encode_t         decode;
+    njs_buffer_encode_length_t  decode_length;
+} njs_buffer_encoding_t;
+
+
 njs_int_t njs_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
     uint32_t size);
+njs_int_t njs_buffer_new(njs_vm_t *vm, njs_value_t *value, const u_char *start,
+    uint32_t size);
+njs_typed_array_t *njs_buffer_alloc(njs_vm_t *vm, size_t size,
+    njs_bool_t zeroing);
+
+const njs_buffer_encoding_t *njs_buffer_encoding(njs_vm_t *vm,
+    const njs_value_t *value);
+njs_int_t njs_buffer_decode_string(njs_vm_t *vm, const njs_value_t *value,
+    njs_value_t *dst, const njs_buffer_encoding_t *encoding);
 
 
 extern const njs_object_type_init_t  njs_buffer_type_init;
index 71e2d0b86a232100a6eeffd5f64e267a8a333a59..9a3d3846ac991465d41270c5b29755fb53262055 100644 (file)
@@ -56,6 +56,14 @@ typedef struct {
 } njs_crypto_enc_t;
 
 
+static njs_hash_alg_t *njs_crypto_algorithm(njs_vm_t *vm,
+    const njs_value_t *value);
+static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm,
+    const njs_value_t *value);
+static njs_int_t njs_buffer_digest(njs_vm_t *vm, njs_value_t *value,
+    const njs_str_t *src);
+
+
 static njs_hash_alg_t njs_hash_algorithms[] = {
 
    {
@@ -92,8 +100,14 @@ static njs_hash_alg_t njs_hash_algorithms[] = {
 
 };
 
+
 static njs_crypto_enc_t njs_encodings[] = {
 
+   {
+     njs_str("buffer"),
+     njs_buffer_digest
+   },
+
    {
      njs_str("hex"),
      njs_string_hex
@@ -116,11 +130,6 @@ static njs_crypto_enc_t njs_encodings[] = {
 };
 
 
-static njs_hash_alg_t *njs_crypto_alg(njs_vm_t *vm, const njs_str_t *name);
-static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm,
-    const njs_str_t *name);
-
-
 static njs_object_value_t *
 njs_crypto_object_value_alloc(njs_vm_t *vm, njs_object_type_t type)
 {
@@ -152,19 +161,11 @@ static njs_int_t
 njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    njs_str_t           alg_name;
     njs_digest_t        *dgst;
     njs_hash_alg_t      *alg;
     njs_object_value_t  *hash;
 
-    if (njs_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "algorithm must be a string");
-        return NJS_ERROR;
-    }
-
-    njs_string_get(&args[1], &alg_name);
-
-    alg = njs_crypto_alg(vm, &alg_name);
+    alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
     if (njs_slow_path(alg == NULL)) {
         return NJS_ERROR;
     }
@@ -193,34 +194,79 @@ njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 static njs_int_t
 njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
+    njs_index_t tag)
 {
-    njs_str_t     data;
-    njs_value_t   *this;
-    njs_digest_t  *dgst;
-
-    if (njs_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "data must be a string");
-        return NJS_ERROR;
-    }
+    njs_str_t                    data;
+    njs_int_t                    ret;
+    njs_hmac_t                   *ctx;
+    njs_value_t                  *this, dst;
+    njs_digest_t                 *dgst;
+    njs_typed_array_t            *array;
+    const njs_value_t            *value;
+    njs_array_buffer_t           *buffer;
+    const njs_buffer_encoding_t  *encoding;
 
     this = njs_argument(args, 0);
-
-    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_CRYPTO_HASH))) {
+    if (njs_slow_path(!njs_is_object_data(this, tag))) {
         njs_type_error(vm, "\"this\" is not a hash object");
         return NJS_ERROR;
     }
 
-    njs_string_get(&args[1], &data);
+    value = njs_arg(args, nargs, 1);
 
-    dgst = njs_object_data(this);
+    switch (value->type) {
+    case NJS_STRING:
+        encoding = njs_buffer_encoding(vm, njs_arg(args, nargs, 2));
+        if (njs_slow_path(encoding == NULL)) {
+            return NJS_ERROR;
+        }
+
+        ret = njs_buffer_decode_string(vm, value, &dst, encoding);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        njs_string_get(&dst, &data);
+        break;
+
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(value);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        data.start = &buffer->u.u8[array->offset];
+        data.length = array->byte_length;
+        break;
+
+    default:
+        njs_type_error(vm, "data argument \"%s\" is not a string "
+                       "or Buffer-like object", njs_type_string(value->type));
 
-    if (njs_slow_path(dgst->alg == NULL)) {
-        njs_error(vm, "Digest already called");
         return NJS_ERROR;
     }
 
-    dgst->alg->update(&dgst->u, data.start, data.length);
+    if (tag == NJS_DATA_TAG_CRYPTO_HASH) {
+        dgst = njs_object_data(this);
+        if (njs_slow_path(dgst->alg == NULL)) {
+            njs_error(vm, "Digest already called");
+            return NJS_ERROR;
+        }
+
+        dgst->alg->update(&dgst->u, data.start, data.length);
+
+    } else {
+        ctx = njs_object_data(this);
+        if (njs_slow_path(ctx->alg == NULL)) {
+            njs_error(vm, "Digest already called");
+            return NJS_ERROR;
+        }
+
+        ctx->alg->update(&ctx->u, data.start, data.length);
+    }
 
     vm->retval = *this;
 
@@ -230,71 +276,62 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
 static njs_int_t
 njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
+    njs_index_t tag)
 {
-    u_char            digest[32], *p;
-    njs_int_t         ret;
-    njs_str_t         enc_name, str;
+    njs_str_t         str;
+    njs_hmac_t        *ctx;
     njs_value_t       *this;
     njs_digest_t      *dgst;
     njs_hash_alg_t    *alg;
     njs_crypto_enc_t  *enc;
+    u_char            hash1[32], digest[32];
 
-    if (njs_slow_path(nargs > 1 && !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "encoding must be a string");
+    this = njs_argument(args, 0);
+    if (njs_slow_path(!njs_is_object_data(this, tag))) {
+        njs_type_error(vm, "\"this\" is not a hash object");
         return NJS_ERROR;
     }
 
-    this = njs_argument(args, 0);
-
-    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_CRYPTO_HASH))) {
-        njs_type_error(vm, "\"this\" is not a hash object");
+    enc = njs_crypto_encoding(vm, njs_arg(args, nargs, 1));
+    if (njs_slow_path(enc == NULL)) {
         return NJS_ERROR;
     }
 
-    enc = NULL;
+    if (tag == NJS_DATA_TAG_CRYPTO_HASH) {
+        dgst = njs_object_data(this);
+        if (njs_slow_path(dgst->alg == NULL)) {
+            goto exception;
+        }
 
-    if (nargs > 1) {
-        njs_string_get(&args[1], &enc_name);
+        alg = dgst->alg;
+        alg->final(digest, &dgst->u);
+        dgst->alg = NULL;
 
-        enc = njs_crypto_encoding(vm, &enc_name);
-        if (njs_slow_path(enc == NULL)) {
-            return NJS_ERROR;
+    } else {
+        ctx = njs_object_data(this);
+        if (njs_slow_path(ctx->alg == NULL)) {
+            goto exception;
         }
-    }
 
-    dgst = njs_object_data(this);
+        alg = ctx->alg;
+        alg->final(hash1, &ctx->u);
 
-    if (njs_slow_path(dgst->alg == NULL)) {
-        njs_error(vm, "Digest already called");
-        return NJS_ERROR;
+        alg->init(&ctx->u);
+        alg->update(&ctx->u, ctx->opad, 64);
+        alg->update(&ctx->u, hash1, alg->size);
+        alg->final(digest, &ctx->u);
+        ctx->alg = NULL;
     }
 
-    alg = dgst->alg;
-
-    alg->final(digest, &dgst->u);
-
     str.start = digest;
     str.length = alg->size;
 
-    if (enc == NULL) {
-        p = njs_string_alloc(vm, &vm->retval, str.length, 0);
-
-        if (njs_fast_path(p != NULL)) {
-            memcpy(p, str.start, str.length);
-            ret = NJS_OK;
-
-        } else {
-            ret = NJS_ERROR;
-        }
-
-    } else {
-        ret = enc->encode(vm, &vm->retval, &str);
-    }
+    return enc->encode(vm, &vm->retval, &str);
 
-    dgst->alg = NULL;
+exception:
 
-    return ret;
+    njs_error(vm, "Digest already called");
+    return NJS_ERROR;
 }
 
 
@@ -317,7 +354,8 @@ static const njs_object_prop_t  njs_hash_prototype_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("update"),
-        .value = njs_native_function(njs_hash_prototype_update, 0),
+        .value = njs_native_function2(njs_hash_prototype_update, 0,
+                                      NJS_DATA_TAG_CRYPTO_HASH),
         .writable = 1,
         .configurable = 1,
     },
@@ -325,7 +363,8 @@ static const njs_object_prop_t  njs_hash_prototype_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("digest"),
-        .value = njs_native_function(njs_hash_prototype_digest, 0),
+        .value = njs_native_function2(njs_hash_prototype_digest, 0,
+                                      NJS_DATA_TAG_CRYPTO_HASH),
         .writable = 1,
         .configurable = 1,
     },
@@ -397,32 +436,48 @@ static njs_int_t
 njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused)
 {
-    u_char              digest[32], key_buf[64];
-    njs_str_t           alg_name, key;
+    njs_str_t           key;
     njs_uint_t          i;
     njs_hmac_t          *ctx;
     njs_hash_alg_t      *alg;
+    njs_typed_array_t   *array;
+    const njs_value_t   *value;
+    njs_array_buffer_t  *buffer;
     njs_object_value_t  *hmac;
+    u_char              digest[32], key_buf[64];
 
-    if (njs_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "algorithm must be a string");
+    alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
+    if (njs_slow_path(alg == NULL)) {
         return NJS_ERROR;
     }
 
-    if (njs_slow_path(nargs < 3 || !njs_is_string(&args[2]))) {
-        njs_type_error(vm, "key must be a string");
-        return NJS_ERROR;
-    }
+    value = njs_arg(args, nargs, 2);
 
-    njs_string_get(&args[1], &alg_name);
+    switch (value->type) {
+    case NJS_STRING:
+        njs_string_get(value, &key);
+        break;
+
+    case NJS_TYPED_ARRAY:
+    case NJS_DATA_VIEW:
+        array = njs_typed_array(value);
+        buffer = array->buffer;
+        if (njs_slow_path(njs_is_detached_buffer(buffer))) {
+            njs_type_error(vm, "detached buffer");
+            return NJS_ERROR;
+        }
+
+        key.start = &buffer->u.u8[array->offset];
+        key.length = array->byte_length;
+        break;
+
+    default:
+        njs_type_error(vm, "key argument \"%s\" is not a string "
+                       "or Buffer-like object", njs_type_string(value->type));
 
-    alg = njs_crypto_alg(vm, &alg_name);
-    if (njs_slow_path(alg == NULL)) {
         return NJS_ERROR;
     }
 
-    njs_string_get(&args[2], &key);
-
     ctx = njs_mp_alloc(vm->mem_pool, sizeof(njs_hmac_t));
     if (njs_slow_path(ctx == NULL)) {
         njs_memory_error(vm);
@@ -468,118 +523,6 @@ njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 }
 
 
-static njs_int_t
-njs_hmac_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    njs_str_t    data;
-    njs_hmac_t   *ctx;
-    njs_value_t  *this;
-
-    if (njs_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "data must be a string");
-        return NJS_ERROR;
-    }
-
-    this = njs_argument(args, 0);
-
-    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_CRYPTO_HMAC))) {
-        njs_type_error(vm, "\"this\" is not a hash object");
-        return NJS_ERROR;
-    }
-
-    njs_string_get(&args[1], &data);
-
-    ctx = njs_object_data(this);
-
-    if (njs_slow_path(ctx->alg == NULL)) {
-        njs_error(vm, "Digest already called");
-        return NJS_ERROR;
-    }
-
-    ctx->alg->update(&ctx->u, data.start, data.length);
-
-    vm->retval = *this;
-
-    return NJS_OK;
-}
-
-
-static njs_int_t
-njs_hmac_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
-    njs_index_t unused)
-{
-    u_char            hash1[32], digest[32], *p;
-    njs_str_t         enc_name, str;
-    njs_int_t         ret;
-    njs_hmac_t        *ctx;
-    njs_value_t       *this;
-    njs_hash_alg_t    *alg;
-    njs_crypto_enc_t  *enc;
-
-    if (njs_slow_path(nargs > 1 && !njs_is_string(&args[1]))) {
-        njs_type_error(vm, "encoding must be a string");
-        return NJS_ERROR;
-    }
-
-    this = njs_argument(args, 0);
-
-    if (njs_slow_path(!njs_is_object_data(this, NJS_DATA_TAG_CRYPTO_HMAC))) {
-        njs_type_error(vm, "\"this\" is not a hash object");
-        return NJS_ERROR;
-    }
-
-    enc = NULL;
-
-    if (nargs > 1) {
-        njs_string_get(&args[1], &enc_name);
-
-        enc = njs_crypto_encoding(vm, &enc_name);
-        if (njs_slow_path(enc == NULL)) {
-            return NJS_ERROR;
-        }
-    }
-
-    ctx = njs_object_data(this);
-
-    if (njs_slow_path(ctx->alg == NULL)) {
-        njs_error(vm, "Digest already called");
-        return NJS_ERROR;
-    }
-
-    alg = ctx->alg;
-
-    alg->final(hash1, &ctx->u);
-
-    alg->init(&ctx->u);
-    alg->update(&ctx->u, ctx->opad, 64);
-    alg->update(&ctx->u, hash1, alg->size);
-    alg->final(digest, &ctx->u);
-
-    str.start = digest;
-    str.length = alg->size;
-
-    if (enc == NULL) {
-        p = njs_string_alloc(vm, &vm->retval, str.length, 0);
-
-        if (njs_fast_path(p != NULL)) {
-            memcpy(p, str.start, str.length);
-            ret = NJS_OK;
-
-        } else {
-            ret = NJS_ERROR;
-        }
-
-    } else {
-        ret = enc->encode(vm, &vm->retval, &str);
-    }
-
-    ctx->alg = NULL;
-
-    return ret;
-}
-
-
 static const njs_object_prop_t  njs_hmac_prototype_properties[] =
 {
     {
@@ -599,7 +542,8 @@ static const njs_object_prop_t  njs_hmac_prototype_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("update"),
-        .value = njs_native_function(njs_hmac_prototype_update, 0),
+        .value = njs_native_function2(njs_hash_prototype_update, 0,
+                                      NJS_DATA_TAG_CRYPTO_HMAC),
         .writable = 1,
         .configurable = 1,
     },
@@ -607,7 +551,8 @@ static const njs_object_prop_t  njs_hmac_prototype_properties[] =
     {
         .type = NJS_PROPERTY,
         .name = njs_string("digest"),
-        .value = njs_native_function(njs_hmac_prototype_digest, 0),
+        .value = njs_native_function2(njs_hash_prototype_digest, 0,
+                                      NJS_DATA_TAG_CRYPTO_HMAC),
         .writable = 1,
         .configurable = 1,
     },
@@ -716,34 +661,61 @@ const njs_object_type_init_t  njs_hmac_type_init = {
 
 
 static njs_hash_alg_t *
-njs_crypto_alg(njs_vm_t *vm, const njs_str_t *name)
+njs_crypto_algorithm(njs_vm_t *vm, const njs_value_t *value)
 {
-    njs_hash_alg_t *e;
+    njs_str_t       name;
+    njs_hash_alg_t  *e;
+
+    if (njs_slow_path(!njs_is_string(value))) {
+        njs_type_error(vm, "algorithm must be a string");
+        return NULL;
+    }
+
+    njs_string_get(value, &name);
 
     for (e = &njs_hash_algorithms[0]; e->name.length != 0; e++) {
-        if (njs_strstr_eq(name, &e->name)) {
+        if (njs_strstr_eq(&name, &e->name)) {
             return e;
         }
     }
 
-    njs_type_error(vm, "not supported algorithm: \"%V\"", name);
+    njs_type_error(vm, "not supported algorithm: \"%V\"", &name);
 
     return NULL;
 }
 
 
 static njs_crypto_enc_t *
-njs_crypto_encoding(njs_vm_t *vm, const njs_str_t *name)
+njs_crypto_encoding(njs_vm_t *vm, const njs_value_t *value)
 {
-    njs_crypto_enc_t *e;
+    njs_str_t         name;
+    njs_crypto_enc_t  *e;
 
-    for (e = &njs_encodings[0]; e->name.length != 0; e++) {
-        if (njs_strstr_eq(name, &e->name)) {
+    if (njs_slow_path(!njs_is_string(value))) {
+        if (njs_is_defined(value)) {
+            njs_type_error(vm, "encoding must be a string");
+            return NULL;
+        }
+
+        return &njs_encodings[0];
+    }
+
+    njs_string_get(value, &name);
+
+    for (e = &njs_encodings[1]; e->name.length != 0; e++) {
+        if (njs_strstr_eq(&name, &e->name)) {
             return e;
         }
     }
 
-    njs_type_error(vm, "Unknown digest encoding: \"%V\"", name);
+    njs_type_error(vm, "Unknown digest encoding: \"%V\"", &name);
 
     return NULL;
 }
+
+
+static njs_int_t
+njs_buffer_digest(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src)
+{
+    return njs_buffer_new(vm, value, src->start, src->length);
+}
index d6ac7694236800ee4e892267a2741318a9d58a6b..3ba4ddc43d9b43f3238c8630e4aaf36599b023b9 100644 (file)
@@ -17314,49 +17314,77 @@ static njs_unit_test_t  njs_test[] =
               "h.constructor.name"),
       njs_str("Hash") },
 
-    { njs_str("var h = require('crypto').createHash('md5');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("b86fc6b051f63d73de262d4c34e3a0a9") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('A').update('B').digest('hex')"),
-      njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('AB').digest().toString('hex')"),
-      njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('AB').digest('base64')"),
-      njs_str("BtlFlCqiamG+GMPiK/GbvKjdK10=") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('AB').digest('base64url')"),
-      njs_str("BtlFlCqiamG-GMPiK_GbvKjdK10") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('AB').digest().toString('base64')"),
-      njs_str("BtlFlCqiamG+GMPiK/GbvKjdK10=") },
-
-    { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("c95466320eaae6d19ee314ae4f135b12d45ced9a") },
-
-    { njs_str("var h = require('crypto').createHash('sha256');"
-                 "h.update('A').update('B').digest('hex')"),
-      njs_str("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153") },
+    { njs_str("var hash = require('crypto').createHash.bind(undefined, 'md5');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hash().update('AB').digest().toString(e);"
+              "   var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);"
+              "   var h3 = hash().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("b86fc6b051f63d73de262d4c34e3a0a9,"
+              "uG/GsFH2PXPeJi1MNOOgqQ==,"
+              "uG_GsFH2PXPeJi1MNOOgqQ") },
+
+    { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hash().update('4142', 'hex').digest().toString(e);"
+              "   var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);"
+              "   var h3 = hash().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("06d945942aa26a61be18c3e22bf19bbca8dd2b5d,"
+              "BtlFlCqiamG+GMPiK/GbvKjdK10=,"
+              "BtlFlCqiamG-GMPiK_GbvKjdK10") },
+
+    { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');"
+              "['hex', 'base64', 'base64url'].every(e => {"
+              "   var h = hash().digest(e);"
+              "   var h2 = hash().update('').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest($e):$h != update('').digest($e):$h2`)};"
+              "   return true;"
+              "})"),
+      njs_str("true") },
 
-    { njs_str("var h = require('crypto').createHash('sha256');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153") },
+    { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha1');"
+              "["
+              " ['AB'],"
+              " ['4142', 'hex'],"
+              " ['QUI=', 'base64'],"
+              " ['QUI', 'base64url']"
+              "].every(args => {"
+              "        return hash().update(args[0], args[1]).digest('hex') === '06d945942aa26a61be18c3e22bf19bbca8dd2b5d';"
+              "})"),
+      njs_str("true") },
 
-    { njs_str("var h = require('crypto').createHash('sha256');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930") },
+    { njs_str("var hash = require('crypto').createHash.bind(undefined, 'sha256');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hash().update('AB').digest().toString(e);"
+              "   var h2 = hash().update(Buffer.from('XABX').subarray(1,3)).digest(e);"
+              "   var h3 = hash().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153,"
+              "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH/SrjP9aWQVM=,"
+              "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM") },
+
+    { njs_str("var hash = require('crypto').createHash;"
+              "njs.dump(['', 'abc'.repeat(100)].map(v => {"
+              "    return ['md5', 'sha1', 'sha256'].map(h => {"
+              "        return hash(h).update(v).digest('hex');"
+              "     })"
+              "}))"),
+      njs_str("[['d41d8cd98f00b204e9800998ecf8427e',"
+              "'da39a3ee5e6b4b0d3255bfef95601890afd80709',"
+              "'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855'],"
+              "['f571117acbd8153c8dc3c81b8817773a',"
+              "'c95466320eaae6d19ee314ae4f135b12d45ced9a',"
+              "'d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930']]") },
 
     { njs_str("var h = require('crypto').createHash()"),
       njs_str("TypeError: algorithm must be a string") },
@@ -17368,23 +17396,26 @@ static njs_unit_test_t  njs_test[] =
       njs_str("TypeError: not supported algorithm: \"sha512\"") },
 
     { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update()"),
-      njs_str("TypeError: data must be a string") },
+              "h.update()"),
+      njs_str("TypeError: data argument \"undefined\" is not a string or Buffer-like object") },
 
     { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update({})"),
-      njs_str("TypeError: data must be a string") },
+              "h.update({})"),
+      njs_str("TypeError: data argument \"object\" is not a string or Buffer-like object") },
 
     { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('A').digest('latin1')"),
+              "h.update('A').digest('latin1')"),
       njs_str("TypeError: Unknown digest encoding: \"latin1\"") },
 
+    { njs_str("require('crypto').createHash('sha1').digest() instanceof Buffer"),
+      njs_str("true") },
+
     { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('A').digest('hex'); h.digest('hex')"),
+              "h.update('A').digest('hex'); h.digest('hex')"),
       njs_str("Error: Digest already called") },
 
     { njs_str("var h = require('crypto').createHash('sha1');"
-                 "h.update('A').digest('hex'); h.update('B')"),
+              "h.update('A').digest('hex'); h.update('B')"),
       njs_str("Error: Digest already called") },
 
     { njs_str("typeof require('crypto').createHash('md5')"),
@@ -17396,17 +17427,68 @@ static njs_unit_test_t  njs_test[] =
               "[Object.prototype.toString.call(h), njs.dump(h),h]"),
       njs_str("[object Hmac],Hmac {},[object Hmac]") },
 
-    { njs_str("var h = require('crypto').createHmac('md5', '');"
-                 "h.digest('hex')"),
-      njs_str("74e6f7298a9c2d168935f58c001bad88") },
+    { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'md5', '');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hmac().update('AB').digest().toString(e);"
+              "   var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);"
+              "   var h3 = hmac().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("9e0e9e545ef63d41dfb653daecf8ebc7,"
+              "ng6eVF72PUHftlPa7Pjrxw==,"
+              "ng6eVF72PUHftlPa7Pjrxw") },
+
+    { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha1', '');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hmac().update('AB').digest().toString(e);"
+              "   var h2 = hmac().update(Buffer.from('XABX').subarray(1,3)).digest(e);"
+              "   var h3 = hmac().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("d32c0b6637cc2dfe4670f3fe48ef4434123c4810,"
+              "0ywLZjfMLf5GcPP+SO9ENBI8SBA=,"
+              "0ywLZjfMLf5GcPP-SO9ENBI8SBA") },
 
-    { njs_str("var h = require('crypto').createHmac('sha1', '');"
-                 "h.digest('hex')"),
-      njs_str("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") },
+    { njs_str("var hash = require('crypto').createHmac.bind(undefined, 'sha1', '');"
+              "["
+              " ['AB'],"
+              " ['4142', 'hex'],"
+              " ['QUI=', 'base64'],"
+              " ['QUI', 'base64url']"
+              "].every(args => {"
+              "        return hash().update(args[0], args[1]).digest('hex') === 'd32c0b6637cc2dfe4670f3fe48ef4434123c4810';"
+              "})"),
+      njs_str("true") },
 
-    { njs_str("var h = require('crypto').createHmac('sha1', '');"
-                 "h.digest().toString('hex')"),
-      njs_str("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") },
+    { njs_str("var hmac = require('crypto').createHmac.bind(undefined, 'sha256', '');"
+              "['hex', 'base64', 'base64url'].map(e => {"
+              "   var h = hmac().update('AB').digest().toString(e);"
+              "   var h2 = hmac().update(Buffer.from('AB')).digest(e);"
+              "   var h3 = hmac().update('A').update('B').digest(e);"
+              "   if (h !== h2) {throw new Error(`digest().toString($e):$h != digest($e):$h2`)};"
+              "   if (h !== h3) {throw new Error(`digest().toString($e):$h != update('A').update('B').digest($e):$h3`)};"
+              "   return h;"
+              "})"),
+      njs_str("d53400095496267cf02e5dbd4b0bf9fbfb5f36f311ea7d9809af5487421743e3,"
+              "1TQACVSWJnzwLl29Swv5+/tfNvMR6n2YCa9Uh0IXQ+M=,"
+              "1TQACVSWJnzwLl29Swv5-_tfNvMR6n2YCa9Uh0IXQ-M") },
+
+    { njs_str("var hmac = require('crypto').createHmac;"
+              "njs.dump(['', 'abc'.repeat(100)].map(v => {"
+              "    return ['md5', 'sha1', 'sha256'].map(h => {"
+              "        return hmac(h, Buffer.from('secret')).update(v).digest('hex');"
+              "     })"
+              "}))"),
+      njs_str("[['5c8db03f04cec0f43bcb060023914190',"
+              "'25af6174a0fcecc4d346680a72b7ce644b9a88e8',"
+              "'f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169'],"
+              "['91eb74a225cdd3bbfccc34396c6e3ac5',"
+              "'0aac71e3a813a7acc4a809cfdedb2ecba04ffc5e',"
+              "'8660d2d51d6f20f61d5aadfb6c43df7fd05fc2fc4967d8aec1846f3d9ec03987']]") },
 
     { njs_str("var h = require('crypto').createHmac('sha1', '');"
               "var Hmac = h.constructor; "
@@ -17417,78 +17499,17 @@ static njs_unit_test_t  njs_test[] =
               "h.constructor.name"),
       njs_str("Hmac") },
 
-    { njs_str("var h = require('crypto').createHmac('md5', 'secret key');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("9c72728915eb26620a5caeafd0063b29") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('A').update('B').digest('hex')"),
-      njs_str("adc60e03459c4bae7cf4eb6d9730003e9490b22f") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("adc60e03459c4bae7cf4eb6d9730003e9490b22f") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('AB').digest('base64')"),
-      njs_str("rcYOA0WcS6589OttlzAAPpSQsi8=") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('AB').digest('base64url')"),
-      njs_str("rcYOA0WcS6589OttlzAAPpSQsi8") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('AB').digest().toString('base64')"),
-      njs_str("rcYOA0WcS6589OttlzAAPpSQsi8=") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("b105ad6921e4c54d3fa0a9ec3f7f0ee9bd2c659d") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'A'.repeat(40));"
-                 "h.update('AB').digest('hex')"),
-      njs_str("0b84f78ca5275d76d4b7dafb5845ee2b6a79c4c2") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'A'.repeat(64));"
-                 "h.update('AB').digest('hex')"),
-      njs_str("400ce530816c6b3247e2959f3982a12aaf58c0c9") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'A'.repeat(100));"
-                 "h.update('AB').digest('hex')"),
-      njs_str("670e7cdebae6392797e000e79e51d3b6589d8fad") },
-
-    { njs_str("var h = require('crypto').createHmac('sha256', '');"
-                 "h.digest('hex')"),
-      njs_str("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad") },
-
-    { njs_str("var h = require('crypto').createHmac('sha256', 'secret key');"
-                 "h.update('A').update('B').digest('hex')"),
-      njs_str("46085184b3b45a13d838bf71a0ce03675dab30931e0f1f68fa636ea65fdb286d") },
-
-    { njs_str("var h = require('crypto').createHmac('sha256', 'secret key');"
-                 "h.update('AB').digest('hex')"),
-      njs_str("46085184b3b45a13d838bf71a0ce03675dab30931e0f1f68fa636ea65fdb286d") },
+    { njs_str("require('crypto').createHmac('sha1', '').digest() instanceof Buffer"),
+      njs_str("true") },
 
     { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(64));"
-                 "h.update('AB').digest('hex')"),
+              "h.update('AB').digest('hex')"),
       njs_str("ee9dce43b12eb3e865614ad9c1a8d4fad4b6eac2b64647bd24cd192888d3f367") },
 
     { njs_str("var h = require('crypto').createHmac('sha256', 'A'.repeat(100));"
-                 "h.update('AB').digest('hex')"),
+              "h.update('AB').digest('hex')"),
       njs_str("5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc") },
 
-    { njs_str("var h = require('crypto').createHmac('md5', 'secret key');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("5dd706af43536f8c9c83e7ea55b1a5a2") },
-
-    { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("b105ad6921e4c54d3fa0a9ec3f7f0ee9bd2c659d") },
-
-    { njs_str("var h = require('crypto').createHmac('sha256', 'secret key');"
-                 "h.update('abc'.repeat(100)).digest('hex')"),
-      njs_str("f6550d398ce350ee8d94a0f44f2cf6b9bc8d316ae4625fb4434f22980a276bac") },
-
     { njs_str("var h = require('crypto').createHmac()"),
       njs_str("TypeError: algorithm must be a string") },
 
@@ -17499,14 +17520,14 @@ static njs_unit_test_t  njs_test[] =
       njs_str("TypeError: not supported algorithm: \"sha512\"") },
 
     { njs_str("var h = require('crypto').createHmac('sha1', [])"),
-      njs_str("TypeError: key must be a string") },
+      njs_str("TypeError: key argument \"array\" is not a string or Buffer-like object") },
 
     { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('A').digest('hex'); h.digest('hex')"),
+              "h.update('A').digest('hex'); h.digest('hex')"),
       njs_str("Error: Digest already called") },
 
     { njs_str("var h = require('crypto').createHmac('sha1', 'secret key');"
-                 "h.update('A').digest('hex'); h.update('B')"),
+              "h.update('A').digest('hex'); h.update('B')"),
       njs_str("Error: Digest already called") },
 
     { njs_str("typeof require('crypto').createHmac('md5', 'a')"),
@@ -20075,18 +20096,18 @@ static njs_unit_test_t  njs_shell_test[] =
 
     { njs_str("var h = require('crypto').createHash('sha1')" ENTER
               "h.update([])" ENTER),
-      njs_str("TypeError: data must be a string\n"
+      njs_str("TypeError: data argument \"array\" is not a string or Buffer-like object\n"
               "    at Hash.prototype.update (native)\n"
               "    at main (:1)\n") },
 
     { njs_str("require('crypto').createHmac('sha1', [])" ENTER),
-      njs_str("TypeError: key must be a string\n"
+      njs_str("TypeError: key argument \"array\" is not a string or Buffer-like object\n"
               "    at crypto.createHmac (native)\n"
               "    at main (:1)\n") },
 
     { njs_str("var h = require('crypto').createHmac('sha1', 'secret')" ENTER
               "h.update([])" ENTER),
-      njs_str("TypeError: data must be a string\n"
+      njs_str("TypeError: data argument \"array\" is not a string or Buffer-like object\n"
               "    at Hmac.prototype.update (native)\n"
               "    at main (:1)\n") },