diff options
Diffstat (limited to 'src/crypto/ecdsa')
-rw-r--r-- | src/crypto/ecdsa/CMakeLists.txt | 20 | ||||
-rw-r--r-- | src/crypto/ecdsa/ecdsa.c | 496 | ||||
-rw-r--r-- | src/crypto/ecdsa/ecdsa_asn1.c | 116 | ||||
-rw-r--r-- | src/crypto/ecdsa/ecdsa_error.c | 32 | ||||
-rw-r--r-- | src/crypto/ecdsa/ecdsa_test.c | 328 |
5 files changed, 992 insertions, 0 deletions
diff --git a/src/crypto/ecdsa/CMakeLists.txt b/src/crypto/ecdsa/CMakeLists.txt new file mode 100644 index 0000000..4bddd27 --- /dev/null +++ b/src/crypto/ecdsa/CMakeLists.txt @@ -0,0 +1,20 @@ +include_directories(. .. ../../include) + +add_library( + ecdsa + + OBJECT + + ecdsa.c + ecdsa_asn1.c + ecdsa_error.c +) + + +add_executable( + ecdsa_test + + ecdsa_test.c +) + +target_link_libraries(ecdsa_test crypto) diff --git a/src/crypto/ecdsa/ecdsa.c b/src/crypto/ecdsa/ecdsa.c new file mode 100644 index 0000000..d389799 --- /dev/null +++ b/src/crypto/ecdsa/ecdsa.c @@ -0,0 +1,496 @@ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ecdsa.h> + +#include <string.h> + +#include <openssl/bn.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/thread.h> + +#include "../ec/internal.h" + + +int ECDSA_sign(int type, const uint8_t *digest, size_t digest_len, uint8_t *sig, + unsigned int *sig_len, EC_KEY *eckey) { + if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) { + return eckey->ecdsa_meth->sign(digest, digest_len, sig, sig_len, eckey); + } + + return ECDSA_sign_ex(type, digest, digest_len, sig, sig_len, NULL, NULL, + eckey); +} + +int ECDSA_verify(int type, const uint8_t *digest, size_t digest_len, + const uint8_t *sig, size_t sig_len, EC_KEY *eckey) { + ECDSA_SIG *s; + int ret = 0; + uint8_t *der = NULL; + + if (eckey->ecdsa_meth && eckey->ecdsa_meth->verify) { + return eckey->ecdsa_meth->verify(digest, digest_len, sig, sig_len, eckey); + } + + s = ECDSA_SIG_new(); + const uint8_t *sigp = sig; + if (s == NULL || d2i_ECDSA_SIG(&s, &sigp, sig_len) == NULL || + sigp != sig + sig_len) { + goto err; + } + + /* Ensure that the signature uses DER and doesn't have trailing garbage. */ + const int der_len = i2d_ECDSA_SIG(s, &der); + if (der_len < 0 || (size_t) der_len != sig_len || memcmp(sig, der, sig_len)) { + goto err; + } + + ret = ECDSA_do_verify(digest, digest_len, s, eckey); + +err: + if (der != NULL) { + OPENSSL_free(der); + } + if (s != NULL) { + ECDSA_SIG_free(s); + } + return ret; +} + +/* digest_to_bn interprets |digest_len| bytes from |digest| as a big-endian + * number and sets |out| to that value. It then truncates |out| so that it's, + * at most, as long as |order|. It returns one on success and zero otherwise. */ +static int digest_to_bn(BIGNUM *out, const uint8_t *digest, size_t digest_len, + const BIGNUM *order) { + size_t num_bits; + + num_bits = BN_num_bits(order); + /* Need to truncate digest if it is too long: first truncate whole + * bytes. */ + if (8 * digest_len > num_bits) { + digest_len = (num_bits + 7) / 8; + } + if (!BN_bin2bn(digest, digest_len, out)) { + OPENSSL_PUT_ERROR(ECDSA, digest_to_bn, ERR_R_BN_LIB); + return 0; + } + + /* If still too long truncate remaining bits with a shift */ + if ((8 * digest_len > num_bits) && + !BN_rshift(out, out, 8 - (num_bits & 0x7))) { + OPENSSL_PUT_ERROR(ECDSA, digest_to_bn, ERR_R_BN_LIB); + return 0; + } + + return 1; +} + +ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len, + EC_KEY *key) { + return ECDSA_do_sign_ex(digest, digest_len, NULL, NULL, key); +} + +int ECDSA_do_verify(const uint8_t *digest, size_t digest_len, + const ECDSA_SIG *sig, EC_KEY *eckey) { + int ret = 0; + BN_CTX *ctx; + BIGNUM *order, *u1, *u2, *m, *X; + EC_POINT *point = NULL; + const EC_GROUP *group; + const EC_POINT *pub_key; + + if (eckey->ecdsa_meth && eckey->ecdsa_meth->verify) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_NOT_IMPLEMENTED); + return 0; + } + + /* check input values */ + if ((group = EC_KEY_get0_group(eckey)) == NULL || + (pub_key = EC_KEY_get0_public_key(eckey)) == NULL || + sig == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_MISSING_PARAMETERS); + return 0; + } + + ctx = BN_CTX_new(); + if (!ctx) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_MALLOC_FAILURE); + return 0; + } + BN_CTX_start(ctx); + order = BN_CTX_get(ctx); + u1 = BN_CTX_get(ctx); + u2 = BN_CTX_get(ctx); + m = BN_CTX_get(ctx); + X = BN_CTX_get(ctx); + if (!X) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB); + goto err; + } + + if (!EC_GROUP_get_order(group, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB); + goto err; + } + + if (BN_is_zero(sig->r) || BN_is_negative(sig->r) || + BN_ucmp(sig->r, order) >= 0 || BN_is_zero(sig->s) || + BN_is_negative(sig->s) || BN_ucmp(sig->s, order) >= 0) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ECDSA_R_BAD_SIGNATURE); + ret = 0; /* signature is invalid */ + goto err; + } + /* calculate tmp1 = inv(S) mod order */ + if (!BN_mod_inverse(u2, sig->s, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB); + goto err; + } + if (!digest_to_bn(m, digest, digest_len, order)) { + goto err; + } + /* u1 = m * tmp mod order */ + if (!BN_mod_mul(u1, m, u2, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB); + goto err; + } + /* u2 = r * w mod q */ + if (!BN_mod_mul(u2, sig->r, u2, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB); + goto err; + } + + point = EC_POINT_new(group); + if (point == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_MALLOC_FAILURE); + goto err; + } + if (!EC_POINT_mul(group, point, u1, pub_key, u2, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB); + goto err; + } + if (!EC_POINT_get_affine_coordinates_GFp(group, point, X, NULL, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_EC_LIB); + goto err; + } + if (!BN_nnmod(u1, X, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_verify, ERR_R_BN_LIB); + goto err; + } + /* if the signature is correct u1 is equal to sig->r */ + ret = (BN_ucmp(u1, sig->r) == 0); + +err: + BN_CTX_end(ctx); + BN_CTX_free(ctx); + if (point) { + EC_POINT_free(point); + } + return ret; +} + +static int ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, + BIGNUM **rp, const uint8_t *digest, + size_t digest_len) { + BN_CTX *ctx = NULL; + BIGNUM *k = NULL, *r = NULL, *order = NULL, *X = NULL; + EC_POINT *tmp_point = NULL; + const EC_GROUP *group; + int ret = 0; + + if (eckey == NULL || (group = EC_KEY_get0_group(eckey)) == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx_in == NULL) { + if ((ctx = BN_CTX_new()) == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_MALLOC_FAILURE); + return 0; + } + } else { + ctx = ctx_in; + } + + k = BN_new(); /* this value is later returned in *kinvp */ + r = BN_new(); /* this value is later returned in *rp */ + order = BN_new(); + X = BN_new(); + if (!k || !r || !order || !X) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_MALLOC_FAILURE); + goto err; + } + tmp_point = EC_POINT_new(group); + if (tmp_point == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_EC_LIB); + goto err; + } + if (!EC_GROUP_get_order(group, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_EC_LIB); + goto err; + } + + do { + /* If possible, we'll include the private key and message digest in the k + * generation. The |digest| argument is only empty if |ECDSA_sign_setup| is + * being used. */ + do { + int ok; + + if (digest_len > 0) { + ok = BN_generate_dsa_nonce(k, order, EC_KEY_get0_private_key(eckey), + digest, digest_len, ctx); + } else { + ok = BN_rand_range(k, order); + } + if (!ok) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, + ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED); + goto err; + } + } while (BN_is_zero(k)); + + /* We do not want timing information to leak the length of k, + * so we compute G*k using an equivalent scalar of fixed + * bit-length. */ + + if (!BN_add(k, k, order)) { + goto err; + } + if (BN_num_bits(k) <= BN_num_bits(order)) { + if (!BN_add(k, k, order)) { + goto err; + } + } + + /* compute r the x-coordinate of generator * k */ + if (!EC_POINT_mul(group, tmp_point, k, NULL, NULL, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_EC_LIB); + goto err; + } + if (!EC_POINT_get_affine_coordinates_GFp(group, tmp_point, X, NULL, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_EC_LIB); + goto err; + } + + if (!BN_nnmod(r, X, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_BN_LIB); + goto err; + } + } while (BN_is_zero(r)); + + /* compute the inverse of k */ + if (!BN_mod_inverse(k, k, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ecdsa_sign_setup, ERR_R_BN_LIB); + goto err; + } + /* clear old values if necessary */ + if (*rp != NULL) { + BN_clear_free(*rp); + } + if (*kinvp != NULL) { + BN_clear_free(*kinvp); + } + + /* save the pre-computed values */ + *rp = r; + *kinvp = k; + ret = 1; + +err: + if (!ret) { + if (k != NULL) { + BN_clear_free(k); + } + if (r != NULL) { + BN_clear_free(r); + } + } + if (ctx_in == NULL) + BN_CTX_free(ctx); + if (order != NULL) + BN_free(order); + if (tmp_point != NULL) + EC_POINT_free(tmp_point); + if (X) + BN_clear_free(X); + return ret; +} + +int ECDSA_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, BIGNUM **rp) { + return ecdsa_sign_setup(eckey, ctx, kinv, rp, NULL, 0); +} + +ECDSA_SIG *ECDSA_do_sign_ex(const uint8_t *digest, size_t digest_len, + const BIGNUM *in_kinv, const BIGNUM *in_r, + EC_KEY *eckey) { + int ok = 0; + BIGNUM *kinv = NULL, *s, *m = NULL, *tmp = NULL, *order = NULL; + const BIGNUM *ckinv; + BN_CTX *ctx = NULL; + const EC_GROUP *group; + ECDSA_SIG *ret; + const BIGNUM *priv_key; + + if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ECDSA_R_NOT_IMPLEMENTED); + return NULL; + } + + group = EC_KEY_get0_group(eckey); + priv_key = EC_KEY_get0_private_key(eckey); + + if (group == NULL || priv_key == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + ret = ECDSA_SIG_new(); + if (!ret) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE); + return NULL; + } + s = ret->s; + + if ((ctx = BN_CTX_new()) == NULL || (order = BN_new()) == NULL || + (tmp = BN_new()) == NULL || (m = BN_new()) == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!EC_GROUP_get_order(group, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_EC_LIB); + goto err; + } + if (!digest_to_bn(m, digest, digest_len, order)) { + goto err; + } + for (;;) { + if (in_kinv == NULL || in_r == NULL) { + if (!ecdsa_sign_setup(eckey, ctx, &kinv, &ret->r, digest, digest_len)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_ECDSA_LIB); + goto err; + } + ckinv = kinv; + } else { + ckinv = in_kinv; + if (BN_copy(ret->r, in_r) == NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_MALLOC_FAILURE); + goto err; + } + } + + if (!BN_mod_mul(tmp, priv_key, ret->r, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB); + goto err; + } + if (!BN_mod_add_quick(s, tmp, m, order)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB); + goto err; + } + if (!BN_mod_mul(s, s, ckinv, order, ctx)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ERR_R_BN_LIB); + goto err; + } + if (BN_is_zero(s)) { + /* if kinv and r have been supplied by the caller + * don't to generate new kinv and r values */ + if (in_kinv != NULL && in_r != NULL) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_do_sign_ex, ECDSA_R_NEED_NEW_SETUP_VALUES); + goto err; + } + } else { + /* s != 0 => we have a valid signature */ + break; + } + } + + ok = 1; + +err: + if (!ok) { + ECDSA_SIG_free(ret); + ret = NULL; + } + if (ctx) + BN_CTX_free(ctx); + if (m) + BN_clear_free(m); + if (tmp) + BN_clear_free(tmp); + if (order) + BN_free(order); + if (kinv) + BN_clear_free(kinv); + return ret; +} + +int ECDSA_sign_ex(int type, const uint8_t *digest, size_t digest_len, + uint8_t *sig, unsigned int *sig_len, const BIGNUM *kinv, + const BIGNUM *r, EC_KEY *eckey) { + ECDSA_SIG *s = NULL; + + if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_sign_ex, ECDSA_R_NOT_IMPLEMENTED); + *sig_len = 0; + return 0; + } + + s = ECDSA_do_sign_ex(digest, digest_len, kinv, r, eckey); + if (s == NULL) { + *sig_len = 0; + return 0; + } + *sig_len = i2d_ECDSA_SIG(s, &sig); + ECDSA_SIG_free(s); + return 1; +} diff --git a/src/crypto/ecdsa/ecdsa_asn1.c b/src/crypto/ecdsa/ecdsa_asn1.c new file mode 100644 index 0000000..b3c6a87 --- /dev/null +++ b/src/crypto/ecdsa/ecdsa_asn1.c @@ -0,0 +1,116 @@ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ecdsa.h> + +#include <openssl/asn1.h> +#include <openssl/asn1t.h> +#include <openssl/ec_key.h> + +#include "../ec/internal.h" + + +DECLARE_ASN1_FUNCTIONS_const(ECDSA_SIG); +DECLARE_ASN1_ENCODE_FUNCTIONS_const(ECDSA_SIG, ECDSA_SIG); + +ASN1_SEQUENCE(ECDSA_SIG) = { + ASN1_SIMPLE(ECDSA_SIG, r, CBIGNUM), + ASN1_SIMPLE(ECDSA_SIG, s, CBIGNUM), +} ASN1_SEQUENCE_END(ECDSA_SIG); + +IMPLEMENT_ASN1_FUNCTIONS_const(ECDSA_SIG); + +size_t ECDSA_size(const EC_KEY *key) { + size_t ret, i, group_order_size; + ASN1_INTEGER bs; + BIGNUM *order = NULL; + unsigned char buf[4]; + const EC_GROUP *group; + + if (key->ecdsa_meth && key->ecdsa_meth->group_order_size) { + group_order_size = key->ecdsa_meth->group_order_size(key); + } else { + size_t num_bits; + + if (key == NULL) { + return 0; + } + group = EC_KEY_get0_group(key); + if (group == NULL) { + return 0; + } + + order = BN_new(); + if (order == NULL) { + return 0; + } + if (!EC_GROUP_get_order(group, order, NULL)) { + BN_clear_free(order); + return 0; + } + + num_bits = BN_num_bits(order); + group_order_size = (num_bits + 7) / 8; + } + + bs.length = group_order_size; + bs.data = buf; + bs.type = V_ASN1_INTEGER; + /* If the top bit is set the ASN.1 encoding is 1 larger. */ + buf[0] = 0xff; + + i = i2d_ASN1_INTEGER(&bs, NULL); + i += i; /* r and s */ + ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE); + BN_clear_free(order); + return ret; +} diff --git a/src/crypto/ecdsa/ecdsa_error.c b/src/crypto/ecdsa/ecdsa_error.c new file mode 100644 index 0000000..cbd69ce --- /dev/null +++ b/src/crypto/ecdsa/ecdsa_error.c @@ -0,0 +1,32 @@ +/* Copyright (c) 2014, Google Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include <openssl/err.h> + +#include <openssl/ecdsa.h> + +const ERR_STRING_DATA ECDSA_error_string_data[] = { + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_do_sign_ex, 0), "ECDSA_do_sign_ex"}, + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_do_verify, 0), "ECDSA_do_verify"}, + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_sign_ex, 0), "ECDSA_sign_ex"}, + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ECDSA_sign_setup, 0), "ECDSA_sign_setup"}, + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_digest_to_bn, 0), "digest_to_bn"}, + {ERR_PACK(ERR_LIB_ECDSA, ECDSA_F_ecdsa_sign_setup, 0), "ecdsa_sign_setup"}, + {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_BAD_SIGNATURE), "BAD_SIGNATURE"}, + {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_MISSING_PARAMETERS), "MISSING_PARAMETERS"}, + {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_NEED_NEW_SETUP_VALUES), "NEED_NEW_SETUP_VALUES"}, + {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_NOT_IMPLEMENTED), "NOT_IMPLEMENTED"}, + {ERR_PACK(ERR_LIB_ECDSA, 0, ECDSA_R_RANDOM_NUMBER_GENERATION_FAILED), "RANDOM_NUMBER_GENERATION_FAILED"}, + {0, NULL}, +}; diff --git a/src/crypto/ecdsa/ecdsa_test.c b/src/crypto/ecdsa/ecdsa_test.c new file mode 100644 index 0000000..d48f9c3 --- /dev/null +++ b/src/crypto/ecdsa/ecdsa_test.c @@ -0,0 +1,328 @@ +/* ==================================================================== + * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@OpenSSL.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). */ + +#include <openssl/ecdsa.h> + +#include <openssl/bio.h> +#include <openssl/bn.h> +#include <openssl/crypto.h> +#include <openssl/ec.h> +#include <openssl/err.h> +#include <openssl/mem.h> +#include <openssl/obj.h> +#include <openssl/rand.h> + + +int test_builtin(BIO *out) { + size_t n = 0; + EC_KEY *eckey = NULL, *wrong_eckey = NULL; + EC_GROUP *group; + BIGNUM *order = NULL; + ECDSA_SIG *ecdsa_sig = NULL; + unsigned char digest[20], wrong_digest[20]; + unsigned char *signature = NULL; + const unsigned char *sig_ptr; + unsigned char *sig_ptr2; + unsigned char *raw_buf = NULL; + unsigned int sig_len, r_len, s_len, bn_len, buf_len; + int nid, ret = 0; + + /* fill digest values with some random data */ + if (!RAND_bytes(digest, 20) || !RAND_bytes(wrong_digest, 20)) { + BIO_printf(out, "ERROR: unable to get random data\n"); + goto builtin_err; + } + + order = BN_new(); + if (order == NULL) { + goto builtin_err; + } + + /* create and verify a ecdsa signature with every availble curve + * (with ) */ + BIO_printf(out, + "\ntesting ECDSA_sign() and ECDSA_verify() " + "with some internal curves:\n"); + + static const int kCurveNIDs[] = {NID_secp224r1, NID_X9_62_prime256v1, + NID_secp384r1, NID_secp521r1, NID_undef}; + + /* now create and verify a signature for every curve */ + for (n = 0; kCurveNIDs[n] != NID_undef; n++) { + unsigned char dirt, offset; + + nid = kCurveNIDs[n]; + /* create new ecdsa key (== EC_KEY) */ + eckey = EC_KEY_new(); + if (eckey == NULL) { + goto builtin_err; + } + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + goto builtin_err; + } + if (!EC_KEY_set_group(eckey, group)) { + goto builtin_err; + } + EC_GROUP_free(group); + if (!EC_GROUP_get_order(EC_KEY_get0_group(eckey), order, NULL)) { + goto builtin_err; + } + if (BN_num_bits(order) < 160) { + /* Too small to test. */ + EC_KEY_free(eckey); + eckey = NULL; + continue; + } + + BIO_printf(out, "%s: ", OBJ_nid2sn(nid)); + /* create key */ + if (!EC_KEY_generate_key(eckey)) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + /* create second key */ + wrong_eckey = EC_KEY_new(); + if (wrong_eckey == NULL) { + goto builtin_err; + } + group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + goto builtin_err; + } + if (EC_KEY_set_group(wrong_eckey, group) == 0) { + goto builtin_err; + } + EC_GROUP_free(group); + if (!EC_KEY_generate_key(wrong_eckey)) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + + BIO_printf(out, "."); + (void)BIO_flush(out); + /* check key */ + if (!EC_KEY_check_key(eckey)) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + /* create signature */ + sig_len = ECDSA_size(eckey); + signature = OPENSSL_malloc(sig_len); + if (signature == NULL) { + goto builtin_err; + } + if (!ECDSA_sign(0, digest, 20, signature, &sig_len, eckey)) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + /* verify signature */ + if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) != 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + /* verify signature with the wrong key */ + if (ECDSA_verify(0, digest, 20, signature, sig_len, wrong_eckey) == 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + /* wrong digest */ + if (ECDSA_verify(0, wrong_digest, 20, signature, sig_len, eckey) == 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + /* wrong length */ + if (ECDSA_verify(0, digest, 20, signature, sig_len - 1, eckey) == 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + + /* Modify a single byte of the signature: to ensure we don't + * garble the ASN1 structure, we read the raw signature and + * modify a byte in one of the bignums directly. */ + sig_ptr = signature; + ecdsa_sig = d2i_ECDSA_SIG(NULL, &sig_ptr, sig_len); + if (ecdsa_sig == NULL) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + + /* Store the two BIGNUMs in raw_buf. */ + r_len = BN_num_bytes(ecdsa_sig->r); + s_len = BN_num_bytes(ecdsa_sig->s); + bn_len = BN_num_bytes(order); + if (r_len > bn_len || s_len > bn_len) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + buf_len = 2 * bn_len; + raw_buf = OPENSSL_malloc(2 * bn_len); + if (raw_buf == NULL) { + goto builtin_err; + } + /* Pad the bignums with leading zeroes. */ + if (!BN_bn2bin_padded(raw_buf, bn_len, ecdsa_sig->r) || + !BN_bn2bin_padded(raw_buf + bn_len, bn_len, ecdsa_sig->s)) { + goto builtin_err; + } + + /* Modify a single byte in the buffer. */ + offset = raw_buf[10] % buf_len; + dirt = raw_buf[11] ? raw_buf[11] : 1; + raw_buf[offset] ^= dirt; + /* Now read the BIGNUMs back in from raw_buf. */ + if (BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL || + BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL) { + goto builtin_err; + } + + sig_ptr2 = signature; + sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr2); + if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) == 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + /* Sanity check: undo the modification and verify signature. */ + raw_buf[offset] ^= dirt; + if (BN_bin2bn(raw_buf, bn_len, ecdsa_sig->r) == NULL || + BN_bin2bn(raw_buf + bn_len, bn_len, ecdsa_sig->s) == NULL) { + goto builtin_err; + } + + sig_ptr2 = signature; + sig_len = i2d_ECDSA_SIG(ecdsa_sig, &sig_ptr2); + if (ECDSA_verify(0, digest, 20, signature, sig_len, eckey) != 1) { + BIO_printf(out, " failed\n"); + goto builtin_err; + } + BIO_printf(out, "."); + (void)BIO_flush(out); + + BIO_printf(out, " ok\n"); + /* cleanup */ + /* clean bogus errors */ + ERR_clear_error(); + OPENSSL_free(signature); + signature = NULL; + EC_KEY_free(eckey); + eckey = NULL; + EC_KEY_free(wrong_eckey); + wrong_eckey = NULL; + ECDSA_SIG_free(ecdsa_sig); + ecdsa_sig = NULL; + OPENSSL_free(raw_buf); + raw_buf = NULL; + } + + ret = 1; +builtin_err: + if (eckey) { + EC_KEY_free(eckey); + } + if (order) { + BN_free(order); + } + if (wrong_eckey) { + EC_KEY_free(wrong_eckey); + } + if (ecdsa_sig) { + ECDSA_SIG_free(ecdsa_sig); + } + if (signature) { + OPENSSL_free(signature); + } + if (raw_buf) { + OPENSSL_free(raw_buf); + } + + return ret; +} + +int main(void) { + int ret = 1; + BIO *out; + + CRYPTO_library_init(); + ERR_load_crypto_strings(); + + out = BIO_new_fp(stdout, BIO_NOCLOSE); + + if (!test_builtin(out)) + goto err; + + ret = 0; + +err: + if (ret) + BIO_printf(out, "\nECDSA test failed\n"); + else + BIO_printf(out, "\nPASS\n"); + if (ret) + BIO_print_errors(out); + + if (out != NULL) + BIO_free(out); + + return ret; +} |