[njs] Nodejs style crypto methods.

Dmitry Volyntsev xeioex at nginx.com
Fri Mar 30 16:20:59 UTC 2018


details:   http://hg.nginx.org/njs/rev/d3747fbfce39
branches:  
changeset: 477:d3747fbfce39
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Mar 30 18:50:38 2018 +0300
description:
Nodejs style crypto methods.

var crypto = require('crypto')
var hash = crypto.createHash(<alg>)
    available algorithms: 'md5', 'sha1', 'sha256'.
hash.update(<data>)
hash.digest([<encoding>])
    available encodings: 'hex', 'base64'.

var hmac = crypto.createHmac(<alg>, <key>)
    available algorithms: 'md5', 'sha1', 'sha256'.
hmac.update(<data>)
hmac.digest([<encoding>])
    available encodings: 'hex', 'base64'.

diffstat:

 Makefile                        |   19 +
 njs/njs_builtin.c               |   17 +-
 njs/njs_crypto.c                |  713 ++++++++++++++++++++++++++++++++++++++++
 njs/njs_crypto.h                |   24 +
 njs/njs_object.c                |   12 +-
 njs/njs_vm.c                    |   10 +
 njs/njs_vm.h                    |   32 +-
 njs/njscript.h                  |    1 +
 njs/test/njs_interactive_test.c |   22 +
 njs/test/njs_unit_test.c        |  163 +++++++++
 nxt/Makefile                    |   36 ++
 nxt/nxt_md5.c                   |  270 +++++++++++++++
 nxt/nxt_md5.h                   |   23 +
 nxt/nxt_sha1.c                  |  298 ++++++++++++++++
 nxt/nxt_sha1.h                  |   24 +
 nxt/nxt_sha2.c                  |  320 +++++++++++++++++
 nxt/nxt_sha2.h                  |   24 +
 17 files changed, 1996 insertions(+), 12 deletions(-)

diffs (truncated from 2313 to 1000 lines):

diff -r be43fdd9579e -r d3747fbfce39 Makefile
--- a/Makefile	Fri Mar 30 15:46:40 2018 +0300
+++ b/Makefile	Fri Mar 30 18:50:38 2018 +0300
@@ -24,6 +24,7 @@ NXT_BUILDDIR =	build
 	$(NXT_BUILDDIR)/njs_module.o \
 	$(NXT_BUILDDIR)/njs_event.o \
 	$(NXT_BUILDDIR)/njs_fs.o \
+	$(NXT_BUILDDIR)/njs_crypto.o \
 	$(NXT_BUILDDIR)/njs_extern.o \
 	$(NXT_BUILDDIR)/njs_variable.o \
 	$(NXT_BUILDDIR)/njs_builtin.o \
@@ -40,6 +41,9 @@ NXT_BUILDDIR =	build
 	$(NXT_BUILDDIR)/nxt_lvlhsh.o \
 	$(NXT_BUILDDIR)/nxt_trace.o \
 	$(NXT_BUILDDIR)/nxt_random.o \
+	$(NXT_BUILDDIR)/nxt_md5.o \
+	$(NXT_BUILDDIR)/nxt_sha1.o \
+	$(NXT_BUILDDIR)/nxt_sha2.o \
 	$(NXT_BUILDDIR)/nxt_pcre.o \
 	$(NXT_BUILDDIR)/nxt_malloc.o \
 	$(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
@@ -62,6 +66,7 @@ NXT_BUILDDIR =	build
 		$(NXT_BUILDDIR)/njs_module.o \
 		$(NXT_BUILDDIR)/njs_event.o \
 		$(NXT_BUILDDIR)/njs_fs.o \
+		$(NXT_BUILDDIR)/njs_crypto.o \
 		$(NXT_BUILDDIR)/njs_extern.o \
 		$(NXT_BUILDDIR)/njs_variable.o \
 		$(NXT_BUILDDIR)/njs_builtin.o \
@@ -78,6 +83,9 @@ NXT_BUILDDIR =	build
 		$(NXT_BUILDDIR)/nxt_lvlhsh.o \
 		$(NXT_BUILDDIR)/nxt_trace.o \
 		$(NXT_BUILDDIR)/nxt_random.o \
+		$(NXT_BUILDDIR)/nxt_md5.o \
+		$(NXT_BUILDDIR)/nxt_sha1.o \
+		$(NXT_BUILDDIR)/nxt_sha2.o \
 		$(NXT_BUILDDIR)/nxt_pcre.o \
 		$(NXT_BUILDDIR)/nxt_malloc.o \
 		$(NXT_BUILDDIR)/nxt_mem_cache_pool.o \
@@ -350,6 +358,17 @@ dist:
 		-I$(NXT_LIB) -Injs \
 		njs/njs_fs.c
 
+$(NXT_BUILDDIR)/njs_crypto.o: \
+	$(NXT_BUILDDIR)/libnxt.a \
+	njs/njscript.h \
+	njs/njs_vm.h \
+	njs/njs_crypto.h \
+	njs/njs_crypto.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_crypto.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) -Injs \
+		njs/njs_crypto.c
+
 $(NXT_BUILDDIR)/njs_extern.o: \
 	$(NXT_BUILDDIR)/libnxt.a \
 	njs/njscript.h \
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_builtin.c
--- a/njs/njs_builtin.c	Fri Mar 30 15:46:40 2018 +0300
+++ b/njs/njs_builtin.c	Fri Mar 30 18:50:38 2018 +0300
@@ -33,6 +33,7 @@
 #include <njs_time.h>
 #include <njs_module.h>
 #include <njs_fs.h>
+#include <njs_crypto.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -58,7 +59,8 @@ const njs_object_init_t    *njs_object_i
 
 
 const njs_object_init_t    *njs_module_init[] = {
-    &njs_fs_object_init          /* fs                 */
+    &njs_fs_object_init,         /* fs                 */
+    &njs_crypto_object_init      /* crypto             */
 };
 
 
@@ -71,6 +73,8 @@ const njs_object_init_t  *njs_prototype_
     &njs_function_prototype_init,
     &njs_regexp_prototype_init,
     &njs_date_prototype_init,
+    &njs_hash_prototype_init,
+    &njs_hmac_prototype_init,
     &njs_error_prototype_init,
     &njs_eval_error_prototype_init,
     &njs_internal_error_prototype_init,
@@ -91,6 +95,8 @@ const njs_object_init_t    *njs_construc
     &njs_function_constructor_init,
     &njs_regexp_constructor_init,
     &njs_date_constructor_init,
+    &njs_hash_constructor_init,
+    &njs_hmac_constructor_init,
     &njs_error_constructor_init,
     &njs_eval_error_constructor_init,
     &njs_internal_error_constructor_init,
@@ -192,6 +198,12 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { .date =         { .time = NAN,
                             .object = { .type = NJS_DATE } } },
 
+        { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0),
+                            .object = { .type = NJS_OBJECT } } },
+
+        { .object_value = { .value = njs_value(NJS_DATA, 0, 0.0),
+                            .object = { .type = NJS_OBJECT } } },
+
         { .object =       { .type = NJS_OBJECT_ERROR } },
         { .object =       { .type = NJS_OBJECT_EVAL_ERROR } },
         { .object =       { .type = NJS_OBJECT_INTERNAL_ERROR } },
@@ -214,6 +226,9 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_regexp_constructor,
           { NJS_SKIP_ARG, NJS_STRING_ARG, NJS_STRING_ARG } },
         { njs_date_constructor,       { 0 } },
+        { njs_hash_constructor,       { NJS_SKIP_ARG, NJS_STRING_ARG } },
+        { njs_hmac_constructor,       { NJS_SKIP_ARG, NJS_STRING_ARG,
+                                        NJS_STRING_ARG } },
         { njs_error_constructor,      { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_eval_error_constructor, { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_internal_error_constructor,
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_crypto.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_crypto.c	Fri Mar 30 18:50:38 2018 +0300
@@ -0,0 +1,713 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_md5.h>
+#include <nxt_sha1.h>
+#include <nxt_sha2.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_crypto.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_string.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+
+
+typedef void (*njs_hash_init)(void *ctx);
+typedef void (*njs_hash_update)(void *ctx, const void *data, size_t size);
+typedef void (*njs_hash_final)(u_char *result, void *ctx);
+
+typedef njs_ret_t (*njs_digest_encode)(njs_vm_t *vm, njs_value_t *value,
+    const nxt_str_t *src);
+
+
+typedef struct {
+    nxt_str_t           name;
+
+    size_t              size;
+    njs_hash_init       init;
+    njs_hash_update     update;
+    njs_hash_final      final;
+} njs_hash_alg_t;
+
+typedef struct {
+    union {
+        nxt_md5_t       md5;
+        nxt_sha1_t      sha1;
+        nxt_sha2_t      sha2;
+    } u;
+
+    njs_hash_alg_t      *alg;
+} njs_digest_t;
+
+typedef struct {
+    nxt_str_t           key;
+    u_char              opad[64];
+
+    union {
+        nxt_md5_t       md5;
+        nxt_sha1_t      sha1;
+        nxt_sha2_t      sha2;
+    } u;
+
+    njs_hash_alg_t      *alg;
+} njs_hmac_t;
+
+
+typedef struct {
+    nxt_str_t             name;
+
+    njs_digest_encode     encode;
+} njs_crypto_enc_t;
+
+
+static njs_hash_alg_t njs_hash_algorithms[] = {
+
+   {
+     nxt_string("md5"),
+     16,
+     (njs_hash_init) nxt_md5_init,
+     (njs_hash_update) nxt_md5_update,
+     (njs_hash_final) nxt_md5_final
+   },
+
+   {
+     nxt_string("sha1"),
+     20,
+     (njs_hash_init) nxt_sha1_init,
+     (njs_hash_update) nxt_sha1_update,
+     (njs_hash_final) nxt_sha1_final
+   },
+
+   {
+     nxt_string("sha256"),
+     32,
+     (njs_hash_init) nxt_sha2_init,
+     (njs_hash_update) nxt_sha2_update,
+     (njs_hash_final) nxt_sha2_final
+   },
+
+   {
+    nxt_null_string,
+    0,
+    NULL,
+    NULL,
+    NULL
+   }
+
+};
+
+static njs_crypto_enc_t njs_encodings[] = {
+
+   {
+     nxt_string("hex"),
+     njs_string_hex
+   },
+
+   {
+     nxt_string("base64"),
+     njs_string_base64
+   },
+
+   {
+    nxt_null_string,
+    NULL
+   }
+};
+
+
+static njs_hash_alg_t *njs_crypto_alg(njs_vm_t *vm, const nxt_str_t *name);
+static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm,
+    const nxt_str_t *name);
+
+
+static njs_object_value_t *
+njs_crypto_object_value_alloc(njs_vm_t *vm, nxt_uint_t proto)
+{
+    njs_object_value_t  *ov;
+
+    ov = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_object_value_t));
+
+    if (nxt_fast_path(ov != NULL)) {
+        nxt_lvlhsh_init(&ov->object.hash);
+        nxt_lvlhsh_init(&ov->object.shared_hash);
+        ov->object.type = NJS_OBJECT_VALUE;
+        ov->object.shared = 0;
+        ov->object.extensible = 1;
+
+        ov->object.__proto__ = &vm->prototypes[proto].object;
+    }
+
+    return ov;
+}
+
+
+static njs_ret_t
+njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    nxt_str_t           alg_name;
+    njs_digest_t        *dgst;
+    njs_hash_alg_t      *alg;
+    njs_object_value_t  *hash;
+
+    if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "algorithm must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    njs_string_get(&args[1], &alg_name);
+
+    alg = njs_crypto_alg(vm, &alg_name);
+    if (nxt_slow_path(alg == NULL)) {
+        return NJS_ERROR;
+    }
+
+    hash = njs_crypto_object_value_alloc(vm, NJS_PROTOTYPE_CRYPTO_HASH);
+    if (nxt_slow_path(hash == NULL)) {
+        goto memory_error;
+    }
+
+    dgst = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_digest_t));
+    if (nxt_slow_path(dgst == NULL)) {
+        goto memory_error;
+    }
+
+    dgst->alg = alg;
+
+    alg->init(&dgst->u);
+
+    njs_value_data_set(&hash->value, dgst);
+
+    vm->retval.data.u.object_value = hash;
+    vm->retval.type = NJS_OBJECT_VALUE;
+    vm->retval.data.truth = 1;
+
+    return NJS_OK;
+
+memory_error:
+
+    njs_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    nxt_str_t           data;
+    njs_digest_t        *dgst;
+    njs_object_value_t  *hash;
+
+    if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_object_value(&args[0]))) {
+        njs_type_error(vm, "'this' is not an object_value", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) {
+        njs_type_error(vm, "value of 'this' is not a data type", NULL);
+        return NJS_ERROR;
+    }
+
+    hash = args[0].data.u.object_value;
+
+    njs_string_get(&args[1], &data);
+
+    dgst = njs_value_data(&hash->value);
+    dgst->alg->update(&dgst->u, data.start, data.length);
+
+    vm->retval = args[0];
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    u_char              digest[32], *p;
+    njs_ret_t           ret;
+    nxt_str_t           enc_name, str;
+    njs_digest_t        *dgst;
+    njs_hash_alg_t      *alg;
+    njs_crypto_enc_t    *enc;
+    njs_object_value_t  *hash;
+
+    if (nxt_slow_path(nargs > 1 && !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "encoding must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_object_value(&args[0]))) {
+        njs_type_error(vm, "'this' is not an object_value", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) {
+        njs_type_error(vm, "value of 'this' is not a data type", NULL);
+        return NJS_ERROR;
+    }
+
+    enc = NULL;
+
+    if (nargs > 1) {
+        njs_string_get(&args[1], &enc_name);
+
+        enc = njs_crypto_encoding(vm, &enc_name);
+        if (nxt_slow_path(enc == NULL)) {
+            return NJS_ERROR;
+        }
+    }
+
+    hash = args[0].data.u.object_value;
+
+    dgst = njs_value_data(&hash->value);
+
+    if (nxt_slow_path(dgst->alg == NULL)) {
+        njs_error(vm, "Digest already called", NULL);
+        return NJS_ERROR;
+    }
+
+    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 (nxt_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);
+    }
+
+    dgst->alg = NULL;
+
+    return ret;
+}
+
+
+static njs_ret_t
+njs_hash_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    static const njs_value_t  string = njs_string("[object Hash]");
+
+    vm->retval = string;
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_hash_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Hash"),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toString"),
+        .value = njs_native_function(njs_hash_prototype_to_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("update"),
+        .value = njs_native_function(njs_hash_prototype_update, 0,
+                                     NJS_OBJECT_ARG, NJS_SKIP_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("digest"),
+        .value = njs_native_function(njs_hash_prototype_digest, 0,
+                                     NJS_OBJECT_ARG, NJS_SKIP_ARG),
+    },
+};
+
+
+const njs_object_init_t  njs_hash_prototype_init = {
+    nxt_string("Hash"),
+    njs_hash_prototype_properties,
+    nxt_nitems(njs_hash_prototype_properties),
+};
+
+
+njs_ret_t
+njs_hash_constructor(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_crypto_create_hash(vm, args, nargs, unused);
+}
+
+
+const njs_object_init_t  njs_hash_constructor_init = {
+    nxt_string("Hash"),
+    NULL,
+    0,
+};
+
+
+static njs_ret_t
+njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    u_char              digest[32], key_buf[64];
+    nxt_str_t           alg_name, key;
+    nxt_uint_t          i;
+    njs_hmac_t          *ctx;
+    njs_hash_alg_t      *alg;
+    njs_object_value_t  *hmac;
+
+    if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "algorithm must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(nargs < 3 || !njs_is_string(&args[2]))) {
+        njs_type_error(vm, "key must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    njs_string_get(&args[1], &alg_name);
+
+    alg = njs_crypto_alg(vm, &alg_name);
+    if (nxt_slow_path(alg == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_string_get(&args[2], &key);
+
+    ctx = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_hmac_t));
+    if (nxt_slow_path(ctx == NULL)) {
+        goto memory_error;
+    }
+
+    ctx->alg = alg;
+
+    if (key.length > 64) {
+        alg->init(&ctx->u);
+        alg->update(&ctx->u, key.start, key.length);
+        alg->final(digest, &ctx->u);
+
+        memcpy(key_buf, digest, alg->size);
+        memset(key_buf + alg->size, 0, sizeof(key_buf) - alg->size);
+
+    } else if (key.length < alg->size) {
+
+        memcpy(key_buf, key.start, key.length);
+        memset(key_buf + key.length, 0, sizeof(key_buf) - key.length);
+
+    } else {
+        memcpy(key_buf, key.start, sizeof(key_buf));
+    }
+
+    for (i = 0; i < 64; i++) {
+        ctx->opad[i] = key_buf[i] ^ 0x5c;
+    }
+
+    for (i = 0; i < 64; i++) {
+         key_buf[i] ^= 0x36;
+    }
+
+    alg->init(&ctx->u);
+    alg->update(&ctx->u, key_buf, 64);
+
+    hmac = njs_crypto_object_value_alloc(vm, NJS_PROTOTYPE_CRYPTO_HMAC);
+    if (nxt_slow_path(hmac == NULL)) {
+        goto memory_error;
+    }
+
+    njs_value_data_set(&hmac->value, ctx);
+
+    vm->retval.data.u.object_value = hmac;
+    vm->retval.type = NJS_OBJECT_VALUE;
+    vm->retval.data.truth = 1;
+
+    return NJS_OK;
+
+memory_error:
+
+    njs_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_hmac_prototype_update(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    nxt_str_t           data;
+    njs_hmac_t          *ctx;
+    njs_object_value_t  *hmac;
+
+    if (nxt_slow_path(nargs < 2 || !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_object_value(&args[0]))) {
+        njs_type_error(vm, "'this' is not an object_value", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) {
+        njs_type_error(vm, "value of 'this' is not a data type", NULL);
+        return NJS_ERROR;
+    }
+
+    hmac = args[0].data.u.object_value;
+
+    njs_string_get(&args[1], &data);
+
+    ctx = njs_value_data(&hmac->value);
+    ctx->alg->update(&ctx->u, data.start, data.length);
+
+    vm->retval = args[0];
+
+    return NJS_OK;
+}
+
+
+static njs_ret_t
+njs_hmac_prototype_digest(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    u_char              hash1[32], digest[32], *p;
+    nxt_str_t           enc_name, str;
+    njs_ret_t           ret;
+    njs_hmac_t          *ctx;
+    njs_hash_alg_t      *alg;
+    njs_crypto_enc_t    *enc;
+    njs_object_value_t  *hmac;
+
+    if (nxt_slow_path(nargs > 1 && !njs_is_string(&args[1]))) {
+        njs_type_error(vm, "encoding must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_object_value(&args[0]))) {
+        njs_type_error(vm, "'this' is not an object_value", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_data(&args[0].data.u.object_value->value))) {
+        njs_type_error(vm, "value of 'this' is not a data type", NULL);
+        return NJS_ERROR;
+    }
+
+    enc = NULL;
+
+    if (nargs > 1) {
+        njs_string_get(&args[1], &enc_name);
+
+        enc = njs_crypto_encoding(vm, &enc_name);
+        if (nxt_slow_path(enc == NULL)) {
+            return NJS_ERROR;
+        }
+    }
+
+    hmac = args[0].data.u.object_value;
+
+    ctx = njs_value_data(&hmac->value);
+
+    if (nxt_slow_path(ctx->alg == NULL)) {
+        njs_error(vm, "Digest already called", NULL);
+        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 (nxt_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 njs_ret_t
+njs_hmac_prototype_to_string(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    static const njs_value_t  string = njs_string("[object Hmac]");
+
+    vm->retval = string;
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_hmac_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("Hmac"),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("toString"),
+        .value = njs_native_function(njs_hmac_prototype_to_string, 0, 0),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("update"),
+        .value = njs_native_function(njs_hmac_prototype_update, 0,
+                                     NJS_OBJECT_ARG, NJS_SKIP_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("digest"),
+        .value = njs_native_function(njs_hmac_prototype_digest, 0,
+                                     NJS_OBJECT_ARG, NJS_SKIP_ARG),
+    },
+};
+
+
+const njs_object_init_t  njs_hmac_prototype_init = {
+    nxt_string("Hmac"),
+    njs_hmac_prototype_properties,
+    nxt_nitems(njs_hmac_prototype_properties),
+};
+
+
+njs_ret_t
+njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_crypto_create_hmac(vm, args, nargs, unused);
+}
+
+
+const njs_object_init_t  njs_hmac_constructor_init = {
+    nxt_string("Hmac"),
+    NULL,
+    0,
+};
+
+
+static const njs_object_prop_t  njs_crypto_object_properties[] =
+{
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("createHash"),
+        .value = njs_native_function(njs_crypto_create_hash, 0,
+                                     NJS_SKIP_ARG),
+    },
+
+    {
+        .type = NJS_METHOD,
+        .name = njs_string("createHmac"),
+        .value = njs_native_function(njs_crypto_create_hmac, 0,
+                                     NJS_SKIP_ARG),
+    },
+
+};
+
+
+const njs_object_init_t  njs_crypto_object_init = {
+    nxt_string("crypto"),
+    njs_crypto_object_properties,
+    nxt_nitems(njs_crypto_object_properties),
+};
+
+
+static njs_hash_alg_t *
+njs_crypto_alg(njs_vm_t *vm, const nxt_str_t *name)
+{
+    njs_hash_alg_t *e;
+
+    for (e = &njs_hash_algorithms[0]; e->name.length != 0; e++) {
+        if (nxt_strstr_eq(name, &e->name)) {
+            return e;
+        }
+    }
+
+    njs_type_error(vm, "not supported algorithm: '%.*s'",
+                   (int) name->length, name->start);
+
+    return NULL;
+}
+
+
+static njs_crypto_enc_t *
+njs_crypto_encoding(njs_vm_t *vm, const nxt_str_t *name)
+{
+    njs_crypto_enc_t *e;
+
+    for (e = &njs_encodings[0]; e->name.length != 0; e++) {
+        if (nxt_strstr_eq(name, &e->name)) {
+            return e;
+        }
+    }
+
+    njs_type_error(vm, "Unknown digest encoding: '%.*s'",
+                   (int) name->length, name->start);
+
+    return NULL;
+}
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_crypto.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_crypto.h	Fri Mar 30 18:50:38 2018 +0300
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_CRYPTO_H_INCLUDED_
+#define _NJS_CRYPTO_H_INCLUDED_
+
+njs_ret_t njs_hash_constructor(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+njs_ret_t njs_hmac_constructor(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+
+extern const njs_object_init_t  njs_crypto_object_init;
+
+extern const njs_object_init_t  njs_hash_prototype_init;
+extern const njs_object_init_t  njs_hmac_prototype_init;
+
+extern const njs_object_init_t  njs_hash_constructor_init;
+extern const njs_object_init_t  njs_hmac_constructor_init;
+
+
+#endif /* _NJS_CRYPTO_H_INCLUDED_ */
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_object.c
--- a/njs/njs_object.c	Fri Mar 30 15:46:40 2018 +0300
+++ b/njs/njs_object.c	Fri Mar 30 18:50:38 2018 +0300
@@ -1332,6 +1332,10 @@ static const njs_value_t  njs_object_num
                                      njs_long_string("[object Number]");
 static const njs_value_t  njs_object_string_string =
                                      njs_long_string("[object String]");
+static const njs_value_t  njs_object_data_string =
+                                     njs_string("[object Data]");
+static const njs_value_t  njs_object_exernal_string =
+                                     njs_long_string("[object External]");
 static const njs_value_t  njs_object_object_string =
                                      njs_long_string("[object Object]");
 static const njs_value_t  njs_object_array_string =
@@ -1357,6 +1361,9 @@ static const njs_value_t  njs_object_typ
                                      njs_long_string("[object TypeError]");
 static const njs_value_t  njs_object_uri_error_string =
                                      njs_long_string("[object URIError]");
+static const njs_value_t  njs_object_object_value_string =
+                                     njs_long_string("[object ObjectValue]");
+
 
 
 njs_ret_t
@@ -1375,8 +1382,8 @@ njs_object_prototype_to_string(njs_vm_t 
         &njs_object_number_string,
         &njs_object_string_string,
 
-        &njs_string_empty,
-        &njs_object_function_string,
+        &njs_object_data_string,
+        &njs_object_exernal_string,
         &njs_string_empty,
         &njs_string_empty,
         &njs_string_empty,
@@ -1404,6 +1411,7 @@ njs_object_prototype_to_string(njs_vm_t 
         &njs_object_syntax_error_string,
         &njs_object_type_error_string,
         &njs_object_uri_error_string,
+        &njs_object_object_value_string,
     };
 
     index = args[0].type;
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_vm.c
--- a/njs/njs_vm.c	Fri Mar 30 15:46:40 2018 +0300
+++ b/njs/njs_vm.c	Fri Mar 30 18:50:38 2018 +0300
@@ -1036,6 +1036,7 @@ njs_property_query(njs_vm_t *vm, njs_pro
     case NJS_OBJECT_SYNTAX_ERROR:
     case NJS_OBJECT_TYPE_ERROR:
     case NJS_OBJECT_URI_ERROR:
+    case NJS_OBJECT_VALUE:
         obj = object->data.u.object;
         break;
 
@@ -3650,6 +3651,15 @@ njs_value_number_set(njs_value_t *value,
 }
 
 
+nxt_noinline void
+njs_value_data_set(njs_value_t *value, void *data)
+{
+    value->data.u.data = data;
+    value->type = NJS_DATA;
+    value->data.truth = 1;
+}
+
+
 void
 njs_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...)
 {
diff -r be43fdd9579e -r d3747fbfce39 njs/njs_vm.h
--- a/njs/njs_vm.h	Fri Mar 30 15:46:40 2018 +0300
+++ b/njs/njs_vm.h	Fri Mar 30 18:50:38 2018 +0300
@@ -76,7 +76,7 @@ typedef enum {
 
     /* The order of the above type is used in njs_is_primitive(). */
 
-    /* Reserved                 0x05, */
+    NJS_DATA                  = 0x05,
 
     /* The type is external code. */
     NJS_EXTERNAL              = 0x06,
@@ -90,7 +90,7 @@ typedef enum {
     NJS_INVALID               = 0x07,
 
     /*
-     * The object types have the fourth bit set.  It is used in njs_is_object().
+     * The object types are >= NJS_OBJECT, this is used in njs_is_object().
      * NJS_OBJECT_BOOLEAN, NJS_OBJECT_NUMBER, and NJS_OBJECT_STRING must be
      * in the same order as NJS_BOOLEAN, NJS_NUMBER, and NJS_STRING.  It is
      * used in njs_primitive_prototype_index().  The order of object types
@@ -112,6 +112,7 @@ typedef enum {
     NJS_OBJECT_SYNTAX_ERROR   = 0x1d,
     NJS_OBJECT_TYPE_ERROR     = 0x1e,
     NJS_OBJECT_URI_ERROR      = 0x1f,
+    NJS_OBJECT_VALUE          = 0x20,
 } njs_value_type_t;
 
 
@@ -154,7 +155,7 @@ union njs_value_s {
      * the maximum size of short string to 13.
      */
     struct {
-        njs_value_type_t              type:8;  /* 5 bits */
+        njs_value_type_t              type:8;  /* 6 bits */
         /*
          * The truth field is set during value assignment and then can be
          * quickly tested by logical and conditional operations regardless
@@ -184,7 +185,7 @@ union njs_value_s {
     } data;
 
     struct {
-        njs_value_type_t              type:8;  /* 5 bits */
+        njs_value_type_t              type:8;  /* 6 bits */
 
 #define NJS_STRING_SHORT              14
 #define NJS_STRING_LONG               15
@@ -196,7 +197,7 @@ union njs_value_s {
     } short_string;
 
     struct {
-        njs_value_type_t              type:8;  /* 5 bits */
+        njs_value_type_t              type:8;  /* 6 bits */
         uint8_t                       truth;
 
         /* 0xff if data is external string. */
@@ -208,7 +209,7 @@ union njs_value_s {
     } long_string;
 
     struct {
-        njs_value_type_t              type:8;  /* 5 bits */
+        njs_value_type_t              type:8;  /* 6 bits */
         uint8_t                       truth;
 
         uint16_t                      _spare;
@@ -217,7 +218,7 @@ union njs_value_s {


More information about the nginx-devel mailing list