Skip to content

Commit

Permalink
Added support for raw public key export and import
Browse files Browse the repository at this point in the history
In total there are 3 new functions to export/import raw values of public and private keys.
Public key functions are used to handle ASN1_PKEY_CTRL_GET1_TLS_ENCPT and ASN1_PKEY_CTRL_SET1_TLS_ENCPT control codes used by SSL stack during key exchange.
Private key export function is just a convenient way to get the key value (EVP_PKEY_new_raw_private_key does not provide paramset to use for key import).
  • Loading branch information
arx11 committed Mar 23, 2024
1 parent ede3886 commit 432a3d9
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 0 deletions.
153 changes: 153 additions & 0 deletions gost_ameth.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,135 @@ static ASN1_STRING *gost_encode_cms_params(int ka_nid)
return ret;
}

static int gost_set_raw_pub_key(EVP_PKEY *pk, const unsigned char *pub, size_t len)
{
int ret = 0;
BIGNUM *X = NULL;
BIGNUM *Y = NULL;
EC_POINT *pub_key = NULL;
EC_KEY *ec;
const EC_GROUP *group;
const BIGNUM *order;
int half;

if (pub == NULL || len == 0) {
return 0;
}

if ((ec = EVP_PKEY_get0(pk)) == NULL
|| (group = EC_KEY_get0_group(ec)) == NULL
|| (order = EC_GROUP_get0_order(group)) == NULL) {
return 0;
}

half = len / 2;
if ((X = BN_lebin2bn(pub, half, NULL)) == NULL
|| (Y = BN_lebin2bn(pub + half, half, NULL)) == NULL
|| (pub_key = EC_POINT_new(group)) == NULL) {
goto end;
}

if (EC_POINT_set_affine_coordinates(group, pub_key, X, Y, NULL) != 1) {
goto end;
}

if (EC_KEY_set_public_key(ec, pub_key) != 1) {
goto end;
}

ret = 1;
end:
EC_POINT_free(pub_key);
BN_free(Y);
BN_free(X);
return ret;
}

static int gost_get_raw_priv_key(const EVP_PKEY *pk, unsigned char *priv, size_t *len)
{
const EC_KEY *ec;
const EC_GROUP *group;
const BIGNUM *order;
const BIGNUM *priv_key;
int half;

if (len == NULL) {
return 0;
}

if ((ec = EVP_PKEY_get0(pk)) == NULL
|| (group = EC_KEY_get0_group(ec)) == NULL
|| (order = EC_GROUP_get0_order(group)) == NULL
|| (priv_key = EC_KEY_get0_private_key(ec)) == NULL) {
return 0;
}

half = BN_num_bytes(order);
if (priv == NULL) {
*len = half;
return 1;
}

if (BN_bn2lebinpad(priv_key, priv, half) != half) {
return 0;
}

return 1;
}

static int gost_get_raw_pub_key(const EVP_PKEY *pk, unsigned char *pub, size_t *len)
{
int ret = 0;
BIGNUM *X = NULL;
BIGNUM *Y = NULL;
const EC_KEY *ec;
const EC_GROUP *group;
const BIGNUM *order;
const EC_POINT *pub_key;
int half;

if (len == NULL) {
return 0;
}

if ((ec = EVP_PKEY_get0(pk)) == NULL
|| (group = EC_KEY_get0_group(ec)) == NULL
|| (order = EC_GROUP_get0_order(group)) == NULL
|| (pub_key = EC_KEY_get0_public_key(ec)) == NULL) {
return 0;
}

half = BN_num_bytes(order);
if (pub == NULL) {
*len = 2 * half;
return 1;
}

if (*len < 2 * half) {
return 0;
}

if ((X = BN_new()) == NULL
|| (Y = BN_new()) == NULL) {
goto end;
}

if (EC_POINT_get_affine_coordinates(group, pub_key, X, Y, NULL) != 1) {
goto end;
}

if (BN_bn2lebinpad(X, pub, half) != half
|| BN_bn2lebinpad(Y, pub + half, half) != half) {
goto end;
}

ret = 1;
end:
BN_free(Y);
BN_free(X);
return ret;
}

/*
* Control function
*/
Expand Down Expand Up @@ -531,6 +660,26 @@ static int pkey_ctrl_gost(EVP_PKEY *pkey, int op, long arg1, void *arg2)
case ASN1_PKEY_CTRL_DEFAULT_MD_NID:
*(int *)arg2 = md_nid;
return 2;
case ASN1_PKEY_CTRL_GET1_TLS_ENCPT:
{
unsigned char **dup = (unsigned char **)arg2;
unsigned char *buf = NULL;
size_t len;

if (dup == NULL
|| gost_get_raw_pub_key(pkey, NULL, &len) != 1
|| (buf = OPENSSL_malloc(len)) == NULL
|| gost_get_raw_pub_key(pkey, buf, &len) != 1) {
if (buf)
OPENSSL_free(buf);
return 0;
}

*dup = buf;
return len;
}
case ASN1_PKEY_CTRL_SET1_TLS_ENCPT:
return gost_set_raw_pub_key(pkey, (const unsigned char *)arg2, arg1);
}

return -2;
Expand Down Expand Up @@ -1196,6 +1345,10 @@ int register_ameth_gost(int nid, EVP_PKEY_ASN1_METHOD **ameth,
pub_cmp_gost_ec, pub_print_gost_ec,
pkey_size_gost, pkey_bits_gost);

EVP_PKEY_asn1_set_set_pub_key(*ameth, gost_set_raw_pub_key);
EVP_PKEY_asn1_set_get_priv_key(*ameth, gost_get_raw_priv_key);
EVP_PKEY_asn1_set_get_pub_key(*ameth, gost_get_raw_pub_key);

EVP_PKEY_asn1_set_ctrl(*ameth, pkey_ctrl_gost);
EVP_PKEY_asn1_set_security_bits(*ameth, pkey_bits_gost);
break;
Expand Down
44 changes: 44 additions & 0 deletions test_sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,50 @@ static int test_sign(struct test_sign *t)
EVP_PKEY_CTX_free(ctx1);
EVP_PKEY_free(key1);

/* Extract public key in raw format.
* Should contain X||Y in little endian.
*/
size_t publen;
unsigned char *pub;
err = EVP_PKEY_get_raw_public_key(priv_key, NULL, &publen);
T(pub = OPENSSL_zalloc(publen));
err &= EVP_PKEY_get_raw_public_key(priv_key, pub, &publen);
printf("\tEVP_PKEY_get_raw_public_key:");
print_test_tf(err, err, "success", "failure");
ret |= err != 1;

unsigned char *pub2 = NULL;
int pub2len = EVP_PKEY_get1_encoded_public_key(priv_key, &pub2);
err = (publen == pub2len) && pub2 && !memcmp(pub, pub2, publen);
printf("\tEVP_PKEY_get1_encoded_public_key:\t");
print_test_tf(err, err, "success", "failure");
ret |= err != 1;
if (pub2)
OPENSSL_free(pub2);

EVP_PKEY *copy_key;
T(copy_key = EVP_PKEY_new());
T(EVP_PKEY_copy_parameters(copy_key, priv_key));
err = EVP_PKEY_set1_encoded_public_key(copy_key, pub, publen);
printf("\tEVP_PKEY_set1_encoded_public_key:\t");
print_test_tf(err, err, "success", "failure");
EVP_PKEY_free(copy_key);
OPENSSL_free(pub);
ret |= err != 1;

/* Extract private key in raw format.
* Should contain d on little endian form.
*/
size_t privlen;
unsigned char *priv;
err = EVP_PKEY_get_raw_private_key(priv_key, NULL, &privlen);
T(priv = OPENSSL_zalloc(privlen));
err &= EVP_PKEY_get_raw_private_key(priv_key, priv, &privlen);
printf("\tEVP_PKEY_get_raw_private_key:\t");
print_test_tf(err, err, "success", "failure");
OPENSSL_free(priv);
ret |= err != 1;

/*
* Prepare for sign testing.
*/
Expand Down

0 comments on commit 432a3d9

Please sign in to comment.