Search code examples
opensslcompressionpointecdsa

ECDSA: How to get y coordinate from uncompressing x using openssl


What I am trying to do with ECDSA is take an x "compressed" coordinate and a y "bit" from values taken from external ECDSA hardware (it's really a public key) and then trying to recover the y coordinate (uncompressed) with OpenSSL function calls.

I am trying the following code, but it does not work as I expected (my xy[] array does not get populated with correct data). Can anyone help?

I'm new to ECDSA and even newer to OpenSSL, but I was thinking I could do the following:

EC_GROUP *curve_group = EC_GROUP_new_by_curve_name(NID_X9_62_prime192v1);
EC_POINT *point;
BIGNUM *x_compressed = BN_new();
int y_chooser_bit = 1;
int results = 0;
size_t returnsize = 0;
 
unsigned char x_compressed_byte_array[25] = {0x02, 0x71, 0xc0, 0x73, 0x73, 
                                             0x9b, 0xbf, 0xc2, 0x0a, 
                                             0x81, 0xcd, 0xdd, 0xf4, 
                                             0xcf, 0xca, 0xc7, 0xb5, 
                                             0xa9, 0x99, 0x61, 0x23, 
                                             0x2c, 0x5c, 0x63, 0x7a};

unsigned char xy[49];

// create a big number from the unsigned char array
BN_bin2bn(&x_compressed_byte_array[0],200,NULL); // length is in bits?

point = EC_POINT_new(curve_group);

results = EC_POINT_set_compressed_coordinates_GFp(curve_group, point, 
                                                  x_compressed,                                                               
                                                  y_chooser_bit, NULL);

returnsize = EC_POINT_point2oct(curve_group, point, 
                                POINT_CONVERSION_UNCOMPRESSED, 
                                &xy[0], 49, NULL); // 49

// clean up allocated memory
BN_free(x_compressed);
EC_POINT_free(point);
EC_GROUP_free(curve_group);

Solution

  • In this line of your code

    BN_bin2bn(&x_compressed_byte_array[0],200,NULL); // length is in bits?
    

    You should pass bn_compressed, the BIGNUM you created, to hold the converted result. And the length is in bytes.

    BN_bin2bn(&x_compressed_byte_array[0],sizeof(x_compressed_byte_array),x_compressed);
    

    After you set_compressed_coordinates, you can verify if the point you just created locate correctly in the group.

    if (!EC_POINT_is_on_curve(curve_group,point,NULL)) return 0;
    

    To further examine the point coordinates (x,y), you can place this in your code.

    if (!EC_POINT_get_affine_coordinates_GFp(curve_group, point, x, y, NULL)) return 0;
    fprintf(stdout, "\point = (x,y)\n");
    fprintf(stdout, "     x = 0x");
    BN_print_fp(stdout, x);
    fprintf(stdout, "\n     y = 0x");
    BN_print_fp(stdout, y);
    fprintf(stdout, "\n");
    

    Just remember to new/free BIGNUM x and y at the first/end of your function.

    BIGNUM *x, *y;
    x = BN_new();
    y = BN_new();
    
    /* your code */
    
    if(x) BN_free(x);
    if(y) BN_free(y);
    

    And it should work. The result xy[49] I have is

    0x0471C073739BBFC20A81CDDDF4CFCAC7B7A99961232C5C637C388AB06AA7E43A2B5CFE2D7F3AC2DF910D6F8D8F209CD817