From b9ea5b5ced991afa5b73c3f1e87bea653772b57a Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Fri, 30 Mar 2018 18:50:38 +0300 Subject: [PATCH] Nodejs style crypto methods. var crypto = require('crypto') var hash = crypto.createHash() available algorithms: 'md5', 'sha1', 'sha256'. hash.update() hash.digest([]) available encodings: 'hex', 'base64'. var hmac = crypto.createHmac(, ) available algorithms: 'md5', 'sha1', 'sha256'. hmac.update() hmac.digest([]) available encodings: 'hex', 'base64'. --- 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(-) create mode 100644 njs/njs_crypto.c create mode 100644 njs/njs_crypto.h create mode 100644 nxt/nxt_md5.c create mode 100644 nxt/nxt_md5.h create mode 100644 nxt/nxt_sha1.c create mode 100644 nxt/nxt_sha1.h create mode 100644 nxt/nxt_sha2.c create mode 100644 nxt/nxt_sha2.h diff --git a/Makefile b/Makefile index 23fbc335..c984e04a 100644 --- 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 \ diff --git a/njs/njs_builtin.c b/njs/njs_builtin.c index 84ba3592..a9c25d2e 100644 --- a/njs/njs_builtin.c +++ b/njs/njs_builtin.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -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 index 00000000..ae63d93b --- /dev/null +++ b/njs/njs_crypto.c @@ -0,0 +1,713 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 index 00000000..c45b8775 --- /dev/null +++ b/njs/njs_crypto.h @@ -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 --git a/njs/njs_object.c b/njs/njs_object.c index 9d78861b..013424ee 100644 --- a/njs/njs_object.c +++ b/njs/njs_object.c @@ -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; diff --git a/njs/njs_vm.c b/njs/njs_vm.c index 57161987..4bd93b37 100644 --- a/njs/njs_vm.c +++ b/njs/njs_vm.c @@ -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, ...) { diff --git a/njs/njs_vm.h b/njs/njs_vm.h index 38415a6d..6d605c43 100644 --- a/njs/njs_vm.h +++ b/njs/njs_vm.h @@ -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) }; diff --git a/njs/njscript.h b/njs/njscript.h index c64f4404..bd14f51f 100644 --- a/njs/njscript.h +++ b/njs/njscript.h @@ -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, ...); diff --git a/njs/test/njs_interactive_test.c b/njs/test/njs_interactive_test.c index 55fe25b0..a477706c 100644 --- a/njs/test/njs_interactive_test.c +++ b/njs/test/njs_interactive_test.c @@ -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" diff --git a/njs/test/njs_unit_test.c b/njs/test/njs_unit_test.c index 61b20e45..73ed0a72 100644 --- a/njs/test/njs_unit_test.c +++ b/njs/test/njs_unit_test.c @@ -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()"), diff --git a/nxt/Makefile b/nxt/Makefile index cb51c74e..37b7e02c 100644 --- a/nxt/Makefile +++ b/nxt/Makefile @@ -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 index 00000000..9957b68b --- /dev/null +++ b/nxt/nxt_md5.c @@ -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 +#include +#include +#include +#include + + +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 index 00000000..8c39d25d --- /dev/null +++ b/nxt/nxt_md5.h @@ -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 index 00000000..6c35e82c --- /dev/null +++ b/nxt/nxt_sha1.c @@ -0,0 +1,298 @@ + +/* + * Copyright (C) Maxim Dounin + * Copyright (C) NGINX, Inc. + * + * An internal SHA1 implementation. + */ + + +#include +#include +#include +#include +#include + + +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 index 00000000..2816982b --- /dev/null +++ b/nxt/nxt_sha1.h @@ -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 index 00000000..ecf4c936 --- /dev/null +++ b/nxt/nxt_sha2.c @@ -0,0 +1,320 @@ + +/* + * Copyright (C) Dmitry Volyntsev + * Copyright (C) NGINX, Inc. + * + * An internal SHA2 implementation. + */ + + +#include +#include +#include +#include +#include + + +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 index 00000000..778cd28e --- /dev/null +++ b/nxt/nxt_sha2.h @@ -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_ */ -- 2.47.3