mirror of
https://abf.rosa.ru/djam/libressl.git
synced 2025-02-23 16:12:53 +00:00
823 lines
25 KiB
Diff
823 lines
25 KiB
Diff
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
|
|
|