libressl/0042-gost-add-support-for-new-GOST-key-transport-data-for.patch

824 lines
25 KiB
Diff
Raw Permalink Normal View History

From b7dd5628589230bf30bcd88d4d21cce70adb464b Mon Sep 17 00:00:00 2001
From: Dmitry Baryshkov <dbaryshkov@gmail.com>
Date: Wed, 1 Apr 2020 17:04:15 +0300
Subject: [PATCH 42/87] gost: add support for new GOST key transport data
format
Add support for new GOST key transport data format used by CTR-OMAC
ciphersuites.
Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
---
src/lib/libcrypto/gost/gost.h | 5 +
src/lib/libcrypto/gost/gost_asn1.c | 59 ++++
src/lib/libcrypto/gost/gost_asn1.h | 12 +
src/lib/libcrypto/gost/gost_kdf.c | 174 +++++++++++
src/lib/libcrypto/gost/gost_locl.h | 17 ++
src/lib/libcrypto/gost/gostr341001.c | 10 +-
src/lib/libcrypto/gost/gostr341001_pmeth.c | 318 +++++++++++++++++++--
7 files changed, 565 insertions(+), 30 deletions(-)
create mode 100644 src/lib/libcrypto/gost/gost_kdf.c
diff --git a/src/lib/libcrypto/gost/gost.h b/src/lib/libcrypto/gost/gost.h
index e84a7625b..6a2b60670 100644
--- a/src/lib/libcrypto/gost/gost.h
+++ b/src/lib/libcrypto/gost/gost.h
@@ -228,10 +228,15 @@ size_t GOST_KEY_get_size(const GOST_KEY * r);
#define EVP_PKEY_CTRL_GOST_SIG_FORMAT (EVP_PKEY_ALG_CTRL+2)
#define EVP_PKEY_CTRL_GOST_SET_DIGEST (EVP_PKEY_ALG_CTRL+3)
#define EVP_PKEY_CTRL_GOST_GET_DIGEST (EVP_PKEY_ALG_CTRL+4)
+#define EVP_PKEY_CTRL_GOST_ENC_FORMAT (EVP_PKEY_ALG_CTRL+5)
#define GOST_SIG_FORMAT_SR_BE 0
#define GOST_SIG_FORMAT_RS_LE 1
+#define GOST_ENC_FORMAT_4490 0 /* RFC 4490, TLS CNT-IMIT */
+#define GOST_ENC_FORMAT_PSKEY_MAGMA 1 /* CMS, TLS CTR-OMAC, Magma-encoded */
+#define GOST_ENC_FORMAT_PSKEY_KUZNYECHIK 2 /* CMS, TLS CTR-OMAC, Kuznyechik-encoded */
+
/* BEGIN ERROR CODES */
/* The following lines are auto generated by the script mkerr.pl. Any changes
* made after this point may be overwritten when the script is next run.
diff --git a/src/lib/libcrypto/gost/gost_asn1.c b/src/lib/libcrypto/gost/gost_asn1.c
index 14e46afab..f56486d2a 100644
--- a/src/lib/libcrypto/gost/gost_asn1.c
+++ b/src/lib/libcrypto/gost/gost_asn1.c
@@ -474,4 +474,63 @@ gost3412_ctr_acpkm_get_asn1_params(EVP_CIPHER_CTX *ctx, ASN1_TYPE *params, unsig
return 1;
}
+static const ASN1_TEMPLATE GOST_KEY_TRANSPORT_PSKEY_seq_tt[] = {
+ {
+ .flags = 0,
+ .tag = 0,
+ .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, key_exp),
+ .field_name = "key_exp",
+ .item = &ASN1_OCTET_STRING_it,
+ },
+ {
+ .flags = 0,
+ .tag = 0,
+ .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, ephem_key),
+ .field_name = "ephem_key",
+ .item = &X509_PUBKEY_it,
+ },
+ {
+ .flags = ASN1_TFLG_OPTIONAL,
+ .tag = 0,
+ .offset = offsetof(GOST_KEY_TRANSPORT_PSKEY, ukm),
+ .field_name = "ukm",
+ .item = &ASN1_OCTET_STRING_it,
+ },
+};
+
+const ASN1_ITEM GOST_KEY_TRANSPORT_PSKEY_it = {
+ .itype = ASN1_ITYPE_NDEF_SEQUENCE,
+ .utype = V_ASN1_SEQUENCE,
+ .templates = GOST_KEY_TRANSPORT_PSKEY_seq_tt,
+ .tcount = sizeof(GOST_KEY_TRANSPORT_PSKEY_seq_tt) / sizeof(ASN1_TEMPLATE),
+ .funcs = NULL,
+ .size = sizeof(GOST_KEY_TRANSPORT_PSKEY),
+ .sname = "GOST_KEY_TRANSPORT_PSKEY",
+};
+
+GOST_KEY_TRANSPORT_PSKEY *
+d2i_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY **a, const unsigned char **in, long len)
+{
+ return (GOST_KEY_TRANSPORT_PSKEY *)ASN1_item_d2i((ASN1_VALUE **)a, in, len,
+ &GOST_KEY_TRANSPORT_PSKEY_it);
+}
+
+int
+i2d_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY *a, unsigned char **out)
+{
+ return ASN1_item_i2d((ASN1_VALUE *)a, out, &GOST_KEY_TRANSPORT_PSKEY_it);
+}
+
+GOST_KEY_TRANSPORT_PSKEY *
+GOST_KEY_TRANSPORT_PSKEY_new(void)
+{
+ return (GOST_KEY_TRANSPORT_PSKEY *)ASN1_item_new(&GOST_KEY_TRANSPORT_PSKEY_it);
+}
+
+void
+GOST_KEY_TRANSPORT_PSKEY_free(GOST_KEY_TRANSPORT_PSKEY *a)
+{
+ ASN1_item_free((ASN1_VALUE *)a, &GOST_KEY_TRANSPORT_PSKEY_it);
+}
+
#endif
diff --git a/src/lib/libcrypto/gost/gost_asn1.h b/src/lib/libcrypto/gost/gost_asn1.h
index 5af16e00e..1ae5e6706 100644
--- a/src/lib/libcrypto/gost/gost_asn1.h
+++ b/src/lib/libcrypto/gost/gost_asn1.h
@@ -123,6 +123,18 @@ GOST3412_ENCRYPTION_PARAMS *d2i_GOST3412_ENCRYPTION_PARAMS(GOST3412_ENCRYPTION_P
int i2d_GOST3412_ENCRYPTION_PARAMS(GOST3412_ENCRYPTION_PARAMS *a, unsigned char **out);
extern const ASN1_ITEM GOST3412_ENCRYPTION_PARAMS_it;
+typedef struct {
+ ASN1_OCTET_STRING *key_exp;
+ X509_PUBKEY *ephem_key;
+ ASN1_OCTET_STRING *ukm;
+} GOST_KEY_TRANSPORT_PSKEY;
+
+GOST_KEY_TRANSPORT_PSKEY *GOST_KEY_TRANSPORT_PSKEY_new(void);
+void GOST_KEY_TRANSPORT_PSKEY_free(GOST_KEY_TRANSPORT_PSKEY *a);
+GOST_KEY_TRANSPORT_PSKEY *d2i_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY **a, const unsigned char **in, long len);
+int i2d_GOST_KEY_TRANSPORT_PSKEY(GOST_KEY_TRANSPORT_PSKEY *a, unsigned char **out);
+extern const ASN1_ITEM GOST_KEY_TRANSPORT_PSKEY_it;
+
__END_HIDDEN_DECLS
#endif
diff --git a/src/lib/libcrypto/gost/gost_kdf.c b/src/lib/libcrypto/gost/gost_kdf.c
new file mode 100644
index 000000000..be497aea0
--- /dev/null
+++ b/src/lib/libcrypto/gost/gost_kdf.c
@@ -0,0 +1,174 @@
+/* $OpenBSD: gost_kdf.c,v 1.4 2017/01/29 17:49:23 beck Exp $ */
+/*
+ * Copyright (c) 2020 Dmitry Baryshkov <dbaryshkov@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <string.h>
+
+#include <openssl/opensslconf.h>
+
+#ifndef OPENSSL_NO_GOST
+#include <openssl/gost.h>
+#include <openssl/err.h>
+#include <openssl/kdftree.h>
+#include <openssl/hmac.h>
+#include <openssl/cmac.h>
+
+#include "gost_locl.h"
+
+int
+gost_kexp15(const EVP_CIPHER *ctr_cipher, const EVP_CIPHER *cmac_cipher,
+ const unsigned char *key, unsigned int key_length,
+ const unsigned char *key_mac,
+ const unsigned char *key_enc,
+ const unsigned char *iv,
+ unsigned char *out, size_t *out_length)
+{
+
+ CMAC_CTX *cmac_ctx = CMAC_CTX_new();
+ EVP_CIPHER_CTX ctx;
+ unsigned char cmac[EVP_MAX_BLOCK_LENGTH];
+ size_t cmac_length = sizeof(cmac);
+ unsigned int len = *out_length;
+ unsigned int tmp;
+
+ if (CMAC_Init(cmac_ctx, key_mac, EVP_CIPHER_key_length(cmac_cipher), cmac_cipher, NULL) <= 0 ||
+ CMAC_Update(cmac_ctx, iv, ctr_cipher->iv_len) <= 0 ||
+ CMAC_Update(cmac_ctx, key, key_length) <= 0 ||
+ CMAC_Final(cmac_ctx, cmac, &cmac_length) <= 0) {
+ CMAC_CTX_free(cmac_ctx);
+ return 0;
+ }
+
+ CMAC_CTX_free(cmac_ctx);
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_EncryptInit_ex(&ctx, ctr_cipher, NULL, key_enc, iv) ||
+ !EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+ !EVP_EncryptUpdate(&ctx, out, &len, key, key_length)) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+
+ tmp = *out_length - len;
+ if (!EVP_EncryptUpdate(&ctx, out + len, &tmp, cmac, cmac_length)) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+
+ len += tmp;
+ tmp = *out_length - len;
+ if (!EVP_EncryptFinal_ex(&ctx, out + len, &tmp)) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ return 0;
+ }
+ EVP_CIPHER_CTX_cleanup(&ctx);
+
+ len += tmp;
+ *out_length = len;
+
+ return 1;
+}
+
+int
+gost_kimp15(const EVP_CIPHER *ctr_cipher, const EVP_CIPHER *cmac_cipher,
+ const unsigned char *sexp, unsigned int sexp_length,
+ const unsigned char *key_mac,
+ const unsigned char *key_enc,
+ const unsigned char *iv,
+ unsigned char *out, size_t *out_length)
+{
+ CMAC_CTX *cmac_ctx = CMAC_CTX_new();
+ EVP_CIPHER_CTX ctx;
+ unsigned char cmac[EVP_MAX_BLOCK_LENGTH];
+ size_t cmac_length;
+ unsigned char tmp[EVP_MAX_KEY_LENGTH + EVP_MAX_BLOCK_LENGTH];
+ unsigned int len = sizeof(tmp);
+ unsigned int len2;
+
+ cmac_length = EVP_CIPHER_block_size(cmac_cipher);
+ if (*out_length > EVP_MAX_KEY_LENGTH || sexp_length < cmac_length || sexp_length != 32 + cmac_length) {
+ EVPerror(EVP_R_BAD_BLOCK_LENGTH);
+ return 0;
+ }
+
+ EVP_CIPHER_CTX_init(&ctx);
+ if (!EVP_DecryptInit_ex(&ctx, ctr_cipher, NULL, key_enc, iv) ||
+ !EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+ !EVP_DecryptUpdate(&ctx, tmp, &len, sexp, sexp_length)) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ return 0;
+ }
+
+ len2 = sizeof(tmp) - len;
+ if (!EVP_DecryptFinal_ex(&ctx, tmp + len, &len2)) {
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ return 0;
+ }
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ len += len2;
+
+ len2 = sexp_length - cmac_length;
+ if (CMAC_Init(cmac_ctx, key_mac, EVP_CIPHER_key_length(cmac_cipher), cmac_cipher, NULL) <= 0 ||
+ CMAC_Update(cmac_ctx, iv, ctr_cipher->iv_len) <= 0 ||
+ CMAC_Update(cmac_ctx, tmp, sexp_length - cmac_length) <= 0 ||
+ CMAC_Final(cmac_ctx, cmac, &cmac_length) <= 0) {
+ CMAC_CTX_free(cmac_ctx);
+ GOSTerror(GOST_R_SIGNATURE_MISMATCH);
+ return 0;
+ }
+
+ CMAC_CTX_free(cmac_ctx);
+
+ if (timingsafe_memcmp(cmac, tmp + len2, cmac_length)) {
+ GOSTerror(GOST_R_SIGNATURE_MISMATCH);
+ return 0;
+ }
+
+ memcpy(out, tmp, len2);
+
+ return 1;
+}
+
+int
+gost_keg(EVP_PKEY *pub, EVP_PKEY *priv, int nid,
+ const unsigned char *ukm, unsigned char *keg_out)
+{
+ if (nid == NID_id_tc26_gost3411_2012_512) {
+ if (gost01_VKO_key(pub, priv, ukm, 16, 1, NID_id_tc26_gost3411_2012_512, keg_out) == 0) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ return 0;
+ }
+
+ return 1;
+ } else {
+ unsigned char tmp[32];
+
+ if (gost01_VKO_key(pub, priv, ukm, 16, 1, NID_id_tc26_gost3411_2012_256, tmp) == 0) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ return 0;
+ }
+
+ return KDF_TREE(EVP_streebog256(), NULL,
+ tmp, 32,
+ "kdf tree", 8,
+ ukm + 16, 8,
+ 1,
+ keg_out, 64);
+ }
+}
+#endif
diff --git a/src/lib/libcrypto/gost/gost_locl.h b/src/lib/libcrypto/gost/gost_locl.h
index f512029e1..13e9fd459 100644
--- a/src/lib/libcrypto/gost/gost_locl.h
+++ b/src/lib/libcrypto/gost/gost_locl.h
@@ -121,6 +121,8 @@ extern int VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey,
GOST_KEY *priv_key, const BIGNUM *ukm);
extern BIGNUM *GOST_le2bn(const unsigned char *buf, size_t len, BIGNUM *bn);
extern int GOST_bn2le(BIGNUM *bn, unsigned char *buf, int len);
+extern int gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm,
+ unsigned int ukm_len, int ukm_be, int out_nid, unsigned char *key);
/* GOST R 34.10 parameters */
extern int GostR3410_get_md_digest(int nid);
@@ -128,6 +130,21 @@ extern int GostR3410_get_pk_digest(int nid);
extern int GostR3410_256_param_id(const char *value);
extern int GostR3410_512_param_id(const char *value);
+int gost_keg(EVP_PKEY *pub, EVP_PKEY *priv, int nid,
+ const unsigned char *ukm, unsigned char *keg_out);
+int gost_kexp15(const EVP_CIPHER *cmac_cipher, const EVP_CIPHER *ctr_cipher,
+ const unsigned char *key, unsigned int key_length,
+ const unsigned char *key_mac,
+ const unsigned char *key_enc,
+ const unsigned char *iv,
+ unsigned char *out, size_t *out_length);
+int gost_kimp15(const EVP_CIPHER *cmac_cipher, const EVP_CIPHER *ctr_cipher,
+ const unsigned char *sexp, unsigned int sexp_length,
+ const unsigned char *key_mac,
+ const unsigned char *key_enc,
+ const unsigned char *iv,
+ unsigned char *out, size_t *out_length);
+
__END_HIDDEN_DECLS
#endif
diff --git a/src/lib/libcrypto/gost/gostr341001.c b/src/lib/libcrypto/gost/gostr341001.c
index ba70d5f1f..5ac36d25f 100644
--- a/src/lib/libcrypto/gost/gostr341001.c
+++ b/src/lib/libcrypto/gost/gostr341001.c
@@ -329,7 +329,7 @@ int
VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey, GOST_KEY *priv_key,
const BIGNUM *ukm)
{
- BIGNUM *p = NULL, *order = NULL;
+ BIGNUM *p = NULL, *order = NULL, *cofactor = NULL;
const BIGNUM *key = GOST_KEY_get0_private_key(priv_key);
const EC_GROUP *group = GOST_KEY_get0_group(priv_key);
const EC_POINT *pub_key = GOST_KEY_get0_public_key(pkey);
@@ -350,7 +350,13 @@ VKO_compute_key(BIGNUM *X, BIGNUM *Y, const GOST_KEY *pkey, GOST_KEY *priv_key,
goto err;
if (EC_GROUP_get_order(group, order, ctx) == 0)
goto err;
- if (BN_mod_mul(p, key, ukm, order, ctx) == 0)
+ if ((cofactor = BN_CTX_get(ctx)) == NULL)
+ goto err;
+ if (EC_GROUP_get_cofactor(group, cofactor, ctx) == 0)
+ goto err;
+ if (BN_mod_mul(p, key, cofactor, order, ctx) == 0)
+ goto err;
+ if (!BN_is_zero(ukm) && BN_mod_mul(p, p, ukm, order, ctx) == 0)
goto err;
if (EC_POINT_mul(group, pnt, NULL, pub_key, p, ctx) == 0)
goto err;
diff --git a/src/lib/libcrypto/gost/gostr341001_pmeth.c b/src/lib/libcrypto/gost/gostr341001_pmeth.c
index 0e0cae99e..455337232 100644
--- a/src/lib/libcrypto/gost/gostr341001_pmeth.c
+++ b/src/lib/libcrypto/gost/gostr341001_pmeth.c
@@ -130,8 +130,10 @@ struct gost_pmeth_data {
int digest_nid;
EVP_MD *md;
unsigned char *shared_ukm;
+ unsigned int shared_ukm_len;
int peer_key_used;
int sig_format;
+ int enc_format;
};
static int
@@ -165,8 +167,8 @@ pkey_gost01_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src)
src_data = EVP_PKEY_CTX_get_data(src);
dst_data = EVP_PKEY_CTX_get_data(dst);
*dst_data = *src_data;
- if (src_data->shared_ukm != NULL)
- dst_data->shared_ukm = NULL;
+ dst_data->shared_ukm = NULL;
+ dst_data->shared_ukm_len = 0;
return 1;
}
@@ -310,11 +312,12 @@ err:
return ok;
}
-static int
+int
gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm,
- unsigned char *key)
+ unsigned int ukm_len, int ukm_be, int out_nid, unsigned char *key)
{
unsigned char hashbuf[128];
+ size_t hash_len;
int digest_nid;
int ret = 0;
BN_CTX *ctx = BN_CTX_new();
@@ -331,7 +334,10 @@ gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm,
if ((Y = BN_CTX_get(ctx)) == NULL)
goto err;
- GOST_le2bn(ukm, 8, UKM);
+ if (ukm_be)
+ BN_bin2bn (ukm, ukm_len, UKM);
+ else
+ GOST_le2bn(ukm, ukm_len, UKM);
digest_nid = GOST_KEY_get_digest(priv_key->pkey.gost);
if (VKO_compute_key(X, Y, pub_key->pkey.gost, priv_key->pkey.gost,
@@ -340,27 +346,37 @@ gost01_VKO_key(EVP_PKEY *pub_key, EVP_PKEY *priv_key, const unsigned char *ukm,
switch (digest_nid) {
case NID_id_GostR3411_94_CryptoProParamSet:
- GOST_bn2le(X, hashbuf, 32);
- GOST_bn2le(Y, hashbuf + 32, 32);
- GOSTR341194(hashbuf, 64, key, digest_nid);
- ret = 1;
- break;
case NID_id_tc26_gost3411_2012_256:
GOST_bn2le(X, hashbuf, 32);
GOST_bn2le(Y, hashbuf + 32, 32);
- STREEBOG256(hashbuf, 64, key);
- ret = 1;
+ hash_len = 64;
break;
case NID_id_tc26_gost3411_2012_512:
GOST_bn2le(X, hashbuf, 64);
GOST_bn2le(Y, hashbuf + 64, 64);
- STREEBOG256(hashbuf, 128, key);
- ret = 1;
+ hash_len = 128;
break;
default:
ret = -2;
+ goto err;
+ }
+
+ switch (out_nid) {
+ case NID_id_GostR3411_94_CryptoProParamSet:
+ GOSTR341194(hashbuf, hash_len, key, out_nid);
+ break;
+ case NID_id_tc26_gost3411_2012_256:
+ STREEBOG256(hashbuf, hash_len, key);
+ break;
+ case NID_id_tc26_gost3411_2012_512:
+ STREEBOG512(hashbuf, hash_len, key);
break;
+ default:
+ ret = -2;
+ goto err;
}
+
+ ret = 1;
err:
BN_CTX_end(ctx);
BN_CTX_free(ctx);
@@ -368,7 +384,7 @@ err:
}
int
-pkey_gost01_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len,
+pkey_gost01_decrypt_4490(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len,
const unsigned char *in, size_t in_len)
{
const unsigned char *p = in;
@@ -428,8 +444,15 @@ pkey_gost01_decrypt(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len,
goto err;
}
memcpy(wrappedKey + 40, gkt->key_info->imit->data, 4);
- if (gost01_VKO_key(peerkey, priv, wrappedKey, sharedKey) <= 0)
+ if (gost01_VKO_key(peerkey, priv, wrappedKey, 8, 0,
+ GOST_KEY_get_digest(priv->pkey.gost) ==
+ NID_id_GostR3411_94_CryptoProParamSet ?
+ NID_id_GostR3411_94_CryptoProParamSet :
+ NID_id_tc26_gost3411_2012_256,
+ sharedKey) <= 0) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
goto err;
+ }
if (gost_key_unwrap_crypto_pro(nid, sharedKey, wrappedKey, key) == 0) {
GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
goto err;
@@ -464,15 +487,22 @@ pkey_gost01_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen)
return 32;
}
- if (gost01_VKO_key(peer_key, my_key, data->shared_ukm, key) <= 0)
+ if (gost01_VKO_key(peer_key, my_key, data->shared_ukm, 8, 0,
+ GOST_KEY_get_digest(my_key->pkey.gost) ==
+ NID_id_GostR3411_94_CryptoProParamSet ?
+ NID_id_GostR3411_94_CryptoProParamSet :
+ NID_id_tc26_gost3411_2012_256,
+ key) <= 0) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
return 0;
+ }
*keylen = 32;
return 1;
}
int
-pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len,
+pkey_gost01_encrypt_4490(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len,
const unsigned char *key, size_t key_len)
{
GOST_KEY_TRANSPORT *gkt = NULL;
@@ -484,7 +514,7 @@ pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len,
EVP_PKEY *sec_key = EVP_PKEY_CTX_get0_peerkey(pctx);
int nid = NID_id_Gost28147_89_CryptoPro_A_ParamSet;
- if (data->shared_ukm != NULL) {
+ if (data->shared_ukm != NULL && data->shared_ukm_len >= 8) {
memcpy(ukm, data->shared_ukm, 8);
} else /* if (out != NULL) */ {
arc4random_buf(ukm, 8);
@@ -521,22 +551,29 @@ pkey_gost01_encrypt(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len,
}
if (out != NULL) {
- if (gost01_VKO_key(pubk, sec_key, ukm, shared_key) <= 0)
+ if (gost01_VKO_key(pubk, sec_key, ukm, 8, 0,
+ GOST_KEY_get_digest(pubk->pkey.gost) ==
+ NID_id_GostR3411_94_CryptoProParamSet ?
+ NID_id_GostR3411_94_CryptoProParamSet :
+ NID_id_tc26_gost3411_2012_256,
+ shared_key) <= 0) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
goto err;
+ }
gost_key_wrap_crypto_pro(nid, shared_key, ukm, key,
crypted_key);
}
gkt = GOST_KEY_TRANSPORT_new();
- if (gkt == NULL)
- goto err;
- if (ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8) == 0)
- goto err;
- if (ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40,
- 4) == 0)
+ if (gkt == NULL) {
+ GOSTerror(ERR_R_MALLOC_FAILURE);
goto err;
- if (ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key, crypted_key + 8,
- 32) == 0)
+ }
+ if (!ASN1_OCTET_STRING_set(gkt->key_agreement_info->eph_iv, ukm, 8) ||
+ !ASN1_OCTET_STRING_set(gkt->key_info->imit, crypted_key + 40, 4) ||
+ !ASN1_OCTET_STRING_set(gkt->key_info->encrypted_key, crypted_key + 8, 32)) {
+ GOSTerror(ERR_R_ASN1_LIB);
goto err;
+ }
if (key_is_ephemeral) {
if (X509_PUBKEY_set(&gkt->key_agreement_info->ephem_key,
out != NULL ? sec_key : pubk) == 0) {
@@ -568,6 +605,213 @@ err:
return -1;
}
+int
+pkey_gost01_decrypt_pskey(EVP_PKEY_CTX *pctx, unsigned char *key, size_t *key_len,
+ const unsigned char *in, size_t in_len,
+ const EVP_CIPHER *enc_cipher, const EVP_CIPHER *mac_cipher)
+{
+ const unsigned char *p = in;
+ EVP_PKEY *priv = EVP_PKEY_CTX_get0_pkey(pctx);
+ GOST_KEY_TRANSPORT_PSKEY *gkt = NULL;
+ struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
+ EVP_PKEY *eph_key = NULL, *peerkey = NULL;
+ unsigned char keg_out[64];
+ const unsigned char *ukm;
+
+ if (key == NULL) {
+ *key_len = 32;
+ return 1;
+ }
+
+ gkt = d2i_GOST_KEY_TRANSPORT_PSKEY(NULL, (const unsigned char **)&p, in_len);
+ if (gkt == NULL) {
+ GOSTerror(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
+ return -1;
+ }
+
+ if (data->shared_ukm_len == 0) {
+ if (!gkt->ukm || gkt->ukm->length != 32) {
+ GOSTerror(ERR_R_ASN1_LIB);
+ goto err;
+ }
+ ukm = gkt->ukm->data;
+ } else if (data->shared_ukm_len != 32) {
+ GOSTerror(GOST_R_INVALID_IV_LENGTH);
+ goto err;
+ } else {
+ ukm = data->shared_ukm;
+ }
+
+ eph_key = X509_PUBKEY_get(gkt->ephem_key);
+ if (eph_key == NULL) {
+ GOSTerror(GOST_R_ERROR_PARSING_KEY_TRANSPORT_INFO);
+ goto err;
+ }
+
+ if (EVP_PKEY_derive_set_peer(pctx, eph_key) <= 0) {
+ GOSTerror(GOST_R_INCOMPATIBLE_PEER_KEY);
+ goto err;
+ }
+
+ peerkey = EVP_PKEY_CTX_get0_peerkey(pctx);
+ if (peerkey == NULL) {
+ GOSTerror(GOST_R_NO_PEER_KEY);
+ goto err;
+ }
+
+ /* KEG */
+ if (!gost_keg(peerkey, priv, data->digest_nid, ukm, keg_out)) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ goto err;
+ }
+
+ if (!gost_kimp15(enc_cipher, mac_cipher,
+ gkt->key_exp->data, gkt->key_exp->length,
+ keg_out, keg_out + 32,
+ ukm + 24,
+ key, key_len)) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ goto err;
+ }
+
+ GOST_KEY_TRANSPORT_PSKEY_free(gkt);
+ return 1;
+
+err:
+ GOST_KEY_TRANSPORT_PSKEY_free(gkt);
+ return 0;
+}
+
+int
+pkey_gost01_encrypt_pskey(EVP_PKEY_CTX *pctx, unsigned char *out, size_t *out_len,
+ const unsigned char *key, size_t key_len,
+ const EVP_CIPHER *enc_cipher, const EVP_CIPHER *mac_cipher)
+{
+ GOST_KEY_TRANSPORT_PSKEY *gkt = NULL;
+ struct gost_pmeth_data *data = EVP_PKEY_CTX_get_data(pctx);
+ EVP_PKEY *pubk = EVP_PKEY_CTX_get0_pkey(pctx);
+ EVP_PKEY *sec_key = NULL;
+ GOST_KEY *tmp_key;
+ unsigned char ukm[32];
+ unsigned char keg_out[64];
+ unsigned char kexp_out[64];
+ size_t kexp_len = sizeof(kexp_out);
+ int tmp_len;
+
+ if (data->shared_ukm == NULL) {
+ arc4random_buf(ukm, sizeof(ukm));
+ } else if (data->shared_ukm_len != sizeof(ukm)) {
+ GOSTerror(GOST_R_INVALID_IV_LENGTH);
+ return 0;
+ } else {
+ memcpy(ukm, data->shared_ukm, sizeof(ukm));
+ }
+
+ if (out) {
+ sec_key = EVP_PKEY_new();
+ if (sec_key == NULL) {
+ GOSTerror(ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ tmp_key = GOST_KEY_new();
+ if (tmp_key == NULL) {
+ GOSTerror(ERR_R_MALLOC_FAILURE);
+ goto err;
+ }
+
+ if (EVP_PKEY_assign(sec_key, EVP_PKEY_base_id(pubk), tmp_key) == 0) {
+ GOST_KEY_free(tmp_key);
+ goto err;
+ }
+ if (EVP_PKEY_copy_parameters(sec_key, pubk) == 0)
+ goto err;
+ if (gost2001_keygen(sec_key->pkey.gost) == 0)
+ goto err;
+
+ /* KEG */
+ if (!gost_keg(pubk, sec_key, data->digest_nid, ukm, keg_out)) {
+ GOSTerror(GOST_R_ERROR_COMPUTING_SHARED_KEY);
+ goto err;
+ }
+
+ if (!gost_kexp15(enc_cipher, mac_cipher,
+ key, key_len,
+ keg_out, keg_out + 32,
+ ukm + 24,
+ kexp_out, &kexp_len))
+ goto err;
+ } else {
+ kexp_len = key_len + EVP_CIPHER_block_size(mac_cipher);
+ }
+
+ gkt = GOST_KEY_TRANSPORT_PSKEY_new();
+ if (gkt == NULL)
+ goto err;
+
+ if (!ASN1_OCTET_STRING_set(gkt->key_exp, kexp_out, kexp_len) ||
+ !X509_PUBKEY_set(&gkt->ephem_key, out ? sec_key : pubk))
+ goto err;
+
+ if (data->shared_ukm == NULL &&
+ (((gkt->ukm = ASN1_OCTET_STRING_new()) == NULL) ||
+ !ASN1_OCTET_STRING_set(gkt->ukm, ukm, sizeof(ukm))))
+ goto err;
+
+ tmp_len = i2d_GOST_KEY_TRANSPORT_PSKEY(gkt, NULL);
+ if (!out) {
+ *out_len = tmp_len;
+ } else {
+ if (*out_len < tmp_len)
+ goto err;
+ *out_len = i2d_GOST_KEY_TRANSPORT_PSKEY(gkt, &out);
+ }
+
+ EVP_PKEY_free(sec_key);
+ GOST_KEY_TRANSPORT_PSKEY_free(gkt);
+
+ return 1;
+err:
+ EVP_PKEY_free(sec_key);
+ GOST_KEY_TRANSPORT_PSKEY_free(gkt);
+ return 0;
+}
+
+int
+pkey_gost01_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *out_len,
+ const unsigned char *key, size_t key_len)
+{
+ struct gost_pmeth_data *pctx = EVP_PKEY_CTX_get_data(ctx);
+
+ switch (pctx->enc_format) {
+ case GOST_ENC_FORMAT_4490:
+ return pkey_gost01_decrypt_4490(ctx, out, out_len, key, key_len);
+ case GOST_ENC_FORMAT_PSKEY_MAGMA:
+ return pkey_gost01_decrypt_pskey(ctx, out, out_len, key, key_len, EVP_magma_ctr(), EVP_magma_cbc());
+ case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK:
+ return pkey_gost01_decrypt_pskey(ctx, out, out_len, key, key_len, EVP_kuznyechik_ctr(), EVP_kuznyechik_cbc());
+ default:
+ return -1;
+ }
+}
+
+int
+pkey_gost01_encrypt(EVP_PKEY_CTX *ctx, unsigned char *out, size_t *out_len,
+ const unsigned char *key, size_t key_len)
+{
+ struct gost_pmeth_data *pctx = EVP_PKEY_CTX_get_data(ctx);
+
+ switch (pctx->enc_format) {
+ case GOST_ENC_FORMAT_4490:
+ return pkey_gost01_encrypt_4490(ctx, out, out_len, key, key_len);
+ case GOST_ENC_FORMAT_PSKEY_MAGMA:
+ return pkey_gost01_encrypt_pskey(ctx, out, out_len, key, key_len, EVP_magma_ctr(), EVP_magma_cbc());
+ case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK:
+ return pkey_gost01_encrypt_pskey(ctx, out, out_len, key, key_len, EVP_kuznyechik_ctr(), EVP_kuznyechik_cbc());
+ default:
+ return -1;
+ }
+}
static int
pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
@@ -604,6 +848,7 @@ pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
memcpy(ukm, p2, p1);
free(pctx->shared_ukm);
pctx->shared_ukm = ukm;
+ pctx->shared_ukm_len = p1;
return 1;
}
@@ -631,6 +876,23 @@ pkey_gost01_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2)
case EVP_PKEY_CTRL_GOST_GET_DIGEST:
*(int *)p2 = pctx->digest_nid;
return 1;
+ case EVP_PKEY_CTRL_GOST_ENC_FORMAT:
+ switch (p1) {
+ case GOST_ENC_FORMAT_4490:
+ /* All keys are supported */
+ break;
+ case GOST_ENC_FORMAT_PSKEY_MAGMA:
+ case GOST_ENC_FORMAT_PSKEY_KUZNYECHIK:
+ if (pctx->digest_nid != NID_id_tc26_gost3411_2012_256 &&
+ pctx->digest_nid != NID_id_tc26_gost3411_2012_512)
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ pctx->enc_format = p1;
+ return 1;
+ break;
default:
return -2;
}
--
2.17.1