details: https://hg.nginx.org/njs/rev/f70929728e86 branches: changeset: 1936:f70929728e86 user: Dmitry Volyntsev <xei...@nginx.com> date: Fri Aug 26 21:49:14 2022 -0700 description: WebCrypto: fixed sign and verify methods for ECDSA.
diffstat: external/njs_openssl.h | 2 + external/njs_webcrypto_module.c | 277 ++++++++++++++++++++++++++++ test/webcrypto/README.rst | 12 +- test/webcrypto/asn12ieeep1336.c | 49 ++++ test/webcrypto/text.base64.sha1.ecdsa.sig | 4 +- test/webcrypto/text.base64.sha256.ecdsa.sig | 4 +- 6 files changed, 342 insertions(+), 6 deletions(-) diffs (403 lines): diff -r 43b31a943c08 -r f70929728e86 external/njs_openssl.h --- a/external/njs_openssl.h Thu Aug 25 16:57:28 2022 -0700 +++ b/external/njs_openssl.h Fri Aug 26 21:49:14 2022 -0700 @@ -43,6 +43,8 @@ #else #define njs_evp_md_ctx_new() EVP_MD_CTX_create() #define njs_evp_md_ctx_free(_ctx) EVP_MD_CTX_destroy(_ctx) +#define ECDSA_SIG_get0_s(sig) (sig)->s +#define ECDSA_SIG_get0_r(sig) (sig)->r #endif diff -r 43b31a943c08 -r f70929728e86 external/njs_webcrypto_module.c --- a/external/njs_webcrypto_module.c Thu Aug 25 16:57:28 2022 -0700 +++ b/external/njs_webcrypto_module.c Fri Aug 26 21:49:14 2022 -0700 @@ -1935,6 +1935,266 @@ njs_set_rsa_padding(njs_vm_t *vm, njs_va } +static const EC_KEY * +njs_pkey_get_ec_key(EVP_PKEY *pkey) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + return EVP_PKEY_get0_EC_KEY(pkey); +#else + if (pkey->type != EVP_PKEY_EC) { + return NULL; + } + + return pkey->pkey.ec; +#endif +} + + +static int +njs_ec_group_order_bits(const EC_GROUP *group) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + return EC_GROUP_order_bits(group); +#else + int bits; + BIGNUM *order; + + order = BN_new(); + if (order == NULL) { + return 0; + } + + if (EC_GROUP_get_order(group, order, NULL) == 0) { + return 0; + } + + bits = BN_num_bits(order); + + BN_free(order); + + return bits; +#endif +} + + +static unsigned int +njs_ec_rs_size(EVP_PKEY *pkey) +{ + int bits; + const EC_KEY *ec_key; + const EC_GROUP *ec_group; + + ec_key = njs_pkey_get_ec_key(pkey); + if (ec_key == NULL) { + return 0; + } + + ec_group = EC_KEY_get0_group(ec_key); + if (ec_group == NULL) { + return 0; + } + + bits = njs_ec_group_order_bits(ec_group); + if (bits == 0) { + return 0; + } + + return (bits + 7) / 8; +} + + +static int +njs_bn_bn2binpad(const BIGNUM *bn, unsigned char *to, int tolen) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + return BN_bn2binpad(bn, to, tolen); +#else + return BN_bn2bin(bn, &to[tolen - BN_num_bytes(bn)]); +#endif +} + + +static njs_int_t +njs_convert_der_to_p1363(njs_vm_t *vm, EVP_PKEY *pkey, const u_char *der, + size_t der_len, u_char **pout, size_t *out_len) +{ + u_char *data; + unsigned n; + njs_int_t ret; + ECDSA_SIG *ec_sig; + + ret = NJS_OK; + ec_sig = NULL; + + n = njs_ec_rs_size(pkey); + if (n == 0) { + goto fail; + } + + data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n); + if (njs_slow_path(data == NULL)) { + goto memory_error; + } + + ec_sig = d2i_ECDSA_SIG(NULL, &der, der_len); + if (ec_sig == NULL) { + goto fail; + } + +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) + memset(data, 0, 2 * n); +#endif + + if (njs_bn_bn2binpad(ECDSA_SIG_get0_r(ec_sig), data, n) <= 0) { + goto fail; + } + + if (njs_bn_bn2binpad(ECDSA_SIG_get0_s(ec_sig), &data[n], n) <= 0) { + goto fail; + } + + *pout = data; + *out_len = 2 * n; + + goto done; + +fail: + + *out_len = 0; + +done: + + if (ec_sig != NULL) { + ECDSA_SIG_free(ec_sig); + } + + return ret; + +memory_error: + + njs_vm_memory_pool(vm); + + return NJS_ERROR; +} + + +static int +njs_ecdsa_sig_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + return ECDSA_SIG_set0(sig, r, s); +#else + if (r == NULL || s == NULL) { + return 0; + } + + BN_clear_free(sig->r); + BN_clear_free(sig->s); + + sig->r = r; + sig->s = s; + + return 1; +#endif +} + + +static njs_int_t +njs_convert_p1363_to_der(njs_vm_t *vm, EVP_PKEY *pkey, u_char *p1363, + size_t p1363_len, u_char **pout, size_t *out_len) +{ + int len; + BIGNUM *r, *s; + u_char *data; + unsigned n; + njs_int_t ret; + ECDSA_SIG *ec_sig; + + ret = NJS_OK; + ec_sig = NULL; + r = NULL; + s = NULL; + + n = njs_ec_rs_size(pkey); + + if (njs_slow_path(n == 0 || p1363_len != 2 * n)) { + goto fail; + } + + ec_sig = ECDSA_SIG_new(); + if (njs_slow_path(ec_sig == NULL)) { + goto memory_error; + } + + r = BN_new(); + if (njs_slow_path(r == NULL)) { + goto memory_error; + } + + s = BN_new(); + if (njs_slow_path(s == NULL)) { + goto memory_error; + } + + if (r != BN_bin2bn(p1363, n, r)) { + goto fail; + } + + if (s != BN_bin2bn(&p1363[n], n, s)) { + goto fail; + } + + if (njs_ecdsa_sig_set0(ec_sig, r, s) != 1) { + njs_webcrypto_error(vm, "njs_ecdsa_sig_set0() failed"); + ret = NJS_ERROR; + goto fail; + } + + data = njs_mp_alloc(njs_vm_memory_pool(vm), 2 * n + 16); + if (njs_slow_path(data == NULL)) { + goto memory_error; + } + + *pout = data; + len = i2d_ECDSA_SIG(ec_sig, &data); + + if (len < 0) { + goto fail; + } + + *out_len = len; + + goto done; + +fail: + + *out_len = 0; + +done: + + if (ec_sig != NULL) { + ECDSA_SIG_free(ec_sig); + + } else { + if (s != NULL) { + BN_free(s); + } + + if (r != NULL) { + BN_free(r); + } + } + + return ret; + +memory_error: + + njs_vm_memory_pool(vm); + + return NJS_ERROR; +} + + static njs_int_t njs_ext_sign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, njs_index_t verify) @@ -2121,7 +2381,24 @@ njs_ext_sign(njs_vm_t *vm, njs_value_t * goto fail; } + if (alg->type == NJS_ALGORITHM_ECDSA) { + ret = njs_convert_der_to_p1363(vm, key->pkey, dst, outlen, + &dst, &outlen); + if (njs_slow_path(ret != NJS_OK)) { + goto fail; + } + } + } else { + if (alg->type == NJS_ALGORITHM_ECDSA) { + ret = njs_convert_p1363_to_der(vm, key->pkey, sig.start, + sig.length, &sig.start, + &sig.length); + if (njs_slow_path(ret != NJS_OK)) { + goto fail; + } + } + ret = EVP_PKEY_verify(pctx, sig.start, sig.length, m, m_len); if (njs_slow_path(ret < 0)) { njs_webcrypto_error(vm, "EVP_PKEY_verify() failed"); diff -r 43b31a943c08 -r f70929728e86 test/webcrypto/README.rst --- a/test/webcrypto/README.rst Thu Aug 25 16:57:28 2022 -0700 +++ b/test/webcrypto/README.rst Fri Aug 26 21:49:14 2022 -0700 @@ -124,13 +124,21 @@ Signing data using RSA-PSS Signing data using ECDSA ------------------------ +Note: there are two types of ECDSA signatures: ASN.1 and IEEE P1363 +Webcrypto requires IEEE P1363, but OpenSSL outputs only ASN.1 variety. +To create P1363, we build an auxilary program asn12IEEEP1336 + .. code-block:: shell echo -n "SigneD-TExt" > text.txt openssl dgst -sha256 -binary text.txt > text.sha256 openssl pkeyutl -sign -in text.sha256 -inkey test/webcrypto/ec.pkcs8 | \ - base64 > test/webcrypto/text.base64.sha256.ecdsa.sig - base64 -d test/webcrypto/text.base64.sha256.ecdsa.sig > text.sha256.ecdsa.sig + base64 > test/webcrypto/text.base64.sha256.ecdsa.asn1.sig + base64 -d test/webcrypto/text.base64.sha256.ecdsa.asn1.sig > text.sha256.ecdsa.sig openssl pkeyutl -verify -in text.sha256 -pubin -inkey test/webcrypto/ec.spki -sigfile text.sha256.ecdsa.sig Signature Verified Successfully + # convert to IEEE P1363 + gcc test/webcrypto/asn12ieeep1336.c -lcrypto -o test/webcrypto/asn12ieeep1336 + base64 -d test/webcrypto/text.base64.sha256.ecdsa.asn1.sig | ./test/webcrypto/asn12IEEEP1336 | \ + base64 > test/webcrypto/text.base64.sha256.ecdsa.sig diff -r 43b31a943c08 -r f70929728e86 test/webcrypto/asn12ieeep1336.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/webcrypto/asn12ieeep1336.c Fri Aug 26 21:49:14 2022 -0700 @@ -0,0 +1,49 @@ +#include <openssl/ecdsa.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +int main(int argc, char * argv[]) { + int rbytes, sbytes, len, n; + ECDSA_SIG *ecSig; + unsigned char *p, *end; + const unsigned char *start; + unsigned char der[512]; + unsigned char out[64]; + + p = der; + end = &der[sizeof(der)]; + + for ( ;; ) { + n = read(STDIN_FILENO, der, end - p); + + if (n == 0) { + break; + } + + if ((end - p) == 0) { + printf("too large (> 512) der length in stdin"); + return EXIT_FAILURE; + } + + p += n; + } + + start = der; + ecSig = d2i_ECDSA_SIG(NULL, &start, p - der); + if (ecSig == NULL) { + printf("d2i_ECDSA_SIG() failed"); + return EXIT_FAILURE; + } + + rbytes = BN_num_bytes(ECDSA_SIG_get0_r(ecSig)); + sbytes = BN_num_bytes(ECDSA_SIG_get0_s(ecSig)); + + BN_bn2binpad(ECDSA_SIG_get0_r(ecSig), out, rbytes); + BN_bn2binpad(ECDSA_SIG_get0_s(ecSig), &out[32], sbytes); + + write(STDOUT_FILENO, out, sizeof(out)); + + return EXIT_SUCCESS; +} diff -r 43b31a943c08 -r f70929728e86 test/webcrypto/text.base64.sha1.ecdsa.sig --- a/test/webcrypto/text.base64.sha1.ecdsa.sig Thu Aug 25 16:57:28 2022 -0700 +++ b/test/webcrypto/text.base64.sha1.ecdsa.sig Fri Aug 26 21:49:14 2022 -0700 @@ -1,2 +1,2 @@ -MEQCIAZ/sGPfuYivvm5UsqZgiR2jtT88d2moIgnAh6h1jKdVAiALKiu3myhI046rhEThSLyReuTu -eIEgeCPBa2xGZnFXEg== +Bn+wY9+5iK++blSypmCJHaO1Pzx3aagiCcCHqHWMp1ULKiu3myhI046rhEThSLyReuTueIEgeCPB +a2xGZnFXEg== diff -r 43b31a943c08 -r f70929728e86 test/webcrypto/text.base64.sha256.ecdsa.sig --- a/test/webcrypto/text.base64.sha256.ecdsa.sig Thu Aug 25 16:57:28 2022 -0700 +++ b/test/webcrypto/text.base64.sha256.ecdsa.sig Fri Aug 26 21:49:14 2022 -0700 @@ -1,2 +1,2 @@ -MEUCIFEw11evEWohKswRe3Za0P0u7mvGj4kSnHix/EOKhxApAiEAq2QtwNvFg8RdY6t01ff8mUTP -nT1lEfMSRZmtuVxQuQA= +UTDXV68RaiEqzBF7dlrQ/S7ua8aPiRKceLH8Q4qHECmrZC3A28WDxF1jq3TV9/yZRM+dPWUR8xJF +ma25XFC5AA== _______________________________________________ nginx-devel mailing list -- nginx-devel@nginx.org To unsubscribe send an email to nginx-devel-le...@nginx.org