Search code examples
copensslecdsalibssl

ECDSA signature getting truncated


I am trying to create an ECDSA signature and it's getting created perfectly most of the time but sometimes it's getting truncated randomly like the r and s value are both supposed to be 48 but sometimes I am getting 47 for one and 48 for other. I can't tell what the issue is, is it an issue with the signature or is there an issue with the procedure used for obtaining the r and s value from the signature?

    const BIGNUM *r;
    const BIGNUM *s;
    ECDSA_SIG_get0(signature, &r, &s);
    char *R = BN_bn2hex(r);
    char *S = BN_bn2hex(s);
    int r_len = BN_num_bytes(r);
    int s_len = BN_num_bytes(s);
    uint8_t *r_bytes = (uint8_t *)malloc(r_len);
    uint8_t *s_bytes = (uint8_t *)malloc(s_len);
    BN_bn2bin(r, r_bytes);
    BN_bn2bin(s, s_bytes);
    int signature_len = r_len + s_len;
    uint8_t *signature2 = (uint8_t *)malloc(signature_len);
    memcpy(signature2, r_bytes, r_len);
    memcpy(signature2 + r_len, s_bytes, s_len);
    int max_encoded_len = EVP_ENCODE_LENGTH(signature_len);
    *ppcSignature1 = (char *)calloc(max_encoded_len,1);

I have tried to get the r and s value again from the same signature by adding this code block :

if(r_len != 48 || s_len != 48){
    const BIGNUM *r_temp = ECDSA_SIG_get0_r(signature);
    const BIGNUM *s_temp = ECDSA_SIG_get0_s(signature);
    char *R_temp = BN_bn2hex(r_temp);
    char *S_temp = BN_bn2hex(s_temp);
    printf("R_LEN : %s\n",R_temp);
    printf("S_LEN : %s\n",S_temp);
}

And still the values of the rlen and slen was the same.


Solution

  • There does not appear to be an issue with truncation of the signature, but rather with the interpretation of the value returned by BN_num_bytes (noting that it's a function-like macro.)

    BN_num_bytes indicates how many bytes would be needed for an integer value to hold the number in a BIGNUM. This does not mean that the signature consists of fewer bytes, it's still a 48-byte value. But if BN_num_bytes returns 47, then you know that, interpreting that number as having 48 bytes, the upper 8-15 bits of the value will be 0.

    If you happened to have a signature with the value 0x34529, then you would obtain the value 3 from BN_num_bytes, as you only need three bytes to represent that number. In other words it has 3 significant bytes.


    From the notes on the page describing BN_num_bytes:

    Some have tried using BN_num_bits() on individual numbers in RSA keys, DH keys and DSA keys, and found that they don't always come up with the number of bits they expected (something like 512, 1024, 2048, ...). This is because generating a number with some specific number of bits doesn't always set the highest bits, thereby making the number of significant bits a little lower. If you want to know the "key size" of such a key, either use functions like RSA_size(), DH_size() and DSA_size(), or use BN_num_bytes() and multiply with 8 (although there's no real guarantee that will match the "key size", just a lot more probability).

    The documentation notes the issue with using BN_num_bits to get a key size in bits, which has 50/50 odds of actually matching the expected key size. It then notes that you can use BN_num_bytes, but it's only more likely to give you the correct result, not guaranteed (from 1/2 to 1/256 chance of a wrong result.)


    BN_num_bytes can be useful for allocation space, but it's not going to give you information on the nature of the signature. It's a generic tool related to BIGNUM, it doesn't "know" how big you signature is, it just sees a number.