GOST private keys can be wrapped in OCTET STRING, INTEGER or come
unwrapped. Support the latter format.

Sponsored by ROSA Linux

Signed-off-by: Dmitry Baryshkov <dbarysh...@gmail.com>
---
 src/lib/libcrypto/gost/gost_asn1.c         |  52 ++++++++++
 src/lib/libcrypto/gost/gost_asn1.h         |  11 ++
 src/lib/libcrypto/gost/gostr341001_ameth.c | 115 +++++++++++++++++++--
 3 files changed, 169 insertions(+), 9 deletions(-)

diff --git a/src/lib/libcrypto/gost/gost_asn1.c 
b/src/lib/libcrypto/gost/gost_asn1.c
index 703d64070449..bfd81faa1ee2 100644
--- a/src/lib/libcrypto/gost/gost_asn1.c
+++ b/src/lib/libcrypto/gost/gost_asn1.c
@@ -17,6 +17,58 @@
 #include "gost_locl.h"
 #include "gost_asn1.h"
 
+static const ASN1_TEMPLATE MASKED_GOST_KEY_seq_tt[] = {
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(MASKED_GOST_KEY, masked_priv_key),
+               .field_name = "masked_priv_key",
+               .item = &ASN1_OCTET_STRING_it,
+       },
+       {
+               .flags = 0,
+               .tag = 0,
+               .offset = offsetof(MASKED_GOST_KEY, public_key),
+               .field_name = "public_key",
+               .item = &ASN1_OCTET_STRING_it,
+       },
+};
+
+const ASN1_ITEM MASKED_GOST_KEY_it = {
+       .itype = ASN1_ITYPE_NDEF_SEQUENCE,
+       .utype = V_ASN1_SEQUENCE,
+       .templates = MASKED_GOST_KEY_seq_tt,
+       .tcount = sizeof(MASKED_GOST_KEY_seq_tt) / sizeof(ASN1_TEMPLATE),
+       .funcs = NULL,
+       .size = sizeof(MASKED_GOST_KEY),
+       .sname = "MASKED_GOST_KEY",
+};
+
+MASKED_GOST_KEY *
+d2i_MASKED_GOST_KEY(MASKED_GOST_KEY **a, const unsigned char **in, long len)
+{
+       return (MASKED_GOST_KEY *)ASN1_item_d2i((ASN1_VALUE **)a, in, len,
+           &MASKED_GOST_KEY_it);
+}
+
+int
+i2d_MASKED_GOST_KEY(MASKED_GOST_KEY *a, unsigned char **out)
+{
+       return ASN1_item_i2d((ASN1_VALUE *)a, out, &MASKED_GOST_KEY_it);
+}
+
+MASKED_GOST_KEY *
+MASKED_GOST_KEY_new(void)
+{
+       return (MASKED_GOST_KEY *)ASN1_item_new(&MASKED_GOST_KEY_it);
+}
+
+void
+MASKED_GOST_KEY_free(MASKED_GOST_KEY *a)
+{
+       ASN1_item_free((ASN1_VALUE *)a, &MASKED_GOST_KEY_it);
+}
+
 static const ASN1_TEMPLATE GOST_KEY_TRANSPORT_seq_tt[] = {
        {
                .flags = 0,
diff --git a/src/lib/libcrypto/gost/gost_asn1.h 
b/src/lib/libcrypto/gost/gost_asn1.h
index 7cabfc79c965..cdbda7b98b67 100644
--- a/src/lib/libcrypto/gost/gost_asn1.h
+++ b/src/lib/libcrypto/gost/gost_asn1.h
@@ -56,6 +56,17 @@
 
 __BEGIN_HIDDEN_DECLS
 
+typedef struct {
+       ASN1_OCTET_STRING *masked_priv_key;
+       ASN1_OCTET_STRING *public_key;
+} MASKED_GOST_KEY;
+
+MASKED_GOST_KEY *MASKED_GOST_KEY_new(void);
+void MASKED_GOST_KEY_free(MASKED_GOST_KEY *a);
+MASKED_GOST_KEY *d2i_MASKED_GOST_KEY(MASKED_GOST_KEY **a, const unsigned char 
**in, long len);
+int i2d_MASKED_GOST_KEY(MASKED_GOST_KEY *a, unsigned char **out);
+extern const ASN1_ITEM MASKED_GOST_KEY_it;
+
 typedef struct {
        ASN1_OCTET_STRING *encrypted_key;
        ASN1_OCTET_STRING *imit;
diff --git a/src/lib/libcrypto/gost/gostr341001_ameth.c 
b/src/lib/libcrypto/gost/gostr341001_ameth.c
index 7cb70ed420ae..880c17ceaab8 100644
--- a/src/lib/libcrypto/gost/gostr341001_ameth.c
+++ b/src/lib/libcrypto/gost/gostr341001_ameth.c
@@ -437,6 +437,70 @@ 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, *mask;
+       const GOST_KEY *key_ptr = pk->pkey.gost;
+       const EC_GROUP *group = GOST_KEY_get0_group(key_ptr);
+       const unsigned char *p = buf + num_masks * len;
+       BN_CTX *ctx;
+
+       pknum_masked = GOST_le2bn(buf, len, NULL);
+       if (!pknum_masked) {
+               GOSTerror(ERR_R_MALLOC_FAILURE);
+               return NULL;
+       }
+
+       if (num_masks == 0)
+               return pknum_masked;
+
+       ctx = BN_CTX_new();
+       if (ctx == NULL) {
+               GOSTerror(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       BN_CTX_start(ctx);
+
+       q = BN_CTX_get(ctx);
+       if (!q) {
+               GOSTerror(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       mask = BN_CTX_get(ctx);
+       if (!mask) {
+               GOSTerror(ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (EC_GROUP_get_order(group, q, NULL) <= 0) {
+               GOSTerror(ERR_R_EC_LIB);
+               goto err;
+       }
+
+       for (; p != buf; p -= len) {
+               if (GOST_le2bn(p, len, mask) == NULL ||
+                   !BN_mod_mul(pknum_masked, pknum_masked, mask, q, ctx)) {
+                       GOSTerror(ERR_R_BN_LIB);
+                       goto err;
+               }
+       }
+
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+
+       return pknum_masked;
+
+err:
+       BN_CTX_end(ctx);
+       BN_CTX_free(ctx);
+
+       BN_free(pknum_masked);
+       return NULL;
+}
+
 static int
 priv_decode_gost01(EVP_PKEY *pk, const PKCS8_PRIV_KEY_INFO *p8inf)
 {
@@ -450,6 +514,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 +532,61 @@ 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,
+                               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 {
-               priv_key = d2i_ASN1_INTEGER(NULL, &p, priv_len);
-               if (priv_key == NULL)
+       } else if ((V_ASN1_SEQUENCE | V_ASN1_CONSTRUCTED) == *p) {
+               /* New format - Structure with masked private and separate 
public key */
+               MASKED_GOST_KEY *s =
+                   d2i_MASKED_GOST_KEY(NULL, &p, priv_len);
+
+               if (s == NULL ||
+                   !s->masked_priv_key ||
+                   s->masked_priv_key->length % expected_key_len != 0) {
+                       EVPerror(EVP_R_DECODE_ERROR);
+                       MASKED_GOST_KEY_free(s);
                        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);
+               }
+
+               pk_num = unmask_priv_key(pk, s->masked_priv_key->data,
+                                        expected_key_len,
+                                        s->masked_priv_key->length / 
expected_key_len - 1);
+               MASKED_GOST_KEY_free(s);
+       } else if (V_ASN1_INTEGER == *p) {
+               priv_key = d2i_ASN1_INTEGER(NULL, &p, priv_len);
+               if (priv_key == NULL) {
+                       EVPerror(EVP_R_DECODE_ERROR);
                        return 0;
                }
+               pk_num = ASN1_INTEGER_to_BN(priv_key, NULL);
+               ASN1_INTEGER_free(priv_key);
+       } else {
+               EVPerror(EVP_R_DECODE_ERROR);
+               return 0;
+       }
+
+       if (pk_num == NULL) {
+               EVPerror(EVP_R_DECODE_ERROR);
+               return 0;
        }
 
        ec = pk->pkey.gost;
-- 
2.27.0

Reply via email to