diff options
Diffstat (limited to 'src/crypto/ecdsa/ecdsa_asn1.c')
-rw-r--r-- | src/crypto/ecdsa/ecdsa_asn1.c | 183 |
1 files changed, 146 insertions, 37 deletions
diff --git a/src/crypto/ecdsa/ecdsa_asn1.c b/src/crypto/ecdsa/ecdsa_asn1.c index f557ca7..f2d7c36 100644 --- a/src/crypto/ecdsa/ecdsa_asn1.c +++ b/src/crypto/ecdsa/ecdsa_asn1.c @@ -52,45 +52,33 @@ #include <openssl/ecdsa.h> -#include <openssl/asn1.h> -#include <openssl/asn1t.h> +#include <limits.h> +#include <string.h> + +#include <openssl/bn.h> +#include <openssl/bytestring.h> +#include <openssl/err.h> #include <openssl/ec_key.h> #include <openssl/mem.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_ENCODE_FUNCTIONS_const_fname(ECDSA_SIG, ECDSA_SIG, 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 == NULL) { + return 0; + } + size_t group_order_size; 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); + const EC_GROUP *group = EC_KEY_get0_group(key); if (group == NULL) { return 0; } - order = BN_new(); + BIGNUM *order = BN_new(); if (order == NULL) { return 0; } @@ -99,21 +87,11 @@ size_t ECDSA_size(const EC_KEY *key) { return 0; } - num_bits = BN_num_bits(order); - group_order_size = (num_bits + 7) / 8; + group_order_size = BN_num_bytes(order); + BN_clear_free(order); } - 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; + return ECDSA_SIG_max_len(group_order_size); } ECDSA_SIG *ECDSA_SIG_new(void) { @@ -139,3 +117,134 @@ void ECDSA_SIG_free(ECDSA_SIG *sig) { BN_free(sig->s); OPENSSL_free(sig); } + +ECDSA_SIG *ECDSA_SIG_parse(CBS *cbs) { + ECDSA_SIG *ret = ECDSA_SIG_new(); + if (ret == NULL) { + return NULL; + } + CBS child; + if (!CBS_get_asn1(cbs, &child, CBS_ASN1_SEQUENCE) || + !BN_cbs2unsigned(&child, ret->r) || + !BN_cbs2unsigned(&child, ret->s) || + CBS_len(&child) != 0) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_BAD_SIGNATURE); + ECDSA_SIG_free(ret); + return NULL; + } + return ret; +} + +ECDSA_SIG *ECDSA_SIG_from_bytes(const uint8_t *in, size_t in_len) { + CBS cbs; + CBS_init(&cbs, in, in_len); + ECDSA_SIG *ret = ECDSA_SIG_parse(&cbs); + if (ret == NULL || CBS_len(&cbs) != 0) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_BAD_SIGNATURE); + ECDSA_SIG_free(ret); + return NULL; + } + return ret; +} + +int ECDSA_SIG_marshal(CBB *cbb, const ECDSA_SIG *sig) { + CBB child; + if (!CBB_add_asn1(cbb, &child, CBS_ASN1_SEQUENCE) || + !BN_bn2cbb(&child, sig->r) || + !BN_bn2cbb(&child, sig->s) || + !CBB_flush(cbb)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_ENCODE_ERROR); + return 0; + } + return 1; +} + +int ECDSA_SIG_to_bytes(uint8_t **out_bytes, size_t *out_len, + const ECDSA_SIG *sig) { + CBB cbb; + CBB_zero(&cbb); + if (!CBB_init(&cbb, 0) || + !ECDSA_SIG_marshal(&cbb, sig) || + !CBB_finish(&cbb, out_bytes, out_len)) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_ENCODE_ERROR); + CBB_cleanup(&cbb); + return 0; + } + return 1; +} + +/* der_len_len returns the number of bytes needed to represent a length of |len| + * in DER. */ +static size_t der_len_len(size_t len) { + if (len < 0x80) { + return 1; + } + size_t ret = 1; + while (len > 0) { + ret++; + len >>= 8; + } + return ret; +} + +size_t ECDSA_SIG_max_len(size_t order_len) { + /* Compute the maximum length of an |order_len| byte integer. Defensively + * assume that the leading 0x00 is included. */ + size_t integer_len = 1 /* tag */ + der_len_len(order_len + 1) + 1 + order_len; + if (integer_len < order_len) { + return 0; + } + /* An ECDSA signature is two INTEGERs. */ + size_t value_len = 2 * integer_len; + if (value_len < integer_len) { + return 0; + } + /* Add the header. */ + size_t ret = 1 /* tag */ + der_len_len(value_len) + value_len; + if (ret < value_len) { + return 0; + } + return ret; +} + +ECDSA_SIG *d2i_ECDSA_SIG(ECDSA_SIG **out, const uint8_t **inp, long len) { + if (len < 0) { + return NULL; + } + CBS cbs; + CBS_init(&cbs, *inp, (size_t)len); + ECDSA_SIG *ret = ECDSA_SIG_parse(&cbs); + if (ret == NULL) { + return NULL; + } + if (out != NULL) { + ECDSA_SIG_free(*out); + *out = ret; + } + *inp += (size_t)len - CBS_len(&cbs); + return ret; +} + +int i2d_ECDSA_SIG(const ECDSA_SIG *sig, uint8_t **outp) { + uint8_t *der; + size_t der_len; + if (!ECDSA_SIG_to_bytes(&der, &der_len, sig)) { + return -1; + } + if (der_len > INT_MAX) { + OPENSSL_PUT_ERROR(ECDSA, ERR_R_OVERFLOW); + OPENSSL_free(der); + return -1; + } + if (outp != NULL) { + if (*outp == NULL) { + *outp = der; + der = NULL; + } else { + memcpy(*outp, der, der_len); + *outp += der_len; + } + } + OPENSSL_free(der); + return (int)der_len; +} |