[prev in list] [next in list] [prev in thread] [next in thread] 

List:       openbsd-tech
Subject:    [PATCH 4/5] gostr341001: support unwrapped private keys support
From:       Dmitry Baryshkov <dbaryshkov () gmail ! com>
Date:       2020-06-27 17:14:00
Message-ID: 20200627171401.3917129-4-dbaryshkov () gmail ! com
[Download RAW message or body]

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 <dbaryshkov@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

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic