[PATCH] gostr341001: support unwrapped private keys support

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] gostr341001: support unwrapped private keys support

dbaryshkov
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 <[hidden email]>
---
 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_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,
+ 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

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] gostr341001: support unwrapped private keys support

kinichiro inoguchi
Hi,

Where can we see the specifcation for these 3 different format, wrapped in
OCTET STRING, INTEGER and unwrapped but masked ?
I tried to find but couldn't.
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] gostr341001: support unwrapped private keys support

dbaryshkov
Hello,

вт, 31 мар. 2020 г. в 06:20, Kinichiro Inoguchi <[hidden email]>:
>
> Hi,
>
> Where can we see the specifcation for these 3 different format, wrapped in OCTET STRING, INTEGER and unwrapped but masked ?
> I tried to find but couldn't.

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).

--
With best wishes
Dmitry

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] gostr341001: support unwrapped private keys support

kinichiro inoguchi
> 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