]> git.kaiwu.me - njs.git/commitdiff
Nodejs style crypto methods.
authorDmitry Volyntsev <xeioex@nginx.com>
Fri, 30 Mar 2018 15:50:38 +0000 (18:50 +0300)
committerDmitry Volyntsev <xeioex@nginx.com>
Fri, 30 Mar 2018 15:50:38 +0000 (18:50 +0300)
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'.

17 files changed:
Makefile
njs/njs_builtin.c
njs/njs_crypto.c [new file with mode: 0644]
njs/njs_crypto.h [new file with mode: 0644]
njs/njs_object.c
njs/njs_vm.c
njs/njs_vm.h
njs/njscript.h
njs/test/njs_interactive_test.c
njs/test/njs_unit_test.c
nxt/Makefile
nxt/nxt_md5.c [new file with mode: 0644]
nxt/nxt_md5.h [new file with mode: 0644]
nxt/nxt_sha1.c [new file with mode: 0644]
nxt/nxt_sha1.h [new file with mode: 0644]
nxt/nxt_sha2.c [new file with mode: 0644]
nxt/nxt_sha2.h [new file with mode: 0644]

index 23fbc33501fc72b683f4d6edfcd59afe853bf7a8..c984e04a427d32f8657d748da7e7eb8d470bd28a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -24,6 +24,7 @@ $(NXT_BUILDDIR)/libnjs.a: \
        $(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)/libnjs.a: \
        $(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)/libnjs.a: \
                $(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)/libnjs.a: \
                $(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 @@ $(NXT_BUILDDIR)/njs_fs.o: \
                -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 \
index 84ba35929ee161ec07846748263ee72c52617368..a9c25d2e7c1fcd971079933db962e976f340a7c5 100644 (file)
@@ -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_init[] = {
 
 
 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_init[] = {
     &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_constructor_init[] = {
     &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 --git a/njs/njs_crypto.c b/njs/njs_crypto.c
new file mode 100644 (file)
index 0000000..ae63d93
--- /dev/null
@@ -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 --git a/njs/njs_crypto.h b/njs/njs_crypto.h
new file mode 100644 (file)
index 0000000..c45b877
--- /dev/null
@@ -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_ */
index 9d78861bcfaa5e17ea51ac78cb7cd95d0e4c884b..013424ee29f9b67a48e2a49dc009c356f4ef0a52 100644 (file)
@@ -1332,6 +1332,10 @@ static const njs_value_t  njs_object_number_string =
                                      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_type_error_string =
                                      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 *vm, njs_value_t *args,
         &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 *vm, njs_value_t *args,
         &njs_object_syntax_error_string,
         &njs_object_type_error_string,
         &njs_object_uri_error_string,
+        &njs_object_object_value_string,
     };
 
     index = args[0].type;
index 57161987f3b51c5e44a35ed96ebe20c165428191..4bd93b37f3812b728be45815b55cf5301f7ac0c5 100644 (file)
@@ -1036,6 +1036,7 @@ njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *object,
     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, double num)
 }
 
 
+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, ...)
 {
index 38415a6d8a9e3bd52dcba87235693844f2f7231f..6d605c434853372948b26185a603ebab130cee0e 100644 (file)
@@ -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 {
         const njs_extern_t            *proto;
     } external;
 
-    njs_value_type_t                  type:8;  /* 5 bits */
+    njs_value_type_t                  type:8;  /* 6 bits */
 };
 
 
@@ -480,8 +481,16 @@ typedef njs_ret_t (*njs_vmcode_operation_t)(njs_vm_t *vm, njs_value_t *value1,
     ((value)->type <= NJS_STRING)
 
 
+#define njs_is_data(value)                                                    \
+    ((value)->type == NJS_DATA)
+
+
 #define njs_is_object(value)                                                  \
-    (((value)->type & NJS_OBJECT) != 0)
+    ((value)->type >= NJS_OBJECT)
+
+
+#define njs_is_object_value(value)                                            \
+    ((value)->type == NJS_OBJECT_VALUE)
 
 
 #define njs_object_value_type(type)                                           \
@@ -808,6 +817,8 @@ enum njs_prototypes_e {
     NJS_PROTOTYPE_FUNCTION,
     NJS_PROTOTYPE_REGEXP,
     NJS_PROTOTYPE_DATE,
+    NJS_PROTOTYPE_CRYPTO_HASH,
+    NJS_PROTOTYPE_CRYPTO_HMAC,
     NJS_PROTOTYPE_ERROR,
     NJS_PROTOTYPE_EVAL_ERROR,
     NJS_PROTOTYPE_INTERNAL_ERROR,
@@ -841,6 +852,8 @@ enum njs_constructor_e {
     NJS_CONSTRUCTOR_FUNCTION =       NJS_PROTOTYPE_FUNCTION,
     NJS_CONSTRUCTOR_REGEXP =         NJS_PROTOTYPE_REGEXP,
     NJS_CONSTRUCTOR_DATE =           NJS_PROTOTYPE_DATE,
+    NJS_CONSTRUCTOR_CRYPTO_HASH =    NJS_PROTOTYPE_CRYPTO_HASH,
+    NJS_CONSTRUCTOR_CRYPTO_HMAC =    NJS_PROTOTYPE_CRYPTO_HMAC,
     NJS_CONSTRUCTOR_ERROR =          NJS_PROTOTYPE_ERROR,
     NJS_CONSTRUCTOR_EVAL_ERROR =     NJS_PROTOTYPE_EVAL_ERROR,
     NJS_CONSTRUCTOR_INTERNAL_ERROR = NJS_PROTOTYPE_INTERNAL_ERROR,
@@ -865,7 +878,8 @@ enum njs_object_e {
 
 enum njs_module_e {
     NJS_MODULE_FS = 0,
-#define NJS_MODULE_MAX         (NJS_MODULE_FS + 1)
+    NJS_MODULE_CRYPTO,
+#define NJS_MODULE_MAX         (NJS_MODULE_CRYPTO + 1)
 };
 
 
index c64f4404a76ad79fa833606c79efb140d98a116d..bd14f51f5e435c0cbdc740b5491ebb5dec9ecd86 100644 (file)
@@ -179,6 +179,7 @@ NXT_EXPORT njs_ret_t njs_vm_retval_to_ext_string(njs_vm_t *vm,
 NXT_EXPORT void njs_value_void_set(njs_value_t *value);
 NXT_EXPORT void njs_value_boolean_set(njs_value_t *value, int yn);
 NXT_EXPORT void njs_value_number_set(njs_value_t *value, double num);
+NXT_EXPORT void njs_value_data_set(njs_value_t *value, void *data);
 NXT_EXPORT void njs_value_error_set(njs_vm_t *vm, njs_value_t *value,
     const char *fmt, ...);
 
index 55fe25b01fa6870b24e2c8490a5c798483d78b49..a477706c41b1adf62e4d7de7b47103d4a5cdac34 100644 (file)
@@ -177,6 +177,28 @@ static njs_interactive_test_t  njs_test[] =
                  "    at setTimeout (native)\n"
                  "    at main (native)\n") },
 
+    { nxt_string("require('crypto').createHash('sha')" ENTER),
+      nxt_string("TypeError: not supported algorithm: 'sha'\n"
+                 "    at crypto.createHash (native)\n"
+                 "    at main (native)\n") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1')" ENTER
+                 "h.update([])" ENTER),
+      nxt_string("TypeError: data must be a string\n"
+                 "    at Hash.prototype.update (native)\n"
+                 "    at main (native)\n") },
+
+    { nxt_string("require('crypto').createHmac('sha1', [])" ENTER),
+      nxt_string("TypeError: key must be a string\n"
+                 "    at crypto.createHmac (native)\n"
+                 "    at main (native)\n") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret')" ENTER
+                 "h.update([])" ENTER),
+      nxt_string("TypeError: data must be a string\n"
+                 "    at Hmac.prototype.update (native)\n"
+                 "    at main (native)\n") },
+
     { nxt_string("function f(o) {function f_in(o) {return o.a.a};"
                  "               return f_in(o)}; f({})" ENTER),
       nxt_string("TypeError: cannot get property 'a' of undefined\n"
index 61b20e45b36ea965f70252d3e70e58cf2eb1e355..73ed0a72bfdcb17df656ad2c375bcaa6428779bd 100644 (file)
@@ -9062,6 +9062,169 @@ static njs_unit_test_t  njs_test[] =
                  "fs.writeFileSync('/njs_unknown_path', '', true)"),
       nxt_string("TypeError: Unknown options type (a string or object required)") },
 
+    /* require('crypto').createHash() */
+
+    { nxt_string("require('crypto').createHash('sha1')"),
+      nxt_string("[object Hash]") },
+
+    { nxt_string("var h = require('crypto').createHash('md5');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("b86fc6b051f63d73de262d4c34e3a0a9") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('A').update('B').digest('hex')"),
+      nxt_string("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('AB').digest().toString('hex')"),
+      nxt_string("06d945942aa26a61be18c3e22bf19bbca8dd2b5d") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('AB').digest('base64')"),
+      nxt_string("BtlFlCqiamG+GMPiK/GbvKjdK10=") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('AB').digest().toString('base64')"),
+      nxt_string("BtlFlCqiamG+GMPiK/GbvKjdK10=") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("c95466320eaae6d19ee314ae4f135b12d45ced9a") },
+
+    { nxt_string("var h = require('crypto').createHash('sha256');"
+                 "h.update('A').update('B').digest('hex')"),
+      nxt_string("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153") },
+
+    { nxt_string("var h = require('crypto').createHash('sha256');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("38164fbd17603d73f696b8b4d72664d735bb6a7c88577687fd2ae33fd6964153") },
+
+    { nxt_string("var h = require('crypto').createHash('sha256');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("d9f5aeb06abebb3be3f38adec9a2e3b94228d52193be923eb4e24c9b56ee0930") },
+
+    { nxt_string("var h = require('crypto').createHash()"),
+      nxt_string("TypeError: algorithm must be a string") },
+
+    { nxt_string("var h = require('crypto').createHash([])"),
+      nxt_string("TypeError: algorithm must be a string") },
+
+    { nxt_string("var h = require('crypto').createHash('sha512')"),
+      nxt_string("TypeError: not supported algorithm: 'sha512'") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update()"),
+      nxt_string("TypeError: data must be a string") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update({})"),
+      nxt_string("TypeError: data must be a string") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('A').digest('latin1')"),
+      nxt_string("TypeError: Unknown digest encoding: 'latin1'") },
+
+    { nxt_string("var h = require('crypto').createHash('sha1');"
+                 "h.update('A').digest('hex'); h.digest('hex')"),
+      nxt_string("Error: Digest already called") },
+
+    /* require('crypto').createHash() */
+
+    { nxt_string("require('crypto').createHmac('sha1', '')"),
+      nxt_string("[object Hmac]") },
+
+    { nxt_string("var h = require('crypto').createHmac('md5', '');"
+                 "h.digest('hex')"),
+      nxt_string("74e6f7298a9c2d168935f58c001bad88") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', '');"
+                 "h.digest('hex')"),
+      nxt_string("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', '');"
+                 "h.digest().toString('hex')"),
+      nxt_string("fbdb1d1b18aa6c08324b7d64b71fb76370690e1d") },
+
+    { nxt_string("var h = require('crypto').createHmac('md5', 'secret key');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("9c72728915eb26620a5caeafd0063b29") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('A').update('B').digest('hex')"),
+      nxt_string("adc60e03459c4bae7cf4eb6d9730003e9490b22f") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("adc60e03459c4bae7cf4eb6d9730003e9490b22f") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('AB').digest('base64')"),
+      nxt_string("rcYOA0WcS6589OttlzAAPpSQsi8=") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('AB').digest().toString('base64')"),
+      nxt_string("rcYOA0WcS6589OttlzAAPpSQsi8=") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("b105ad6921e4c54d3fa0a9ec3f7f0ee9bd2c659d") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'A'.repeat(64));"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("400ce530816c6b3247e2959f3982a12aaf58c0c9") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'A'.repeat(100));"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("670e7cdebae6392797e000e79e51d3b6589d8fad") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', '');"
+                 "h.digest('hex')"),
+      nxt_string("b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', 'secret key');"
+                 "h.update('A').update('B').digest('hex')"),
+      nxt_string("46085184b3b45a13d838bf71a0ce03675dab30931e0f1f68fa636ea65fdb286d") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', 'secret key');"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("46085184b3b45a13d838bf71a0ce03675dab30931e0f1f68fa636ea65fdb286d") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', 'A'.repeat(64));"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("ee9dce43b12eb3e865614ad9c1a8d4fad4b6eac2b64647bd24cd192888d3f367") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', 'A'.repeat(100));"
+                 "h.update('AB').digest('hex')"),
+      nxt_string("5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc") },
+
+    { nxt_string("var h = require('crypto').createHmac('md5', 'secret key');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("5dd706af43536f8c9c83e7ea55b1a5a2") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', 'secret key');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("b105ad6921e4c54d3fa0a9ec3f7f0ee9bd2c659d") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha256', 'secret key');"
+                 "h.update('abc'.repeat(100)).digest('hex')"),
+      nxt_string("f6550d398ce350ee8d94a0f44f2cf6b9bc8d316ae4625fb4434f22980a276bac") },
+
+    { nxt_string("var h = require('crypto').createHmac()"),
+      nxt_string("TypeError: algorithm must be a string") },
+
+    { nxt_string("var h = require('crypto').createHmac([])"),
+      nxt_string("TypeError: algorithm must be a string") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha512', '')"),
+      nxt_string("TypeError: not supported algorithm: 'sha512'") },
+
+    { nxt_string("var h = require('crypto').createHmac('sha1', [])"),
+      nxt_string("TypeError: key must be a string") },
+
     /* setTimeout(). */
 
     { nxt_string("setTimeout()"),
index cb51c74e43b4ee07308bafc6b061cbb5030edde6..37b7e02c91d753a3cf703d347140b641111180ec 100644 (file)
@@ -11,6 +11,9 @@ $(NXT_BUILDDIR)/libnxt.a: \
        $(NXT_BUILDDIR)/nxt_rbtree.o \
        $(NXT_BUILDDIR)/nxt_lvlhsh.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_trace.o \
@@ -23,6 +26,9 @@ $(NXT_BUILDDIR)/libnxt.a: \
                $(NXT_BUILDDIR)/nxt_rbtree.o \
                $(NXT_BUILDDIR)/nxt_lvlhsh.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_trace.o \
@@ -111,6 +117,36 @@ $(NXT_BUILDDIR)/nxt_random.o: \
                -I$(NXT_LIB) \
                $(NXT_LIB)/nxt_random.c
 
+$(NXT_BUILDDIR)/nxt_md5.o: \
+       $(NXT_LIB)/nxt_types.h \
+       $(NXT_LIB)/nxt_clang.h \
+       $(NXT_LIB)/nxt_md5.h \
+       $(NXT_LIB)/nxt_md5.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_md5.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) \
+               $(NXT_LIB)/nxt_md5.c
+
+$(NXT_BUILDDIR)/nxt_sha1.o: \
+       $(NXT_LIB)/nxt_types.h \
+       $(NXT_LIB)/nxt_clang.h \
+       $(NXT_LIB)/nxt_sha1.h \
+       $(NXT_LIB)/nxt_sha1.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_sha1.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) \
+               $(NXT_LIB)/nxt_sha1.c
+
+$(NXT_BUILDDIR)/nxt_sha2.o: \
+       $(NXT_LIB)/nxt_types.h \
+       $(NXT_LIB)/nxt_clang.h \
+       $(NXT_LIB)/nxt_sha2.h \
+       $(NXT_LIB)/nxt_sha2.c \
+
+       $(NXT_CC) -c -o $(NXT_BUILDDIR)/nxt_sha2.o $(NXT_CFLAGS) \
+               -I$(NXT_LIB) \
+               $(NXT_LIB)/nxt_sha2.c
+
 $(NXT_BUILDDIR)/nxt_pcre.o: \
        $(NXT_LIB)/nxt_types.h \
        $(NXT_LIB)/nxt_clang.h \
diff --git a/nxt/nxt_md5.c b/nxt/nxt_md5.c
new file mode 100644 (file)
index 0000000..9957b68
--- /dev/null
@@ -0,0 +1,270 @@
+
+/*
+ * An internal implementation, based on Alexander Peslyak's
+ * public domain implementation:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ */
+
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_md5.h>
+#include <string.h>
+
+
+static const u_char *nxt_md5_body(nxt_md5_t *ctx, const u_char *data,
+    size_t size);
+
+
+void
+nxt_md5_init(nxt_md5_t *ctx)
+{
+    ctx->a = 0x67452301;
+    ctx->b = 0xefcdab89;
+    ctx->c = 0x98badcfe;
+    ctx->d = 0x10325476;
+
+    ctx->bytes = 0;
+}
+
+
+void
+nxt_md5_update(nxt_md5_t *ctx, const void *data, size_t size)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+    ctx->bytes += size;
+
+    if (used) {
+        free = 64 - used;
+
+        if (size < free) {
+            memcpy(&ctx->buffer[used], data, size);
+            return;
+        }
+
+        memcpy(&ctx->buffer[used], data, free);
+        data = (u_char *) data + free;
+        size -= free;
+        (void) nxt_md5_body(ctx, ctx->buffer, 64);
+    }
+
+    if (size >= 64) {
+        data = nxt_md5_body(ctx, data, size & ~(size_t) 0x3f);
+        size &= 0x3f;
+    }
+
+    memcpy(ctx->buffer, data, size);
+}
+
+
+void
+nxt_md5_final(u_char result[16], nxt_md5_t *ctx)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+
+    ctx->buffer[used++] = 0x80;
+
+    free = 64 - used;
+
+    if (free < 8) {
+        memset(&ctx->buffer[used], 0, free);
+        (void) nxt_md5_body(ctx, ctx->buffer, 64);
+        used = 0;
+        free = 64;
+    }
+
+    memset(&ctx->buffer[used], 0, free - 8);
+
+    ctx->bytes <<= 3;
+    ctx->buffer[56] = (u_char)  ctx->bytes;
+    ctx->buffer[57] = (u_char) (ctx->bytes >> 8);
+    ctx->buffer[58] = (u_char) (ctx->bytes >> 16);
+    ctx->buffer[59] = (u_char) (ctx->bytes >> 24);
+    ctx->buffer[60] = (u_char) (ctx->bytes >> 32);
+    ctx->buffer[61] = (u_char) (ctx->bytes >> 40);
+    ctx->buffer[62] = (u_char) (ctx->bytes >> 48);
+    ctx->buffer[63] = (u_char) (ctx->bytes >> 56);
+
+    (void) nxt_md5_body(ctx, ctx->buffer, 64);
+
+    result[0]  = (u_char)  ctx->a;
+    result[1]  = (u_char) (ctx->a >> 8);
+    result[2]  = (u_char) (ctx->a >> 16);
+    result[3]  = (u_char) (ctx->a >> 24);
+    result[4]  = (u_char)  ctx->b;
+    result[5]  = (u_char) (ctx->b >> 8);
+    result[6]  = (u_char) (ctx->b >> 16);
+    result[7]  = (u_char) (ctx->b >> 24);
+    result[8]  = (u_char)  ctx->c;
+    result[9]  = (u_char) (ctx->c >> 8);
+    result[10] = (u_char) (ctx->c >> 16);
+    result[11] = (u_char) (ctx->c >> 24);
+    result[12] = (u_char)  ctx->d;
+    result[13] = (u_char) (ctx->d >> 8);
+    result[14] = (u_char) (ctx->d >> 16);
+    result[15] = (u_char) (ctx->d >> 24);
+
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in
+ * Colin Plumb's implementation.
+ */
+
+#define F(x, y, z)  ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)  ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)  ((x) ^ (y) ^ (z))
+#define I(x, y, z)  ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+
+#define STEP(f, a, b, c, d, x, t, s)                                          \
+    (a) += f((b), (c), (d)) + (x) + (t);                                      \
+    (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));                \
+    (a) += (b)
+
+/*
+ * SET() reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ */
+
+#define SET(n)                                                                \
+    (block[n] =                                                               \
+     (   (uint32_t) p[n * 4]                                                  \
+      | ((uint32_t) p[n * 4 + 1] << 8)                                        \
+      | ((uint32_t) p[n * 4 + 2] << 16)                                       \
+      | ((uint32_t) p[n * 4 + 3] << 24)))                                     \
+
+#define GET(n)      block[n]
+
+
+/*
+ * This processes one or more 64-byte data blocks, but does not update
+ * the bit counters.  There are no alignment requirements.
+ */
+
+static const u_char *
+nxt_md5_body(nxt_md5_t *ctx, const u_char *data, size_t size)
+{
+    uint32_t       a, b, c, d;
+    uint32_t       saved_a, saved_b, saved_c, saved_d;
+    const u_char   *p;
+    uint32_t       block[16];
+
+    p = data;
+
+    a = ctx->a;
+    b = ctx->b;
+    c = ctx->c;
+    d = ctx->d;
+
+    do {
+        saved_a = a;
+        saved_b = b;
+        saved_c = c;
+        saved_d = d;
+
+        /* Round 1 */
+
+        STEP(F, a, b, c, d, SET(0),  0xd76aa478, 7);
+        STEP(F, d, a, b, c, SET(1),  0xe8c7b756, 12);
+        STEP(F, c, d, a, b, SET(2),  0x242070db, 17);
+        STEP(F, b, c, d, a, SET(3),  0xc1bdceee, 22);
+        STEP(F, a, b, c, d, SET(4),  0xf57c0faf, 7);
+        STEP(F, d, a, b, c, SET(5),  0x4787c62a, 12);
+        STEP(F, c, d, a, b, SET(6),  0xa8304613, 17);
+        STEP(F, b, c, d, a, SET(7),  0xfd469501, 22);
+        STEP(F, a, b, c, d, SET(8),  0x698098d8, 7);
+        STEP(F, d, a, b, c, SET(9),  0x8b44f7af, 12);
+        STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17);
+        STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22);
+        STEP(F, a, b, c, d, SET(12), 0x6b901122, 7);
+        STEP(F, d, a, b, c, SET(13), 0xfd987193, 12);
+        STEP(F, c, d, a, b, SET(14), 0xa679438e, 17);
+        STEP(F, b, c, d, a, SET(15), 0x49b40821, 22);
+
+        /* Round 2 */
+
+        STEP(G, a, b, c, d, GET(1),  0xf61e2562, 5);
+        STEP(G, d, a, b, c, GET(6),  0xc040b340, 9);
+        STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14);
+        STEP(G, b, c, d, a, GET(0),  0xe9b6c7aa, 20);
+        STEP(G, a, b, c, d, GET(5),  0xd62f105d, 5);
+        STEP(G, d, a, b, c, GET(10), 0x02441453, 9);
+        STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14);
+        STEP(G, b, c, d, a, GET(4),  0xe7d3fbc8, 20);
+        STEP(G, a, b, c, d, GET(9),  0x21e1cde6, 5);
+        STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9);
+        STEP(G, c, d, a, b, GET(3),  0xf4d50d87, 14);
+        STEP(G, b, c, d, a, GET(8),  0x455a14ed, 20);
+        STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5);
+        STEP(G, d, a, b, c, GET(2),  0xfcefa3f8, 9);
+        STEP(G, c, d, a, b, GET(7),  0x676f02d9, 14);
+        STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20);
+
+        /* Round 3 */
+
+        STEP(H, a, b, c, d, GET(5),  0xfffa3942, 4);
+        STEP(H, d, a, b, c, GET(8),  0x8771f681, 11);
+        STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16);
+        STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23);
+        STEP(H, a, b, c, d, GET(1),  0xa4beea44, 4);
+        STEP(H, d, a, b, c, GET(4),  0x4bdecfa9, 11);
+        STEP(H, c, d, a, b, GET(7),  0xf6bb4b60, 16);
+        STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23);
+        STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4);
+        STEP(H, d, a, b, c, GET(0),  0xeaa127fa, 11);
+        STEP(H, c, d, a, b, GET(3),  0xd4ef3085, 16);
+        STEP(H, b, c, d, a, GET(6),  0x04881d05, 23);
+        STEP(H, a, b, c, d, GET(9),  0xd9d4d039, 4);
+        STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11);
+        STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16);
+        STEP(H, b, c, d, a, GET(2),  0xc4ac5665, 23);
+
+        /* Round 4 */
+
+        STEP(I, a, b, c, d, GET(0),  0xf4292244, 6);
+        STEP(I, d, a, b, c, GET(7),  0x432aff97, 10);
+        STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15);
+        STEP(I, b, c, d, a, GET(5),  0xfc93a039, 21);
+        STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6);
+        STEP(I, d, a, b, c, GET(3),  0x8f0ccc92, 10);
+        STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15);
+        STEP(I, b, c, d, a, GET(1),  0x85845dd1, 21);
+        STEP(I, a, b, c, d, GET(8),  0x6fa87e4f, 6);
+        STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10);
+        STEP(I, c, d, a, b, GET(6),  0xa3014314, 15);
+        STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21);
+        STEP(I, a, b, c, d, GET(4),  0xf7537e82, 6);
+        STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10);
+        STEP(I, c, d, a, b, GET(2),  0x2ad7d2bb, 15);
+        STEP(I, b, c, d, a, GET(9),  0xeb86d391, 21);
+
+        a += saved_a;
+        b += saved_b;
+        c += saved_c;
+        d += saved_d;
+
+        p += 64;
+
+    } while (size -= 64);
+
+    ctx->a = a;
+    ctx->b = b;
+    ctx->c = c;
+    ctx->d = d;
+
+    return p;
+}
diff --git a/nxt/nxt_md5.h b/nxt/nxt_md5.h
new file mode 100644 (file)
index 0000000..8c39d25
--- /dev/null
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#ifndef _NXT_MD5_H_INCLUDED_
+#define _NXT_MD5_H_INCLUDED_
+
+
+typedef struct {
+    uint64_t  bytes;
+    uint32_t  a, b, c, d;
+    u_char    buffer[64];
+} nxt_md5_t;
+
+
+NXT_EXPORT void nxt_md5_init(nxt_md5_t *ctx);
+NXT_EXPORT void nxt_md5_update(nxt_md5_t *ctx, const void *data, size_t size);
+NXT_EXPORT void nxt_md5_final(u_char result[16], nxt_md5_t *ctx);
+
+#endif /* _NXT_MD5_H_INCLUDED_ */
diff --git a/nxt/nxt_sha1.c b/nxt/nxt_sha1.c
new file mode 100644 (file)
index 0000000..6c35e82
--- /dev/null
@@ -0,0 +1,298 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ * Copyright (C) NGINX, Inc.
+ *
+ * An internal SHA1 implementation.
+ */
+
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_sha1.h>
+#include <string.h>
+
+
+static const u_char *nxt_sha1_body(nxt_sha1_t *ctx, const u_char *data,
+    size_t size);
+
+
+void
+nxt_sha1_init(nxt_sha1_t *ctx)
+{
+    ctx->a = 0x67452301;
+    ctx->b = 0xefcdab89;
+    ctx->c = 0x98badcfe;
+    ctx->d = 0x10325476;
+    ctx->e = 0xc3d2e1f0;
+
+    ctx->bytes = 0;
+}
+
+
+void
+nxt_sha1_update(nxt_sha1_t *ctx, const void *data, size_t size)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+    ctx->bytes += size;
+
+    if (used) {
+        free = 64 - used;
+
+        if (size < free) {
+            memcpy(&ctx->buffer[used], data, size);
+            return;
+        }
+
+        memcpy(&ctx->buffer[used], data, free);
+        data = (u_char *) data + free;
+        size -= free;
+        (void) nxt_sha1_body(ctx, ctx->buffer, 64);
+    }
+
+    if (size >= 64) {
+        data = nxt_sha1_body(ctx, data, size & ~(size_t) 0x3f);
+        size &= 0x3f;
+    }
+
+    memcpy(ctx->buffer, data, size);
+}
+
+
+void
+nxt_sha1_final(u_char result[20], nxt_sha1_t *ctx)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+
+    ctx->buffer[used++] = 0x80;
+
+    free = 64 - used;
+
+    if (free < 8) {
+        memset(&ctx->buffer[used], 0, free);
+        (void) nxt_sha1_body(ctx, ctx->buffer, 64);
+        used = 0;
+        free = 64;
+    }
+
+    memset(&ctx->buffer[used], 0, free - 8);
+
+    ctx->bytes <<= 3;
+    ctx->buffer[56] = (u_char) (ctx->bytes >> 56);
+    ctx->buffer[57] = (u_char) (ctx->bytes >> 48);
+    ctx->buffer[58] = (u_char) (ctx->bytes >> 40);
+    ctx->buffer[59] = (u_char) (ctx->bytes >> 32);
+    ctx->buffer[60] = (u_char) (ctx->bytes >> 24);
+    ctx->buffer[61] = (u_char) (ctx->bytes >> 16);
+    ctx->buffer[62] = (u_char) (ctx->bytes >> 8);
+    ctx->buffer[63] = (u_char)  ctx->bytes;
+
+    (void) nxt_sha1_body(ctx, ctx->buffer, 64);
+
+    result[0]  = (u_char) (ctx->a >> 24);
+    result[1]  = (u_char) (ctx->a >> 16);
+    result[2]  = (u_char) (ctx->a >> 8);
+    result[3]  = (u_char)  ctx->a;
+    result[4]  = (u_char) (ctx->b >> 24);
+    result[5]  = (u_char) (ctx->b >> 16);
+    result[6]  = (u_char) (ctx->b >> 8);
+    result[7]  = (u_char)  ctx->b;
+    result[8]  = (u_char) (ctx->c >> 24);
+    result[9]  = (u_char) (ctx->c >> 16);
+    result[10] = (u_char) (ctx->c >> 8);
+    result[11] = (u_char)  ctx->c;
+    result[12] = (u_char) (ctx->d >> 24);
+    result[13] = (u_char) (ctx->d >> 16);
+    result[14] = (u_char) (ctx->d >> 8);
+    result[15] = (u_char)  ctx->d;
+    result[16] = (u_char) (ctx->e >> 24);
+    result[17] = (u_char) (ctx->e >> 16);
+    result[18] = (u_char) (ctx->e >> 8);
+    result[19] = (u_char)  ctx->e;
+
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+
+/*
+ * Helper functions.
+ */
+
+#define ROTATE(bits, word)  (((word) << (bits)) | ((word) >> (32 - (bits))))
+
+#define F1(b, c, d)  (((b) & (c)) | ((~(b)) & (d)))
+#define F2(b, c, d)  ((b) ^ (c) ^ (d))
+#define F3(b, c, d)  (((b) & (c)) | ((b) & (d)) | ((c) & (d)))
+
+#define STEP(f, a, b, c, d, e, w, t)                                          \
+    temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t);               \
+    (e) = (d);                                                                \
+    (d) = (c);                                                                \
+    (c) = ROTATE(30, (b));                                                    \
+    (b) = (a);                                                                \
+    (a) = temp;
+
+
+/*
+ * GET() reads 4 input bytes in big-endian byte order and returns
+ * them as uint32_t.
+ */
+
+#define GET(n)                                                                \
+    (  ((uint32_t) p[n * 4 + 3])                                              \
+     | ((uint32_t) p[n * 4 + 2] << 8)                                         \
+     | ((uint32_t) p[n * 4 + 1] << 16)                                        \
+     | ((uint32_t) p[n * 4]     << 24))
+
+
+/*
+ * This processes one or more 64-byte data blocks, but does not update
+ * the bit counters.  There are no alignment requirements.
+ */
+
+static const u_char *
+nxt_sha1_body(nxt_sha1_t *ctx, const u_char *data, size_t size)
+{
+    uint32_t       a, b, c, d, e, temp;
+    uint32_t       saved_a, saved_b, saved_c, saved_d, saved_e;
+    uint32_t       words[80];
+    nxt_uint_t     i;
+    const u_char  *p;
+
+    p = data;
+
+    a = ctx->a;
+    b = ctx->b;
+    c = ctx->c;
+    d = ctx->d;
+    e = ctx->e;
+
+    do {
+        saved_a = a;
+        saved_b = b;
+        saved_c = c;
+        saved_d = d;
+        saved_e = e;
+
+        /* Load data block into the words array */
+
+        for (i = 0; i < 16; i++) {
+            words[i] = GET(i);
+        }
+
+        for (i = 16; i < 80; i++) {
+            words[i] = ROTATE(1, words[i - 3]
+                                 ^ words[i - 8]
+                                 ^ words[i - 14]
+                                 ^ words[i - 16]);
+        }
+
+        /* Transformations */
+
+        STEP(F1, a, b, c, d, e, words[0],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[1],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[2],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[3],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[4],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[5],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[6],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[7],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[8],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[9],  0x5a827999);
+        STEP(F1, a, b, c, d, e, words[10], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[11], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[12], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[13], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[14], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[15], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[16], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[17], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[18], 0x5a827999);
+        STEP(F1, a, b, c, d, e, words[19], 0x5a827999);
+
+        STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1);
+        STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1);
+
+        STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc);
+        STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc);
+
+        STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6);
+        STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6);
+
+        a += saved_a;
+        b += saved_b;
+        c += saved_c;
+        d += saved_d;
+        e += saved_e;
+
+        p += 64;
+
+    } while (size -= 64);
+
+    ctx->a = a;
+    ctx->b = b;
+    ctx->c = c;
+    ctx->d = d;
+    ctx->e = e;
+
+    return p;
+}
diff --git a/nxt/nxt_sha1.h b/nxt/nxt_sha1.h
new file mode 100644 (file)
index 0000000..2816982
--- /dev/null
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#ifndef _NXT_SHA1_H_INCLUDED_
+#define _NXT_SHA1_H_INCLUDED_
+
+
+typedef struct {
+    uint64_t  bytes;
+    uint32_t  a, b, c, d, e;
+    u_char    buffer[64];
+} nxt_sha1_t;
+
+
+NXT_EXPORT void nxt_sha1_init(nxt_sha1_t *ctx);
+NXT_EXPORT void nxt_sha1_update(nxt_sha1_t *ctx, const void *data, size_t size);
+NXT_EXPORT void nxt_sha1_final(u_char result[20], nxt_sha1_t *ctx);
+
+
+#endif /* _NXT_SHA1_H_INCLUDED_ */
diff --git a/nxt/nxt_sha2.c b/nxt/nxt_sha2.c
new file mode 100644 (file)
index 0000000..ecf4c93
--- /dev/null
@@ -0,0 +1,320 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ *
+ * An internal SHA2 implementation.
+ */
+
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_sha2.h>
+#include <string.h>
+
+
+static const u_char *nxt_sha2_body(nxt_sha2_t *ctx, const u_char *data,
+    size_t size);
+
+
+void
+nxt_sha2_init(nxt_sha2_t *ctx)
+{
+    ctx->a = 0x6a09e667;
+    ctx->b = 0xbb67ae85;
+    ctx->c = 0x3c6ef372;
+    ctx->d = 0xa54ff53a;
+    ctx->e = 0x510e527f;
+    ctx->f = 0x9b05688c;
+    ctx->g = 0x1f83d9ab;
+    ctx->h = 0x5be0cd19;
+
+    ctx->bytes = 0;
+}
+
+
+void
+nxt_sha2_update(nxt_sha2_t *ctx, const void *data, size_t size)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+    ctx->bytes += size;
+
+    if (used) {
+        free = 64 - used;
+
+        if (size < free) {
+            memcpy(&ctx->buffer[used], data, size);
+            return;
+        }
+
+        memcpy(&ctx->buffer[used], data, free);
+        data = (u_char *) data + free;
+        size -= free;
+        (void) nxt_sha2_body(ctx, ctx->buffer, 64);
+    }
+
+    if (size >= 64) {
+        data = nxt_sha2_body(ctx, data, size & ~(size_t) 0x3f);
+        size &= 0x3f;
+    }
+
+    memcpy(ctx->buffer, data, size);
+}
+
+
+void
+nxt_sha2_final(u_char result[32], nxt_sha2_t *ctx)
+{
+    size_t  used, free;
+
+    used = (size_t) (ctx->bytes & 0x3f);
+
+    ctx->buffer[used++] = 0x80;
+
+    free = 64 - used;
+
+    if (free < 8) {
+        memset(&ctx->buffer[used], 0, free);
+        (void) nxt_sha2_body(ctx, ctx->buffer, 64);
+        used = 0;
+        free = 64;
+    }
+
+    memset(&ctx->buffer[used], 0, free - 8);
+
+    ctx->bytes <<= 3;
+    ctx->buffer[56] = (u_char) (ctx->bytes >> 56);
+    ctx->buffer[57] = (u_char) (ctx->bytes >> 48);
+    ctx->buffer[58] = (u_char) (ctx->bytes >> 40);
+    ctx->buffer[59] = (u_char) (ctx->bytes >> 32);
+    ctx->buffer[60] = (u_char) (ctx->bytes >> 24);
+    ctx->buffer[61] = (u_char) (ctx->bytes >> 16);
+    ctx->buffer[62] = (u_char) (ctx->bytes >> 8);
+    ctx->buffer[63] = (u_char)  ctx->bytes;
+
+    (void) nxt_sha2_body(ctx, ctx->buffer, 64);
+
+    result[0]  = (u_char) (ctx->a >> 24);
+    result[1]  = (u_char) (ctx->a >> 16);
+    result[2]  = (u_char) (ctx->a >> 8);
+    result[3]  = (u_char)  ctx->a;
+    result[4]  = (u_char) (ctx->b >> 24);
+    result[5]  = (u_char) (ctx->b >> 16);
+    result[6]  = (u_char) (ctx->b >> 8);
+    result[7]  = (u_char)  ctx->b;
+    result[8]  = (u_char) (ctx->c >> 24);
+    result[9]  = (u_char) (ctx->c >> 16);
+    result[10] = (u_char) (ctx->c >> 8);
+    result[11] = (u_char)  ctx->c;
+    result[12] = (u_char) (ctx->d >> 24);
+    result[13] = (u_char) (ctx->d >> 16);
+    result[14] = (u_char) (ctx->d >> 8);
+    result[15] = (u_char)  ctx->d;
+    result[16] = (u_char) (ctx->e >> 24);
+    result[17] = (u_char) (ctx->e >> 16);
+    result[18] = (u_char) (ctx->e >> 8);
+    result[19] = (u_char)  ctx->e;
+    result[20] = (u_char) (ctx->f >> 24);
+    result[21] = (u_char) (ctx->f >> 16);
+    result[22] = (u_char) (ctx->f >> 8);
+    result[23] = (u_char)  ctx->f;
+    result[24] = (u_char) (ctx->g >> 24);
+    result[25] = (u_char) (ctx->g >> 16);
+    result[26] = (u_char) (ctx->g >> 8);
+    result[27] = (u_char)  ctx->g;
+    result[28] = (u_char) (ctx->h >> 24);
+    result[29] = (u_char) (ctx->h >> 16);
+    result[30] = (u_char) (ctx->h >> 8);
+    result[31] = (u_char)  ctx->h;
+
+    memset(ctx, 0, sizeof(*ctx));
+}
+
+
+/*
+ * Helper functions.
+ */
+
+#define ROTATE(bits, word)  (((word) >> (bits)) | ((word) << (32 - (bits))))
+
+#define S0(a) (ROTATE(2, a) ^ ROTATE(13, a) ^ ROTATE(22, a))
+#define S1(e) (ROTATE(6, e) ^ ROTATE(11, e) ^ ROTATE(25, e))
+#define CH(e, f, g) (((e) & (f)) ^ ((~(e)) & (g)))
+#define MAJ(a, b, c) (((a) & (b)) ^ ((a) & (c)) ^ ((b) & (c)))
+
+#define STEP(a, b, c, d, e, f, g, h, w, k)                                    \
+    temp1 = (h) + S1(e) + CH(e, f, g) + (k) + (w);                            \
+    temp2 = S0(a) + MAJ(a, b, c);                                             \
+    (h) = (g);                                                                \
+    (g) = (f);                                                                \
+    (f) = (e);                                                                \
+    (e) = (d) + temp1;                                                        \
+    (d) = (c);                                                                \
+    (c) = (b);                                                                \
+    (b) = (a);                                                                \
+    (a) = temp1 + temp2;
+
+
+/*
+ * GET() reads 4 input bytes in big-endian byte order and returns
+ * them as uint32_t.
+ */
+
+#define GET(n)                                                                \
+    (  ((uint32_t) p[n * 4 + 3])                                              \
+     | ((uint32_t) p[n * 4 + 2] << 8)                                         \
+     | ((uint32_t) p[n * 4 + 1] << 16)                                        \
+     | ((uint32_t) p[n * 4]     << 24))
+
+
+/*
+ * This processes one or more 64-byte data blocks, but does not update
+ * the bit counters.  There are no alignment requirements.
+ */
+
+static const u_char *
+nxt_sha2_body(nxt_sha2_t *ctx, const u_char *data, size_t size)
+{
+    uint32_t       a, b, c, d, e, f, g, h, s0, s1, temp1, temp2;
+    uint32_t       saved_a, saved_b, saved_c, saved_d, saved_e, saved_f,
+                   saved_g, saved_h;
+    uint32_t       words[64];
+    nxt_uint_t     i;
+    const u_char  *p;
+
+    p = data;
+
+    a = ctx->a;
+    b = ctx->b;
+    c = ctx->c;
+    d = ctx->d;
+    e = ctx->e;
+    f = ctx->f;
+    g = ctx->g;
+    h = ctx->h;
+
+    do {
+        saved_a = a;
+        saved_b = b;
+        saved_c = c;
+        saved_d = d;
+        saved_e = e;
+        saved_f = f;
+        saved_g = g;
+        saved_h = h;
+
+        /* Load data block into the words array */
+
+        for (i = 0; i < 16; i++) {
+            words[i] = GET(i);
+        }
+
+        for (i = 16; i < 64; i++) {
+            s0 = ROTATE(7, words[i - 15])
+                 ^ ROTATE(18, words[i - 15])
+                 ^ (words[i - 15] >> 3);
+
+            s1 = ROTATE(17, words[i - 2])
+                 ^ ROTATE(19, words[i - 2])
+                 ^ (words[i - 2] >> 10);
+
+            words[i] = words[i - 16] + s0 + words[i - 7] + s1;
+        }
+
+        /* Transformations */
+
+        STEP(a, b, c, d, e, f, g, h, words[0],  0x428a2f98);
+        STEP(a, b, c, d, e, f, g, h, words[1],  0x71374491);
+        STEP(a, b, c, d, e, f, g, h, words[2],  0xb5c0fbcf);
+        STEP(a, b, c, d, e, f, g, h, words[3],  0xe9b5dba5);
+        STEP(a, b, c, d, e, f, g, h, words[4],  0x3956c25b);
+        STEP(a, b, c, d, e, f, g, h, words[5],  0x59f111f1);
+        STEP(a, b, c, d, e, f, g, h, words[6],  0x923f82a4);
+        STEP(a, b, c, d, e, f, g, h, words[7],  0xab1c5ed5);
+        STEP(a, b, c, d, e, f, g, h, words[8],  0xd807aa98);
+        STEP(a, b, c, d, e, f, g, h, words[9],  0x12835b01);
+        STEP(a, b, c, d, e, f, g, h, words[10], 0x243185be);
+        STEP(a, b, c, d, e, f, g, h, words[11], 0x550c7dc3);
+        STEP(a, b, c, d, e, f, g, h, words[12], 0x72be5d74);
+        STEP(a, b, c, d, e, f, g, h, words[13], 0x80deb1fe);
+        STEP(a, b, c, d, e, f, g, h, words[14], 0x9bdc06a7);
+        STEP(a, b, c, d, e, f, g, h, words[15], 0xc19bf174);
+
+        STEP(a, b, c, d, e, f, g, h, words[16], 0xe49b69c1);
+        STEP(a, b, c, d, e, f, g, h, words[17], 0xefbe4786);
+        STEP(a, b, c, d, e, f, g, h, words[18], 0x0fc19dc6);
+        STEP(a, b, c, d, e, f, g, h, words[19], 0x240ca1cc);
+        STEP(a, b, c, d, e, f, g, h, words[20], 0x2de92c6f);
+        STEP(a, b, c, d, e, f, g, h, words[21], 0x4a7484aa);
+        STEP(a, b, c, d, e, f, g, h, words[22], 0x5cb0a9dc);
+        STEP(a, b, c, d, e, f, g, h, words[23], 0x76f988da);
+        STEP(a, b, c, d, e, f, g, h, words[24], 0x983e5152);
+        STEP(a, b, c, d, e, f, g, h, words[25], 0xa831c66d);
+        STEP(a, b, c, d, e, f, g, h, words[26], 0xb00327c8);
+        STEP(a, b, c, d, e, f, g, h, words[27], 0xbf597fc7);
+        STEP(a, b, c, d, e, f, g, h, words[28], 0xc6e00bf3);
+        STEP(a, b, c, d, e, f, g, h, words[29], 0xd5a79147);
+        STEP(a, b, c, d, e, f, g, h, words[30], 0x06ca6351);
+        STEP(a, b, c, d, e, f, g, h, words[31], 0x14292967);
+
+        STEP(a, b, c, d, e, f, g, h, words[32], 0x27b70a85);
+        STEP(a, b, c, d, e, f, g, h, words[33], 0x2e1b2138);
+        STEP(a, b, c, d, e, f, g, h, words[34], 0x4d2c6dfc);
+        STEP(a, b, c, d, e, f, g, h, words[35], 0x53380d13);
+        STEP(a, b, c, d, e, f, g, h, words[36], 0x650a7354);
+        STEP(a, b, c, d, e, f, g, h, words[37], 0x766a0abb);
+        STEP(a, b, c, d, e, f, g, h, words[38], 0x81c2c92e);
+        STEP(a, b, c, d, e, f, g, h, words[39], 0x92722c85);
+        STEP(a, b, c, d, e, f, g, h, words[40], 0xa2bfe8a1);
+        STEP(a, b, c, d, e, f, g, h, words[41], 0xa81a664b);
+        STEP(a, b, c, d, e, f, g, h, words[42], 0xc24b8b70);
+        STEP(a, b, c, d, e, f, g, h, words[43], 0xc76c51a3);
+        STEP(a, b, c, d, e, f, g, h, words[44], 0xd192e819);
+        STEP(a, b, c, d, e, f, g, h, words[45], 0xd6990624);
+        STEP(a, b, c, d, e, f, g, h, words[46], 0xf40e3585);
+        STEP(a, b, c, d, e, f, g, h, words[47], 0x106aa070);
+
+        STEP(a, b, c, d, e, f, g, h, words[48], 0x19a4c116);
+        STEP(a, b, c, d, e, f, g, h, words[49], 0x1e376c08);
+        STEP(a, b, c, d, e, f, g, h, words[50], 0x2748774c);
+        STEP(a, b, c, d, e, f, g, h, words[51], 0x34b0bcb5);
+        STEP(a, b, c, d, e, f, g, h, words[52], 0x391c0cb3);
+        STEP(a, b, c, d, e, f, g, h, words[53], 0x4ed8aa4a);
+        STEP(a, b, c, d, e, f, g, h, words[54], 0x5b9cca4f);
+        STEP(a, b, c, d, e, f, g, h, words[55], 0x682e6ff3);
+        STEP(a, b, c, d, e, f, g, h, words[56], 0x748f82ee);
+        STEP(a, b, c, d, e, f, g, h, words[57], 0x78a5636f);
+        STEP(a, b, c, d, e, f, g, h, words[58], 0x84c87814);
+        STEP(a, b, c, d, e, f, g, h, words[59], 0x8cc70208);
+        STEP(a, b, c, d, e, f, g, h, words[60], 0x90befffa);
+        STEP(a, b, c, d, e, f, g, h, words[61], 0xa4506ceb);
+        STEP(a, b, c, d, e, f, g, h, words[62], 0xbef9a3f7);
+        STEP(a, b, c, d, e, f, g, h, words[63], 0xc67178f2);
+
+        a += saved_a;
+        b += saved_b;
+        c += saved_c;
+        d += saved_d;
+        e += saved_e;
+        f += saved_f;
+        g += saved_g;
+        h += saved_h;
+
+        p += 64;
+
+    } while (size -= 64);
+
+    ctx->a = a;
+    ctx->b = b;
+    ctx->c = c;
+    ctx->d = d;
+    ctx->e = e;
+    ctx->f = f;
+    ctx->g = g;
+    ctx->h = h;
+
+    return p;
+}
diff --git a/nxt/nxt_sha2.h b/nxt/nxt_sha2.h
new file mode 100644 (file)
index 0000000..778cd28
--- /dev/null
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#ifndef _NXT_SHA2_H_INCLUDED_
+#define _NXT_SHA2_H_INCLUDED_
+
+
+typedef struct {
+    uint64_t  bytes;
+    uint32_t  a, b, c, d, e, f, g, h;
+    u_char    buffer[64];
+} nxt_sha2_t;
+
+
+NXT_EXPORT void nxt_sha2_init(nxt_sha2_t *ctx);
+NXT_EXPORT void nxt_sha2_update(nxt_sha2_t *ctx, const void *data, size_t size);
+NXT_EXPORT void nxt_sha2_final(u_char result[32], nxt_sha2_t *ctx);
+
+
+#endif /* _NXT_SHA2_H_INCLUDED_ */