]> git.kaiwu.me - njs.git/commitdiff
Crypto: switched to OpenSSL EVP for hashing.
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 24 Mar 2026 01:46:02 +0000 (18:46 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Tue, 31 Mar 2026 01:38:02 +0000 (18:38 -0700)
Previously, the crypto module used built-in software implementations
for a limited set of hash algorithms (md5, sha1, sha256).  This
prevented users from using algorithms like sha384, sha512, and sha3
family, even when the underlying OpenSSL library supported them.

The change replaces built-in hash implementations with OpenSSL
EVP_MD_CTX for createHash() and HMAC_CTX for createHmac(), following
the webcrypto module.  Algorithm lookup now uses EVP_get_digestbyname(),
making any digest supported by the linked OpenSSL available to
JavaScript code.

The module now requires OpenSSL and is conditionally compiled, same as
the webcrypto module.  Builds without OpenSSL (--no-openssl) will no
longer have the crypto module available.

Tested with OpenSSL 3.0, OpenSSL 1.1.1w, LibreSSL 3.9.2, and
BoringSSL.  SHA-3 tests are skipped when the SSL library does not
support them (e.g. BoringSSL).

This closes #1037 feature request on Github.

12 files changed:
auto/modules
auto/qjs_modules
external/njs_crypto_module.c
external/njs_hash.h [deleted file]
external/njs_md5.c [deleted file]
external/njs_openssl.h
external/njs_sha1.c [deleted file]
external/njs_sha2.c [deleted file]
external/qjs_crypto_module.c
nginx/config
src/test/njs_unit_test.c
test/crypto.t.mjs

index 421ab2cffb8f3cbb79f0c1f401b8b7cbe9cc5f81..d207cc35e4d79001a7b9e5d4c036379ef22c73de 100644 (file)
@@ -7,16 +7,13 @@ njs_module_srcs=src/njs_buffer.c
 
 . auto/module
 
-njs_module_name=njs_crypto_module
-njs_module_incs=
-njs_module_srcs="external/njs_crypto_module.c \
-                 external/njs_md5.c \
-                 external/njs_sha1.c \
-                 external/njs_sha2.c"
+if [ $NJS_OPENSSL = YES -a $NJS_HAVE_OPENSSL = YES ]; then
+       njs_module_name=njs_crypto_module
+       njs_module_incs=
+       njs_module_srcs=external/njs_crypto_module.c
 
-. auto/module
+       . auto/module
 
-if [ $NJS_OPENSSL = YES -a $NJS_HAVE_OPENSSL = YES ]; then
        njs_module_name=njs_webcrypto_module
        njs_module_incs=
        njs_module_srcs=external/njs_webcrypto_module.c
index 03501d7d46558717b49e012e435e3dc8fd4c1fd2..c7e3d7089ce337078a95342d719d860125d8de8e 100644 (file)
@@ -7,11 +7,13 @@ njs_module_srcs=src/qjs_buffer.c
 
 . auto/qjs_module
 
-njs_module_name=qjs_crypto_module
-njs_module_incs=
-njs_module_srcs=external/qjs_crypto_module.c
+if [ $NJS_OPENSSL = YES -a $NJS_HAVE_OPENSSL = YES ]; then
+       njs_module_name=qjs_crypto_module
+       njs_module_incs=
+       njs_module_srcs=external/qjs_crypto_module.c
 
-. auto/qjs_module
+       . auto/qjs_module
+fi
 
 njs_module_name=qjs_fs_module
 njs_module_incs=
index cdbb4b30e1acd2539fccbf103f6ac3d33f62aa8d..aff99b5a27e62ab4f9ac5c98556c2bd6869d167d 100644 (file)
@@ -8,49 +8,30 @@
 #include <njs.h>
 #include <njs_string.h>
 #include <njs_buffer.h>
-#include "njs_hash.h"
+#include "njs_openssl.h"
 
 
-typedef void (*njs_hash_init)(njs_hash_t *ctx);
-typedef void (*njs_hash_update)(njs_hash_t *ctx, const void *data, size_t size);
-typedef void (*njs_hash_final)(u_char result[32], njs_hash_t *ctx);
-
 typedef njs_int_t (*njs_digest_encode)(njs_vm_t *vm, njs_value_t *value,
     const njs_str_t *src);
 
 
 typedef struct {
-    njs_str_t           name;
-
-    size_t              size;
-    njs_hash_init       init;
-    njs_hash_update     update;
-    njs_hash_final      final;
-} njs_hash_alg_t;
-
-typedef struct {
-    njs_hash_t          ctx;
-    njs_hash_alg_t      *alg;
+    EVP_MD_CTX          *ctx;
 } njs_digest_t;
 
 typedef struct {
-    u_char              opad[64];
-    njs_hash_t          ctx;
-    njs_hash_alg_t      *alg;
+    HMAC_CTX            *ctx;
 } njs_hmac_t;
 
 
 typedef struct {
     njs_str_t             name;
-
     njs_digest_encode     encode;
 } njs_crypto_enc_t;
 
 
-static njs_hash_alg_t *njs_crypto_algorithm(njs_vm_t *vm,
-    njs_value_t *value);
-static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm,
-    njs_value_t *value);
+static const EVP_MD *njs_crypto_algorithm(njs_vm_t *vm, njs_value_t *value);
+static njs_crypto_enc_t *njs_crypto_encoding(njs_vm_t *vm, njs_value_t *value);
 static njs_int_t njs_buffer_digest(njs_vm_t *vm, njs_value_t *value,
     const njs_str_t *src);
 static njs_int_t njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args,
@@ -64,44 +45,10 @@ static njs_int_t njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args,
 static njs_int_t njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args,
     njs_uint_t nargs, njs_index_t unused, njs_value_t *retval);
 
-static njs_int_t njs_crypto_init(njs_vm_t *vm);
-
-
-static njs_hash_alg_t njs_hash_algorithms[] = {
-
-   {
-     njs_str("md5"),
-     16,
-     njs_md5_init,
-     njs_md5_update,
-     njs_md5_final
-   },
-
-   {
-     njs_str("sha1"),
-     20,
-     njs_sha1_init,
-     njs_sha1_update,
-     njs_sha1_final
-   },
-
-   {
-     njs_str("sha256"),
-     32,
-     njs_sha2_init,
-     njs_sha2_update,
-     njs_sha2_final
-   },
-
-   {
-    njs_null_str,
-    0,
-    NULL,
-    NULL,
-    NULL
-   }
+static void njs_crypto_cleanup_digest(void *data);
+static void njs_crypto_cleanup_hmac(void *data);
 
-};
+static njs_int_t njs_crypto_init(njs_vm_t *vm);
 
 
 static njs_crypto_enc_t njs_encodings[] = {
@@ -283,11 +230,12 @@ static njs_int_t
 njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_digest_t    *dgst;
-    njs_hash_alg_t  *alg;
+    njs_digest_t      *dgst;
+    const EVP_MD      *md;
+    njs_mp_cleanup_t  *cln;
 
-    alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
-    if (njs_slow_path(alg == NULL)) {
+    md = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
+    if (njs_slow_path(md == NULL)) {
         return NJS_ERROR;
     }
 
@@ -297,9 +245,26 @@ njs_crypto_create_hash(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    dgst->alg = alg;
+    dgst->ctx = njs_evp_md_ctx_new();
+    if (njs_slow_path(dgst->ctx == NULL)) {
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
 
-    alg->init(&dgst->ctx);
+    cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
+    if (njs_slow_path(cln == NULL)) {
+        njs_evp_md_ctx_free(dgst->ctx);
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    cln->handler = njs_crypto_cleanup_digest;
+    cln->data = dgst;
+
+    if (EVP_DigestInit_ex(dgst->ctx, md, NULL) <= 0) {
+        njs_vm_internal_error(vm, "EVP_DigestInit_ex() failed");
+        return NJS_ERROR;
+    }
 
     return njs_vm_external_create(vm, retval, njs_crypto_hash_proto_id,
                                   dgst, 0);
@@ -327,7 +292,7 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             return NJS_ERROR;
         }
 
-        if (njs_slow_path(dgst->alg == NULL)) {
+        if (njs_slow_path(dgst->ctx == NULL)) {
             njs_vm_error(vm, "Digest already called");
             return NJS_ERROR;
         }
@@ -341,7 +306,7 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             return NJS_ERROR;
         }
 
-        if (njs_slow_path(ctx->alg == NULL)) {
+        if (njs_slow_path(ctx->ctx == NULL)) {
             njs_vm_error(vm, "Digest already called");
             return NJS_ERROR;
         }
@@ -377,10 +342,16 @@ njs_hash_prototype_update(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     if (!hmac) {
-        dgst->alg->update(&dgst->ctx, data.start, data.length);
+        if (EVP_DigestUpdate(dgst->ctx, data.start, data.length) <= 0) {
+            njs_vm_internal_error(vm, "EVP_DigestUpdate() failed");
+            return NJS_ERROR;
+        }
 
     } else {
-        ctx->alg->update(&ctx->ctx, data.start, data.length);
+        if (HMAC_Update(ctx->ctx, data.start, data.length) <= 0) {
+            njs_vm_internal_error(vm, "HMAC_Update() failed");
+            return NJS_ERROR;
+        }
     }
 
     njs_value_assign(retval, this);
@@ -397,9 +368,9 @@ njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_hmac_t        *ctx;
     njs_value_t       *this;
     njs_digest_t      *dgst;
-    njs_hash_alg_t    *alg;
+    unsigned int      len;
     njs_crypto_enc_t  *enc;
-    u_char            hash1[32], digest[32];
+    u_char            digest[EVP_MAX_MD_SIZE];
 
     this = njs_argument(args, 0);
 
@@ -410,7 +381,7 @@ njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             return NJS_ERROR;
         }
 
-        if (njs_slow_path(dgst->alg == NULL)) {
+        if (njs_slow_path(dgst->ctx == NULL)) {
             goto exception;
         }
 
@@ -423,7 +394,7 @@ njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
             return NJS_ERROR;
         }
 
-        if (njs_slow_path(ctx->alg == NULL)) {
+        if (njs_slow_path(ctx->ctx == NULL)) {
             goto exception;
         }
 
@@ -436,23 +407,26 @@ njs_hash_prototype_digest(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     }
 
     if (!hmac) {
-        alg = dgst->alg;
-        alg->final(digest, &dgst->ctx);
-        dgst->alg = NULL;
+        if (EVP_DigestFinal_ex(dgst->ctx, digest, &len) <= 0) {
+            njs_vm_internal_error(vm, "EVP_DigestFinal_ex() failed");
+            return NJS_ERROR;
+        }
+
+        njs_evp_md_ctx_free(dgst->ctx);
+        dgst->ctx = NULL;
 
     } else {
-        alg = ctx->alg;
-        alg->final(hash1, &ctx->ctx);
+        if (HMAC_Final(ctx->ctx, digest, &len) <= 0) {
+            njs_vm_internal_error(vm, "HMAC_Final() failed");
+            return NJS_ERROR;
+        }
 
-        alg->init(&ctx->ctx);
-        alg->update(&ctx->ctx, ctx->opad, 64);
-        alg->update(&ctx->ctx, hash1, alg->size);
-        alg->final(digest, &ctx->ctx);
-        ctx->alg = NULL;
+        njs_hmac_ctx_free(ctx->ctx);
+        ctx->ctx = NULL;
     }
 
     str.start = digest;
-    str.length = alg->size;
+    str.length = len;
 
     return enc->encode(vm, retval, &str);
 
@@ -468,7 +442,8 @@ static njs_int_t
 njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
     njs_index_t unused, njs_value_t *retval)
 {
-    njs_digest_t  *dgst, *copy;
+    njs_digest_t      *dgst, *copy;
+    njs_mp_cleanup_t  *cln;
 
     dgst = njs_vm_external(vm, njs_crypto_hash_proto_id, njs_argument(args, 0));
     if (njs_slow_path(dgst == NULL)) {
@@ -476,7 +451,7 @@ njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    if (njs_slow_path(dgst->alg == NULL)) {
+    if (njs_slow_path(dgst->ctx == NULL)) {
         njs_vm_error(vm, "Digest already called");
         return NJS_ERROR;
     }
@@ -487,7 +462,26 @@ njs_hash_prototype_copy(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    memcpy(copy, dgst, sizeof(njs_digest_t));
+    copy->ctx = njs_evp_md_ctx_new();
+    if (njs_slow_path(copy->ctx == NULL)) {
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
+    if (njs_slow_path(cln == NULL)) {
+        njs_evp_md_ctx_free(copy->ctx);
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
+    }
+
+    cln->handler = njs_crypto_cleanup_digest;
+    cln->data = copy;
+
+    if (EVP_MD_CTX_copy_ex(copy->ctx, dgst->ctx) <= 0) {
+        njs_vm_internal_error(vm, "EVP_MD_CTX_copy_ex() failed");
+        return NJS_ERROR;
+    }
 
     return njs_vm_external_create(vm, retval,
                                   njs_crypto_hash_proto_id, copy, 0);
@@ -500,16 +494,15 @@ njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 {
     njs_int_t                    ret;
     njs_str_t                    key;
-    njs_uint_t                   i;
     njs_hmac_t                   *ctx;
     njs_value_t                  *value;
-    njs_hash_alg_t               *alg;
+    const EVP_MD                 *md;
+    njs_mp_cleanup_t             *cln;
     njs_opaque_value_t           result;
     const njs_buffer_encoding_t  *enc;
-    u_char                       digest[32], key_buf[64];
 
-    alg = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
-    if (njs_slow_path(alg == NULL)) {
+    md = njs_crypto_algorithm(vm, njs_arg(args, nargs, 1));
+    if (njs_slow_path(md == NULL)) {
         return NJS_ERROR;
     }
 
@@ -546,43 +539,37 @@ njs_crypto_create_hmac(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
         return NJS_ERROR;
     }
 
-    ctx->alg = alg;
-
-    if (key.length > sizeof(key_buf)) {
-        alg->init(&ctx->ctx);
-        alg->update(&ctx->ctx, key.start, key.length);
-        alg->final(digest, &ctx->ctx);
-
-        memcpy(key_buf, digest, alg->size);
-        njs_explicit_memzero(key_buf + alg->size, sizeof(key_buf) - alg->size);
-
-    } else {
-        memcpy(key_buf, key.start, key.length);
-        njs_explicit_memzero(key_buf + key.length,
-                             sizeof(key_buf) - key.length);
+    ctx->ctx = njs_hmac_ctx_new();
+    if (njs_slow_path(ctx->ctx == NULL)) {
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
     }
 
-    for (i = 0; i < 64; i++) {
-        ctx->opad[i] = key_buf[i] ^ 0x5c;
+    cln = njs_mp_cleanup_add(njs_vm_memory_pool(vm), 0);
+    if (njs_slow_path(cln == NULL)) {
+        njs_hmac_ctx_free(ctx->ctx);
+        njs_vm_memory_error(vm);
+        return NJS_ERROR;
     }
 
-    for (i = 0; i < 64; i++) {
-         key_buf[i] ^= 0x36;
-    }
+    cln->handler = njs_crypto_cleanup_hmac;
+    cln->data = ctx;
 
-    alg->init(&ctx->ctx);
-    alg->update(&ctx->ctx, key_buf, 64);
+    if (HMAC_Init_ex(ctx->ctx, key.start, (int) key.length, md, NULL) <= 0) {
+        njs_vm_internal_error(vm, "HMAC_Init_ex() failed");
+        return NJS_ERROR;
+    }
 
     return njs_vm_external_create(vm, retval, njs_crypto_hmac_proto_id,
                                   ctx, 0);
 }
 
 
-static njs_hash_alg_t *
+static const EVP_MD *
 njs_crypto_algorithm(njs_vm_t *vm, njs_value_t *value)
 {
-    njs_str_t       name;
-    njs_hash_alg_t  *e;
+    njs_str_t     name;
+    const EVP_MD  *md;
 
     if (njs_slow_path(!njs_value_is_string(value))) {
         njs_vm_type_error(vm, "algorithm must be a string");
@@ -591,15 +578,18 @@ njs_crypto_algorithm(njs_vm_t *vm, njs_value_t *value)
 
     njs_value_string_get(vm, value, &name);
 
-    for (e = &njs_hash_algorithms[0]; e->name.length != 0; e++) {
-        if (njs_strstr_eq(&name, &e->name)) {
-            return e;
-        }
+    if (njs_slow_path(njs_strlen(name.start) != name.length)) {
+        njs_vm_type_error(vm, "not supported algorithm: \"%V\"", &name);
+        return NULL;
     }
 
-    njs_vm_type_error(vm, "not supported algorithm: \"%V\"", &name);
+    md = EVP_get_digestbyname((const char *) name.start);
+    if (njs_slow_path(md == NULL)) {
+        njs_vm_type_error(vm, "not supported algorithm: \"%V\"", &name);
+        return NULL;
+    }
 
-    return NULL;
+    return md;
 }
 
 
@@ -639,6 +629,30 @@ njs_buffer_digest(njs_vm_t *vm, njs_value_t *value, const njs_str_t *src)
 }
 
 
+static void
+njs_crypto_cleanup_digest(void *data)
+{
+    njs_digest_t  *dgst = data;
+
+    if (dgst->ctx != NULL) {
+        njs_evp_md_ctx_free(dgst->ctx);
+        dgst->ctx = NULL;
+    }
+}
+
+
+static void
+njs_crypto_cleanup_hmac(void *data)
+{
+    njs_hmac_t  *ctx = data;
+
+    if (ctx->ctx != NULL) {
+        njs_hmac_ctx_free(ctx->ctx);
+        ctx->ctx = NULL;
+    }
+}
+
+
 static njs_int_t
 njs_crypto_init(njs_vm_t *vm)
 {
diff --git a/external/njs_hash.h b/external/njs_hash.h
deleted file mode 100644 (file)
index acc5e45..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- */
-
-
-#ifndef _NJS_HASH_H_INCLUDED_
-#define _NJS_HASH_H_INCLUDED_
-
-
-typedef struct {
-    uint64_t  bytes;
-    uint32_t  a, b, c, d, e, f, g, h;
-    u_char    buffer[64];
-} njs_hash_t;
-
-
-NJS_EXPORT void njs_md5_init(njs_hash_t *ctx);
-NJS_EXPORT void njs_md5_update(njs_hash_t *ctx, const void *data, size_t size);
-NJS_EXPORT void njs_md5_final(u_char result[32], njs_hash_t *ctx);
-
-NJS_EXPORT void njs_sha1_init(njs_hash_t *ctx);
-NJS_EXPORT void njs_sha1_update(njs_hash_t *ctx, const void *data, size_t size);
-NJS_EXPORT void njs_sha1_final(u_char result[32], njs_hash_t *ctx);
-
-NJS_EXPORT void njs_sha2_init(njs_hash_t *ctx);
-NJS_EXPORT void njs_sha2_update(njs_hash_t *ctx, const void *data, size_t size);
-NJS_EXPORT void njs_sha2_final(u_char result[32], njs_hash_t *ctx);
-
-
-#endif /* _NJS_HASH_H_INCLUDED_ */
diff --git a/external/njs_md5.c b/external/njs_md5.c
deleted file mode 100644 (file)
index dfd9d53..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-
-/*
- * An internal implementation, based on Alexander Peslyak's
- * public domain implementation:
- * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
- */
-
-
-#include <njs_unix.h>
-#include <njs_types.h>
-#include <njs_clang.h>
-#include <njs_str.h>
-#include "njs_hash.h"
-
-
-static const u_char *njs_md5_body(njs_hash_t *ctx, const u_char *data,
-    size_t size);
-
-
-void
-njs_md5_init(njs_hash_t *ctx)
-{
-    ctx->a = 0x67452301;
-    ctx->b = 0xefcdab89;
-    ctx->c = 0x98badcfe;
-    ctx->d = 0x10325476;
-
-    ctx->bytes = 0;
-}
-
-
-void
-njs_md5_update(njs_hash_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) njs_md5_body(ctx, ctx->buffer, 64);
-    }
-
-    if (size >= 64) {
-        data = njs_md5_body(ctx, data, size & ~(size_t) 0x3f);
-        size &= 0x3f;
-    }
-
-    memcpy(ctx->buffer, data, size);
-}
-
-
-void
-njs_md5_final(u_char result[32], njs_hash_t *ctx)
-{
-    size_t  used, free;
-
-    used = (size_t) (ctx->bytes & 0x3f);
-
-    ctx->buffer[used++] = 0x80;
-
-    free = 64 - used;
-
-    if (free < 8) {
-        njs_memzero(&ctx->buffer[used], free);
-        (void) njs_md5_body(ctx, ctx->buffer, 64);
-        used = 0;
-        free = 64;
-    }
-
-    njs_memzero(&ctx->buffer[used], 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) njs_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);
-
-    njs_explicit_memzero(ctx, 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 *
-njs_md5_body(njs_hash_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;
-}
index 98cd72f81af3b44f9008a5b68dd07e97e1dace30..c68f330b34375617b8bb8bb432cbc1cdab9184f2 100644 (file)
 #endif
 
 
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#define njs_hmac_ctx_new()       HMAC_CTX_new()
+#define njs_hmac_ctx_free(_ctx)  HMAC_CTX_free(_ctx)
+#else
+
+njs_inline HMAC_CTX *
+njs_hmac_ctx_new(void)
+{
+    HMAC_CTX  *ctx;
+
+    ctx = OPENSSL_malloc(sizeof(HMAC_CTX));
+    if (ctx != NULL) {
+        HMAC_CTX_init(ctx);
+    }
+
+    return ctx;
+}
+
+
+njs_inline void
+njs_hmac_ctx_free(HMAC_CTX *ctx)
+{
+    if (ctx != NULL) {
+        HMAC_CTX_cleanup(ctx);
+        OPENSSL_free(ctx);
+    }
+}
+
+#endif
+
+
 #define njs_bio_new_mem_buf(b, len) BIO_new_mem_buf((void *) b, len)
 
 
diff --git a/external/njs_sha1.c b/external/njs_sha1.c
deleted file mode 100644 (file)
index e5ca6ee..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-
-/*
- * Copyright (C) Maxim Dounin
- * Copyright (C) NGINX, Inc.
- *
- * An internal SHA1 implementation.
- */
-
-
-#include <njs_unix.h>
-#include <njs_types.h>
-#include <njs_clang.h>
-#include <njs_str.h>
-#include "njs_hash.h"
-
-
-static const u_char *njs_sha1_body(njs_hash_t *ctx, const u_char *data,
-    size_t size);
-
-
-void
-njs_sha1_init(njs_hash_t *ctx)
-{
-    ctx->a = 0x67452301;
-    ctx->b = 0xefcdab89;
-    ctx->c = 0x98badcfe;
-    ctx->d = 0x10325476;
-    ctx->e = 0xc3d2e1f0;
-
-    ctx->bytes = 0;
-}
-
-
-void
-njs_sha1_update(njs_hash_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) njs_sha1_body(ctx, ctx->buffer, 64);
-    }
-
-    if (size >= 64) {
-        data = njs_sha1_body(ctx, data, size & ~(size_t) 0x3f);
-        size &= 0x3f;
-    }
-
-    memcpy(ctx->buffer, data, size);
-}
-
-
-void
-njs_sha1_final(u_char result[32], njs_hash_t *ctx)
-{
-    size_t  used, free;
-
-    used = (size_t) (ctx->bytes & 0x3f);
-
-    ctx->buffer[used++] = 0x80;
-
-    free = 64 - used;
-
-    if (free < 8) {
-        njs_memzero(&ctx->buffer[used], free);
-        (void) njs_sha1_body(ctx, ctx->buffer, 64);
-        used = 0;
-        free = 64;
-    }
-
-    njs_memzero(&ctx->buffer[used], 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) njs_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;
-
-    njs_explicit_memzero(ctx, 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 *
-njs_sha1_body(njs_hash_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];
-    njs_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/external/njs_sha2.c b/external/njs_sha2.c
deleted file mode 100644 (file)
index 2e14634..0000000
+++ /dev/null
@@ -1,320 +0,0 @@
-
-/*
- * Copyright (C) Dmitry Volyntsev
- * Copyright (C) NGINX, Inc.
- *
- * An internal SHA2 implementation.
- */
-
-
-#include <njs_unix.h>
-#include <njs_types.h>
-#include <njs_clang.h>
-#include <njs_str.h>
-#include "njs_hash.h"
-
-
-static const u_char *njs_sha2_body(njs_hash_t *ctx, const u_char *data,
-    size_t size);
-
-
-void
-njs_sha2_init(njs_hash_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
-njs_sha2_update(njs_hash_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) njs_sha2_body(ctx, ctx->buffer, 64);
-    }
-
-    if (size >= 64) {
-        data = njs_sha2_body(ctx, data, size & ~(size_t) 0x3f);
-        size &= 0x3f;
-    }
-
-    memcpy(ctx->buffer, data, size);
-}
-
-
-void
-njs_sha2_final(u_char result[32], njs_hash_t *ctx)
-{
-    size_t  used, free;
-
-    used = (size_t) (ctx->bytes & 0x3f);
-
-    ctx->buffer[used++] = 0x80;
-
-    free = 64 - used;
-
-    if (free < 8) {
-        njs_memzero(&ctx->buffer[used], free);
-        (void) njs_sha2_body(ctx, ctx->buffer, 64);
-        used = 0;
-        free = 64;
-    }
-
-    njs_memzero(&ctx->buffer[used], 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) njs_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;
-
-    njs_explicit_memzero(ctx, 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 *
-njs_sha2_body(njs_hash_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];
-    njs_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;
-}
index 2b1bb26b22f67acac6eb76ac4b89e9874c3a9cb5..9d8510e74a99b9c55db97a50c329c81f36ea5908 100644 (file)
@@ -5,33 +5,17 @@
  */
 
 #include <qjs.h>
-#include "njs_hash.h"
-
-typedef void (*qjs_hash_init)(njs_hash_t *ctx);
-typedef void (*qjs_hash_update)(njs_hash_t *ctx, const void *data, size_t size);
-typedef void (*qjs_hash_final)(u_char result[32], njs_hash_t *ctx);
+#include "njs_openssl.h"
 
 typedef JSValue (*qjs_digest_encode)(JSContext *cx, const njs_str_t *src);
 
 
 typedef struct {
-    njs_str_t      name;
-
-    size_t         size;
-    qjs_hash_init  init;
-    qjs_hash_update update;
-    qjs_hash_final final;
-} qjs_hash_alg_t;
-
-typedef struct {
-    njs_hash_t     ctx;
-    qjs_hash_alg_t *alg;
+    EVP_MD_CTX     *ctx;
 } qjs_digest_t;
 
 typedef struct {
-    u_char         opad[64];
-    njs_hash_t     ctx;
-    qjs_hash_alg_t *alg;
+    HMAC_CTX       *ctx;
 } qjs_hmac_t;
 
 
@@ -42,7 +26,7 @@ typedef struct {
 } qjs_crypto_enc_t;
 
 
-static qjs_hash_alg_t *qjs_crypto_algorithm(JSContext *cx, JSValueConst val);
+static const EVP_MD *qjs_crypto_algorithm(JSContext *cx, JSValueConst val);
 static qjs_crypto_enc_t *qjs_crypto_encoding(JSContext *cx, JSValueConst val);
 static JSValue qjs_buffer_digest(JSContext *cx, const njs_str_t *src);
 static JSValue qjs_crypto_create_hash(JSContext *cx, JSValueConst this_val,
@@ -61,43 +45,6 @@ static int qjs_crypto_module_init(JSContext *cx, JSModuleDef *m);
 static JSModuleDef * qjs_crypto_init(JSContext *cx, const char *module_name);
 
 
-static qjs_hash_alg_t qjs_hash_algorithms[] = {
-
-    {
-        njs_str("md5"),
-        16,
-        njs_md5_init,
-        njs_md5_update,
-        njs_md5_final
-    },
-
-    {
-        njs_str("sha1"),
-        20,
-        njs_sha1_init,
-        njs_sha1_update,
-        njs_sha1_final
-    },
-
-    {
-        njs_str("sha256"),
-        32,
-        njs_sha2_init,
-        njs_sha2_update,
-        njs_sha2_final
-    },
-
-    {
-        njs_null_str,
-        0,
-        NULL,
-        NULL,
-        NULL
-    }
-
-};
-
-
 static qjs_crypto_enc_t qjs_encodings[] = {
 
     {
@@ -173,12 +120,12 @@ static JSValue
 qjs_crypto_create_hash(JSContext *cx, JSValueConst this_val, int argc,
     JSValueConst *argv)
 {
-    JSValue         obj;
-    qjs_digest_t    *dgst;
-    qjs_hash_alg_t  *alg;
+    JSValue       obj;
+    qjs_digest_t  *dgst;
+    const EVP_MD  *md;
 
-    alg = qjs_crypto_algorithm(cx, argv[0]);
-    if (alg == NULL) {
+    md = qjs_crypto_algorithm(cx, argv[0]);
+    if (md == NULL) {
         return JS_EXCEPTION;
     }
 
@@ -187,11 +134,22 @@ qjs_crypto_create_hash(JSContext *cx, JSValueConst this_val, int argc,
         return JS_ThrowOutOfMemory(cx);
     }
 
-    dgst->alg = alg;
-    alg->init(&dgst->ctx);
+    dgst->ctx = njs_evp_md_ctx_new();
+    if (dgst->ctx == NULL) {
+        js_free(cx, dgst);
+        return JS_ThrowOutOfMemory(cx);
+    }
+
+    if (EVP_DigestInit_ex(dgst->ctx, md, NULL) <= 0) {
+        njs_evp_md_ctx_free(dgst->ctx);
+        js_free(cx, dgst);
+        JS_ThrowInternalError(cx, "EVP_DigestInit_ex() failed");
+        return JS_EXCEPTION;
+    }
 
     obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HASH);
     if (JS_IsException(obj)) {
+        njs_evp_md_ctx_free(dgst->ctx);
         js_free(cx, dgst);
         return obj;
     }
@@ -207,26 +165,22 @@ qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, int argc,
     JSValueConst *argv, int hmac)
 {
     njs_str_t                    str, content;
-    njs_hash_t                   *uctx;
     qjs_hmac_t                   *hctx;
     qjs_bytes_t                  bytes;
     qjs_digest_t                 *dgst;
     const qjs_buffer_encoding_t  *enc;
 
-    void (*update)(njs_hash_t *ctx, const void *data, size_t size);
-
     if (!hmac) {
         dgst = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HASH);
         if (dgst == NULL) {
             return JS_ThrowTypeError(cx, "\"this\" is not a hash object");
         }
 
-        if (dgst->alg == NULL) {
+        if (dgst->ctx == NULL) {
             return JS_ThrowTypeError(cx, "Digest already called");
         }
 
-        update = dgst->alg->update;
-        uctx = &dgst->ctx;
+        hctx = NULL;
 
     } else {
         hctx = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HMAC);
@@ -234,12 +188,11 @@ qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, int argc,
             return JS_ThrowTypeError(cx, "\"this\" is not a hmac object");
         }
 
-        if (hctx->alg == NULL) {
+        if (hctx->ctx == NULL) {
             return JS_ThrowTypeError(cx, "Digest already called");
         }
 
-        update = hctx->alg->update;
-        uctx = &hctx->ctx;
+        dgst = NULL;
     }
 
     if (JS_IsString(argv[0])) {
@@ -263,18 +216,49 @@ qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, int argc,
 
             if (enc->decode(cx, &str, &content) != 0) {
                 JS_FreeCString(cx, (const char *) str.start);
-                JS_FreeCString(cx, (const char *) content.start);
+                js_free(cx, content.start);
                 return JS_EXCEPTION;
             }
 
             JS_FreeCString(cx, (const char *) str.start);
 
-            update(uctx, content.start, content.length);
+            if (!hmac) {
+                if (EVP_DigestUpdate(dgst->ctx, content.start, content.length)
+                    <= 0)
+                {
+                    js_free(cx, content.start);
+                    JS_ThrowInternalError(cx, "EVP_DigestUpdate() failed");
+                    return JS_EXCEPTION;
+                }
+
+            } else {
+                if (HMAC_Update(hctx->ctx, content.start, content.length) <= 0)
+                {
+                    js_free(cx, content.start);
+                    JS_ThrowInternalError(cx, "HMAC_Update() failed");
+                    return JS_EXCEPTION;
+                }
+            }
+
             js_free(cx, content.start);
 
         } else {
-           update(uctx, str.start, str.length);
-           JS_FreeCString(cx, (const char *) str.start);
+            if (!hmac) {
+                if (EVP_DigestUpdate(dgst->ctx, str.start, str.length) <= 0) {
+                    JS_FreeCString(cx, (const char *) str.start);
+                    JS_ThrowInternalError(cx, "EVP_DigestUpdate() failed");
+                    return JS_EXCEPTION;
+                }
+
+            } else {
+                if (HMAC_Update(hctx->ctx, str.start, str.length) <= 0) {
+                    JS_FreeCString(cx, (const char *) str.start);
+                    JS_ThrowInternalError(cx, "HMAC_Update() failed");
+                    return JS_EXCEPTION;
+                }
+            }
+
+            JS_FreeCString(cx, (const char *) str.start);
         }
 
     } else if (qjs_is_typed_array(cx, argv[0])) {
@@ -282,7 +266,18 @@ qjs_hash_prototype_update(JSContext *cx, JSValueConst this_val, int argc,
             return JS_EXCEPTION;
         }
 
-        update(uctx, bytes.start, bytes.length);
+        if (!hmac) {
+            if (EVP_DigestUpdate(dgst->ctx, bytes.start, bytes.length) <= 0) {
+                JS_ThrowInternalError(cx, "EVP_DigestUpdate() failed");
+                return JS_EXCEPTION;
+            }
+
+        } else {
+            if (HMAC_Update(hctx->ctx, bytes.start, bytes.length) <= 0) {
+                JS_ThrowInternalError(cx, "HMAC_Update() failed");
+                return JS_EXCEPTION;
+            }
+        }
 
     } else {
         return JS_ThrowTypeError(cx,
@@ -298,11 +293,11 @@ qjs_hash_prototype_digest(JSContext *cx, JSValueConst this_val, int argc,
     JSValueConst *argv, int hmac)
 {
     njs_str_t         str;
+    unsigned int      len;
     qjs_hmac_t        *hctx;
     qjs_digest_t      *dgst;
-    qjs_hash_alg_t    *alg;
     qjs_crypto_enc_t  *enc;
-    u_char            hash1[32],digest[32];
+    u_char            digest[EVP_MAX_MD_SIZE];
 
     if (!hmac) {
         dgst = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HASH);
@@ -310,14 +305,17 @@ qjs_hash_prototype_digest(JSContext *cx, JSValueConst this_val, int argc,
             return JS_ThrowTypeError(cx, "\"this\" is not a hash object");
         }
 
-        alg = dgst->alg;
-        if (alg == NULL) {
+        if (dgst->ctx == NULL) {
             return JS_ThrowTypeError(cx, "Digest already called");
         }
 
-        dgst->alg = NULL;
+        if (EVP_DigestFinal_ex(dgst->ctx, digest, &len) <= 0) {
+            JS_ThrowInternalError(cx, "EVP_DigestFinal_ex() failed");
+            return JS_EXCEPTION;
+        }
 
-        alg->final(digest, &dgst->ctx);
+        njs_evp_md_ctx_free(dgst->ctx);
+        dgst->ctx = NULL;
 
     } else {
         hctx = JS_GetOpaque2(cx, this_val, QJS_CORE_CLASS_CRYPTO_HMAC);
@@ -325,23 +323,21 @@ qjs_hash_prototype_digest(JSContext *cx, JSValueConst this_val, int argc,
             return JS_ThrowTypeError(cx, "\"this\" is not a hmac object");
         }
 
-        alg = hctx->alg;
-        if (alg == NULL) {
+        if (hctx->ctx == NULL) {
             return JS_ThrowTypeError(cx, "Digest already called");
         }
 
-        hctx->alg = NULL;
-
-        alg->final(hash1, &hctx->ctx);
+        if (HMAC_Final(hctx->ctx, digest, &len) <= 0) {
+            JS_ThrowInternalError(cx, "HMAC_Final() failed");
+            return JS_EXCEPTION;
+        }
 
-        alg->init(&hctx->ctx);
-        alg->update(&hctx->ctx, hctx->opad, 64);
-        alg->update(&hctx->ctx, hash1, alg->size);
-        alg->final(digest, &hctx->ctx);
+        njs_hmac_ctx_free(hctx->ctx);
+        hctx->ctx = NULL;
     }
 
     str.start = digest;
-    str.length = alg->size;
+    str.length = len;
 
     if (argc == 0) {
         return qjs_buffer_digest(cx, &str);
@@ -368,7 +364,7 @@ qjs_hash_prototype_copy(JSContext *cx, JSValueConst this_val, int argc,
         return JS_EXCEPTION;
     }
 
-    if (dgst->alg == NULL) {
+    if (dgst->ctx == NULL) {
         return JS_ThrowTypeError(cx, "Digest already called");
     }
 
@@ -377,10 +373,22 @@ qjs_hash_prototype_copy(JSContext *cx, JSValueConst this_val, int argc,
         return JS_ThrowOutOfMemory(cx);
     }
 
-    memcpy(copy, dgst, sizeof(qjs_digest_t));
+    copy->ctx = njs_evp_md_ctx_new();
+    if (copy->ctx == NULL) {
+        js_free(cx, copy);
+        return JS_ThrowOutOfMemory(cx);
+    }
+
+    if (EVP_MD_CTX_copy_ex(copy->ctx, dgst->ctx) <= 0) {
+        njs_evp_md_ctx_free(copy->ctx);
+        js_free(cx, copy);
+        JS_ThrowInternalError(cx, "EVP_MD_CTX_copy_ex() failed");
+        return JS_EXCEPTION;
+    }
 
     obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HASH);
     if (JS_IsException(obj)) {
+        njs_evp_md_ctx_free(copy->ctx);
         js_free(cx, copy);
         return obj;
     }
@@ -395,17 +403,15 @@ static JSValue
 qjs_crypto_create_hmac(JSContext *cx, JSValueConst this_val, int argc,
     JSValueConst *argv)
 {
-    int             i;
-    JS_BOOL         key_is_string;
-    JSValue         obj;
-    njs_str_t       key;
-    qjs_hmac_t      *hmac;
-    qjs_bytes_t     bytes;
-    qjs_hash_alg_t  *alg;
-    u_char          digest[32], key_buf[64];
-
-    alg = qjs_crypto_algorithm(cx, argv[0]);
-    if (alg == NULL) {
+    JS_BOOL       key_is_string;
+    JSValue       obj;
+    njs_str_t     key;
+    qjs_hmac_t    *hmac;
+    qjs_bytes_t   bytes;
+    const EVP_MD  *md;
+
+    md = qjs_crypto_algorithm(cx, argv[0]);
+    if (md == NULL) {
         return JS_EXCEPTION;
     }
 
@@ -438,38 +444,34 @@ qjs_crypto_create_hmac(JSContext *cx, JSValueConst this_val, int argc,
         return JS_ThrowOutOfMemory(cx);
     }
 
-    hmac->alg = alg;
+    hmac->ctx = njs_hmac_ctx_new();
+    if (hmac->ctx == NULL) {
+        js_free(cx, hmac);
+        if (key_is_string) {
+            JS_FreeCString(cx, (const char *) key.start);
+        }
 
-    if (key.length > sizeof(key_buf)) {
-        alg->init(&hmac->ctx);
-        alg->update(&hmac->ctx, key.start, key.length);
-        alg->final(digest, &hmac->ctx);
+        return JS_ThrowOutOfMemory(cx);
+    }
 
-        memcpy(key_buf, digest, alg->size);
-        memset(key_buf + alg->size, 0, sizeof(key_buf) - alg->size);
+    if (HMAC_Init_ex(hmac->ctx, key.start, (int) key.length, md, NULL) <= 0) {
+        njs_hmac_ctx_free(hmac->ctx);
+        js_free(cx, hmac);
+        if (key_is_string) {
+            JS_FreeCString(cx, (const char *) key.start);
+        }
 
-    } else {
-        memcpy(key_buf, key.start, key.length);
-        memset(key_buf + key.length, 0, sizeof(key_buf) - key.length);
+        JS_ThrowInternalError(cx, "HMAC_Init_ex() failed");
+        return JS_EXCEPTION;
     }
 
     if (key_is_string) {
         JS_FreeCString(cx, (const char *) key.start);
     }
 
-    for (i = 0; i < 64; i++) {
-        hmac->opad[i] = key_buf[i] ^ 0x5c;
-    }
-
-    for (i = 0; i < 64; i++) {
-        key_buf[i] ^= 0x36;
-    }
-
-    alg->init(&hmac->ctx);
-    alg->update(&hmac->ctx, key_buf, 64);
-
     obj = JS_NewObjectClass(cx, QJS_CORE_CLASS_CRYPTO_HMAC);
     if (JS_IsException(obj)) {
+        njs_hmac_ctx_free(hmac->ctx);
         js_free(cx, hmac);
         return obj;
     }
@@ -487,6 +489,10 @@ qjs_hash_finalizer(JSRuntime *rt, JSValue val)
 
     dgst = JS_GetOpaque(val, QJS_CORE_CLASS_CRYPTO_HASH);
     if (dgst != NULL) {
+        if (dgst->ctx != NULL) {
+            njs_evp_md_ctx_free(dgst->ctx);
+        }
+
         js_free_rt(rt, dgst);
     }
 }
@@ -499,39 +505,44 @@ qjs_hmac_finalizer(JSRuntime *rt, JSValue val)
 
     hmac = JS_GetOpaque(val, QJS_CORE_CLASS_CRYPTO_HMAC);
     if (hmac != NULL) {
+        if (hmac->ctx != NULL) {
+            njs_hmac_ctx_free(hmac->ctx);
+        }
+
         js_free_rt(rt, hmac);
     }
 }
 
 
-static qjs_hash_alg_t *
+static const EVP_MD *
 qjs_crypto_algorithm(JSContext *cx, JSValueConst val)
 {
-    njs_str_t       name;
-    qjs_hash_alg_t  *a, *alg;
+    size_t        len;
+    const char    *name;
+    const EVP_MD  *md;
 
-    name.start = (u_char *) JS_ToCStringLen(cx, &name.length, val);
-    if (name.start == NULL) {
+    name = JS_ToCStringLen(cx, &len, val);
+    if (name == NULL) {
         JS_ThrowTypeError(cx, "algorithm must be a string");
         return NULL;
     }
 
-    alg = NULL;
-
-    for (a = &qjs_hash_algorithms[0]; a->name.start != NULL; a++) {
-        if (njs_strstr_eq(&name, &a->name)) {
-            alg = a;
-            break;
-        }
+    if (njs_strlen(name) != len) {
+        JS_FreeCString(cx, name);
+        JS_ThrowTypeError(cx, "not supported algorithm");
+        return NULL;
     }
 
-    JS_FreeCString(cx, (const char *) name.start);
+    md = EVP_get_digestbyname(name);
 
-    if (alg == NULL) {
+    JS_FreeCString(cx, name);
+
+    if (md == NULL) {
         JS_ThrowTypeError(cx, "not supported algorithm");
+        return NULL;
     }
 
-    return alg;
+    return md;
 }
 
 
index f54234a83684276969ed0896da2b96cf9fe169be..0d91ae12415ba0129893405765c989c5265edaa3 100644 (file)
@@ -113,13 +113,15 @@ fi
 if [ $NJS_OPENSSL != NO ]; then
     NJS_OPENSSL_LIB=OPENSSL
     have=NJS_HAVE_OPENSSL . auto/have
+    NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/njs_crypto_module.c"
     NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/njs_webcrypto_module.c"
 
     if [ "$NJS_HAVE_QUICKJS" = "YES" ];  then
+        NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/qjs_crypto_module.c"
         NJS_SRCS="$NJS_SRCS $ngx_addon_dir/../external/qjs_webcrypto_module.c"
     fi
 
-    echo " enabled webcrypto module"
+    echo " enabled crypto and webcrypto modules"
 fi
 
 if [ $NJS_LIBXSLT != NO ]; then
index c9f77cdd3a331d050acaf0ad759341462cd0dfb2..30b7ba1b648d2de8a788e64fa9d2aba5a44389f4 100644 (file)
@@ -21740,20 +21740,23 @@ static njs_unit_test_t  njs_shared_test[] =
               "    at require (native)\n"
               "    at main (:1)\n") },
 
+    { njs_str("import cr from 'unknown'"),
+      njs_str("Error: Cannot load module \"unknown\"") },
+
+#if (NJS_HAVE_OPENSSL)
+
     { njs_str("var cr = require('crypto'); cr.createHash"),
       njs_str("[object Function]") },
 
     { njs_str("var cr = require('crypto'); cr.createHash('md5')"),
       njs_str("[object Hash]") },
 
-    { njs_str("import cr from 'unknown'"),
-      njs_str("Error: Cannot load module \"unknown\"") },
-
     { njs_str("import cr from 'crypto'; cr.createHash"),
       njs_str("[object Function]") },
 
     { njs_str("import cr from 'crypto'; cr.createHash('md5')"),
       njs_str("[object Hash]") },
+#endif
 
     { njs_str("var fs = require('fs'); fs.a = 1; fs.a"),
       njs_str("1") },
@@ -22291,6 +22294,7 @@ static njs_unit_test_t  njs_backtraces_test[] =
               "    at require (native)\n"
               "    at main (:1)\n") },
 
+#if (NJS_HAVE_OPENSSL)
     { njs_str("require('crypto').createHash('sha')"),
       njs_str("TypeError: not supported algorithm: \"sha\"\n"
               "    at createHash (native)\n"
@@ -22312,6 +22316,7 @@ static njs_unit_test_t  njs_backtraces_test[] =
       njs_str("TypeError: data is not a string or Buffer-like object\n"
               "    at update (native)\n"
               "    at main (:1)\n") },
+#endif
 
     { njs_str("function f(o) {function f_in(o) {return o.a.a};"
               "               return f_in(o)};"
index ad237d887cfd31808a2187afa18318d2130da54a..e0cc6f12f6e7bbac5f0f524e8d289191bbd0eefb 100644 (file)
@@ -5,6 +5,15 @@ flags: [async]
 
 import cr from 'crypto';
 
+function has_algorithm(alg) {
+    try {
+        cr.createHash(alg).digest();
+        return true;
+    } catch (e) {
+        return false;
+    }
+}
+
 let createHash_tsuite = {
     name: "createHash tests",
     skip: () => !cr.createHash,
@@ -166,6 +175,37 @@ let createHash_tsuite = {
         { hash: 'sha256', data: [Buffer.from('XABX').subarray(1,3)], digest: 'base64url',
           expected: "OBZPvRdgPXP2lri01yZk1zW7anyIV3aH_SrjP9aWQVM" },
 
+        { hash: 'sha384', data: [], digest: 'hex',
+          expected: "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b" },
+        { hash: 'sha384', data: ['AB'], digest: 'hex',
+          expected: "4f9179c7b48de00c642b3782c3f9435bf21bde99cbf9ca13b6c3f1e58fff9064ad47464da97e6277c7f438d8f5a91d6b" },
+        { hash: 'sha384', data: ['A', 'B'], digest: 'hex',
+          expected: "4f9179c7b48de00c642b3782c3f9435bf21bde99cbf9ca13b6c3f1e58fff9064ad47464da97e6277c7f438d8f5a91d6b" },
+
+        { hash: 'sha512', data: [], digest: 'hex',
+          expected: "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" },
+        { hash: 'sha512', data: ['AB'], digest: 'hex',
+          expected: "71edc062331872ff3c13c77d98f4af0e8e27bb360d03690558beab6711f9733e5dc7f114b7af58cfbcd6360575873c09a667a9af749dc912e4ca276a7dfee5d3" },
+        { hash: 'sha512', data: ['A', 'B'], digest: 'hex',
+          expected: "71edc062331872ff3c13c77d98f4af0e8e27bb360d03690558beab6711f9733e5dc7f114b7af58cfbcd6360575873c09a667a9af749dc912e4ca276a7dfee5d3" },
+
+        { hash: 'sha3-256', skip: () => !has_algorithm('sha3-256'),
+          data: [], digest: 'hex',
+          expected: "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a" },
+        { hash: 'sha3-256', skip: () => !has_algorithm('sha3-256'),
+          data: ['AB'], digest: 'hex',
+          expected: "21f29d555c7c5f9e5859a7984c0b2f8ccad9d5245fbe6c4703fd75c0922d7735" },
+        { hash: 'sha3-256', skip: () => !has_algorithm('sha3-256'),
+          data: ['A', 'B'], digest: 'hex',
+          expected: "21f29d555c7c5f9e5859a7984c0b2f8ccad9d5245fbe6c4703fd75c0922d7735" },
+
+        { hash: 'sha3-512', skip: () => !has_algorithm('sha3-512'),
+          data: [], digest: 'hex',
+          expected: "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26" },
+        { hash: 'sha3-512', skip: () => !has_algorithm('sha3-512'),
+          data: ['AB'], digest: 'hex',
+          expected: "fcc802621fee9efe4d8ee032d886f75431edb29d480e945d8f0efb1c0ad419bf9b652fca1fa1f5af0f5b4a74f76a6e86b00dbfbec7dcf00e3f4ef34840e9b720" },
+
         { hash: 'sha1',
           hash_value(hash) {
               var Hash = cr.createHash(hash).constructor;
@@ -390,6 +430,23 @@ let createHmac_tsuite = {
         { hash: 'sha256', key: 'A'.repeat(100), data: ['AB'], digest: 'hex',
           expected: "5647b6c429701ff512f0f18232b4507065d2376ca8899a816a0a6e721bf8ddcc" },
 
+        { hash: 'sha384', key: '', data: [], digest: 'hex',
+          expected: "6c1f2ee938fad2e24bd91298474382ca218c75db3d83e114b3d4367776d14d3551289e75e8209cd4b792302840234adc" },
+        { hash: 'sha384', key: '', data: ['AB'], digest: 'hex',
+          expected: "0145ec85556f28b82b49d7bd8c3373312d95c308b758e3bd3cd972f8ab9d0ea9245f60ef5b994ec936eb42c6fc7ca033" },
+        { hash: 'sha384', key: Buffer.from('secret'), data: ['AB'], digest: 'hex',
+          expected: "afb6653433bc3b3e570fa9c7f0fa1f40d070af21085d7292ce8c93c4a280c2c91b1da980c39a3738361458c75a8c6f64" },
+
+        { hash: 'sha512', key: '', data: [], digest: 'hex',
+          expected: "b936cee86c9f87aa5d3c6f2e84cb5a4239a5fe50480a6ec66b70ab5b1f4ac6730c6c515421b327ec1d69402e53dfb49ad7381eb067b338fd7b0cb22247225d47" },
+        { hash: 'sha512', key: '', data: ['AB'], digest: 'hex',
+          expected: "8bf9155a8dbd563d879ecb5ad27e7b9e8e30ab98c138802594bedd9d839ddecb85443fdc64e18274311975b4ec1c5dd5dc41a6e7530cf34ea3d6545cf5844501" },
+        { hash: 'sha512', key: Buffer.from('secret'), data: ['AB'], digest: 'hex',
+          expected: "1169372f39b4ea921bed3cff6d029c0be7116bf2966098b9923d0730e9bd569e2f52e65f63c3adebe988b0be174fd42578b1536f1f1b8b46a1382da55e27de9d" },
+
+        { hash: 'sha512', key: 'A'.repeat(200), data: ['AB'], digest: 'hex',
+          expected: "d64ea04999cba0f2f167f471569129f9b3105a2e099fb883be3cdee452b38cf7c34d1705b83caaaf784dd4e63703a328d9a5167b03dd059d516b386ff86d5a40" },
+
         { hash: 'sha1',
           hmac_value(hash, key) {
               var Hmac = cr.createHmac(hash, key).constructor;