> There is no English specification for GOST PKCS8 files yet, > unfortunately. You can find similar pieces of code in OpenSSL's GOST > engine (https://github.com/gost-engine/engine/blob/master/gost_ameth.c#L347) > and in GnuTLS > (https://gitlab.com/gnutls/gnutls/-/blob/master/lib/x509/privkey_pkcs8.c#L1159).
I checked GOST engine one and I saw the similar implementation was there. This is just a question and not request, though, don't you need "V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED" case for now, since GOST engine has it. I would like to suggest two return value checks, for BN_mod_mul and unmask_priv_key, details are below. --- src/lib/libcrypto/gost/gostr341001_ameth.c | 75 ++++++++++++++++++++-- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/src/lib/libcrypto/gost/gostr341001_ameth.c b/src/lib/libcrypto/gost/gostr341001_ameth.c index 0f816377dde1..70bd3357f184 100644 --- a/src/lib/libcrypto/gost/gostr341001_ameth.c +++ b/src/lib/libcrypto/gost/gostr341001_ameth.c @@ -437,6 +437,56 @@ priv_print_gost01(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx) return pub_print_gost01(out, pkey, indent, pctx); } +static BIGNUM *unmask_priv_key(EVP_PKEY *pk, + const unsigned char *buf, int len, int num_masks) +{ + BIGNUM *pknum_masked = NULL, *q = NULL; + const GOST_KEY *key_ptr = pk->pkey.gost; + const EC_GROUP *group = GOST_KEY_get0_group(key_ptr); + + pknum_masked = GOST_le2bn(buf, len, NULL); + if (!pknum_masked) { + GOSTerror(ERR_R_MALLOC_FAILURE); + return NULL; + } + + if (num_masks > 0) { + /* + * XXX Remove sign by gost94 + */ + const unsigned char *p = buf + num_masks * len; + + q = BN_new(); + if (!q) { + GOSTerror(ERR_R_MALLOC_FAILURE); + BN_free(pknum_masked); + pknum_masked = NULL; + goto end; + } + if (EC_GROUP_get_order(group, q, NULL) <= 0) { + GOSTerror(ERR_R_EC_LIB); + BN_free(pknum_masked); + pknum_masked = NULL; + goto end; + } + + for (; p != buf; p -= len) { + BIGNUM *mask = GOST_le2bn(p, len, NULL); + BN_CTX *ctx = BN_CTX_new(); + + BN_mod_mul(pknum_masked, pknum_masked, mask, q, ctx); BN_mod_mul might fail and return 0 on error. I would like to suggest checking this. + + BN_CTX_free(ctx); + BN_free(mask); + } + } + +end: + if (q) + BN_free(q); + return pknum_masked; +} + static int priv_decode_gost01(EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf) { @@ -450,6 +500,7 @@ priv_decode_gost01(EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf) GOST_KEY *ec; int ptype = V_ASN1_UNDEF; ASN1_STRING *pval = NULL; + int expected_key_len; if (PKCS8_pkey_get0(&palg_obj, &pkey_buf, &priv_len, &palg, p8inf) == 0) { GOSTerror(GOST_R_BAD_KEY_PARAMETERS_FORMAT); @@ -467,29 +518,43 @@ priv_decode_gost01(EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf) return 0; } p = pkey_buf; - if (V_ASN1_OCTET_STRING == *p) { + + expected_key_len = (pkey_bits_gost01(pk) + 7) / 8; + if (expected_key_len == 0) { + EVPerror(EVP_R_DECODE_ERROR); + return 0; + } else if (priv_len % expected_key_len == 0) { + /* Key is not wrapped but masked */ + pk_num = unmask_priv_key(pk, pkey_buf, expected_key_len, unmask_priv_key returns NULL on error. I would like to suggest checking this. + priv_len / expected_key_len - 1); + } else if (V_ASN1_OCTET_STRING == *p) { /* New format - Little endian octet string */ ASN1_OCTET_STRING *s = d2i_ASN1_OCTET_STRING(NULL, &p, priv_len); if (s == NULL) { - GOSTerror(EVP_R_DECODE_ERROR); + EVPerror(EVP_R_DECODE_ERROR); ASN1_STRING_free(s); return 0; } pk_num = GOST_le2bn(s->data, s->length, NULL); ASN1_STRING_free(s); - } else { + } else if (V_ASN1_INTEGER == *p) { priv_key = d2i_ASN1_INTEGER(NULL, &p, priv_len); - if (priv_key == NULL) + if (priv_key == NULL) { + EVPerror(EVP_R_DECODE_ERROR); return 0; + } ret = ((pk_num = ASN1_INTEGER_to_BN(priv_key, NULL)) != NULL); ASN1_INTEGER_free(priv_key); if (ret == 0) { - GOSTerror(EVP_R_DECODE_ERROR); + EVPerror(EVP_R_DECODE_ERROR); return 0; } + } else { + EVPerror(EVP_R_DECODE_ERROR); + return 0; } ec = pk->pkey.gost; -- 2.25.1