mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-15 04:53:00 +08:00
206 lines
7.4 KiB
Diff
206 lines
7.4 KiB
Diff
From c2b4ae50479d12a2f72914595a1cb3d24bd6e831 Mon Sep 17 00:00:00 2001
|
|
From: Kirill Ermoshin <ermoshin@rutoken.ru>
|
|
Date: Tue, 10 Nov 2026 16:09:05 +0300
|
|
Subject: [PATCH 4/4] Add OSSL_CIPHER_PARAM_TLS_MAC handler for ciphers
|
|
|
|
We need cut MAC during decryption for TLS 1.2
|
|
---
|
|
gost_prov_cipher.c | 54 +++++++++++++++++++++++++++++--
|
|
test_ciphers.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
2 files changed, 132 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/gost_prov_cipher.c b/gost_prov_cipher.c
|
|
index 7a0a02d..0a7472c 100644
|
|
--- a/gost_prov_cipher.c
|
|
+++ b/gost_prov_cipher.c
|
|
@@ -118,6 +118,13 @@ struct gost_prov_crypt_ctx_st {
|
|
GOST_cipher *cipher;
|
|
/* The context for the GOST_cipher functions */
|
|
GOST_cipher_ctx *cctx;
|
|
+ /*
|
|
+ * TLS MAC extracted from the tail of decrypted records, it is
|
|
+ * requested by libssl through OSSL_CIPHER_PARAM_TLS_MAC (octet_ptr).
|
|
+ * The size is set by libssl via OSSL_CIPHER_PARAM_TLS_MAC_SIZE.
|
|
+ */
|
|
+ unsigned char *tls_mac;
|
|
+ size_t tls_mac_size;
|
|
};
|
|
typedef struct gost_prov_crypt_ctx_st GOST_CTX;
|
|
|
|
@@ -147,6 +154,7 @@ static void cipher_freectx(void *vgctx)
|
|
if (gctx == NULL)
|
|
return;
|
|
GOST_cipher_ctx_free(gctx->cctx);
|
|
+ OPENSSL_clear_free(gctx->tls_mac, gctx->tls_mac_size);
|
|
OPENSSL_free(gctx);
|
|
}
|
|
|
|
@@ -314,6 +322,14 @@ static int cipher_get_ctx_params(void *vgctx, OSSL_PARAM params[])
|
|
if (!OSSL_PARAM_set_int(p, nid))
|
|
return 0;
|
|
}
|
|
+ if ((p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC)) != NULL) {
|
|
+ /*
|
|
+ * libssl reads the MAC extracted from the decrypted record tail
|
|
+ * (see cipher_update) through the octet_ptr parameter.
|
|
+ */
|
|
+ if (!OSSL_PARAM_set_octet_ptr(p, gctx->tls_mac, gctx->tls_mac_size))
|
|
+ return 0;
|
|
+ }
|
|
return 1;
|
|
}
|
|
|
|
@@ -383,6 +399,22 @@ static int cipher_set_ctx_params(void *vgctx, const OSSL_PARAM params[])
|
|
(int)arg, (void *)val) <= 0)
|
|
return 0;
|
|
}
|
|
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE)) != NULL) {
|
|
+ size_t mac_size = 0;
|
|
+ if (!OSSL_PARAM_get_size_t(p, &mac_size))
|
|
+ return 0;
|
|
+ if (mac_size > EVP_MAX_MD_SIZE)
|
|
+ return 0;
|
|
+ OPENSSL_clear_free(gctx->tls_mac, gctx->tls_mac_size);
|
|
+ gctx->tls_mac = NULL;
|
|
+ gctx->tls_mac_size = 0;
|
|
+ if (mac_size != 0) {
|
|
+ gctx->tls_mac = OPENSSL_malloc(mac_size);
|
|
+ if (gctx->tls_mac == NULL)
|
|
+ return 0;
|
|
+ gctx->tls_mac_size = mac_size;
|
|
+ }
|
|
+ }
|
|
return 1;
|
|
}
|
|
|
|
@@ -422,8 +454,26 @@ static int cipher_update(void *vgctx,
|
|
int int_outl = outl != NULL ? *outl : 0;
|
|
int res = GOST_CipherUpdate(gctx->cctx, out, &int_outl, in, (int)inl);
|
|
|
|
- if (res > 0 && outl != NULL)
|
|
- *outl = (size_t)int_outl;
|
|
+ if (res > 0) {
|
|
+ /*
|
|
+ * In TLS the MAC is encrypted together with the data, libssl
|
|
+ * expects the provider to cut the MAC off the tail of the
|
|
+ * decrypted record and to hand it over later through the
|
|
+ * OSSL_CIPHER_PARAM_TLS_MAC (octet_ptr) parameter. This works
|
|
+ * only for stream ciphers which don't buffer data internally,
|
|
+ * i.e. all GOST TLS 1.2 suites.
|
|
+ */
|
|
+ if (!GOST_cipher_ctx_encrypting(gctx->cctx)
|
|
+ && gctx->tls_mac_size > 0) {
|
|
+ if (out == NULL || int_outl < 0
|
|
+ || (size_t)int_outl < gctx->tls_mac_size)
|
|
+ return 0;
|
|
+ int_outl -= (int)gctx->tls_mac_size;
|
|
+ memcpy(gctx->tls_mac, out + int_outl, gctx->tls_mac_size);
|
|
+ }
|
|
+ if (outl != NULL)
|
|
+ *outl = (size_t)int_outl;
|
|
+ }
|
|
return res > 0;
|
|
}
|
|
|
|
diff --git a/test_ciphers.c b/test_ciphers.c
|
|
index 7508a82..ed8e382 100644
|
|
--- a/test_ciphers.c
|
|
+++ b/test_ciphers.c
|
|
@@ -858,6 +858,82 @@ static int test_provider_padded_decrypt(const EVP_CIPHER *type, const char *name
|
|
return ret;
|
|
}
|
|
|
|
+/* Size of the MAC (imit) for the GOST TLS 1.2 stream cipher suites */
|
|
+static size_t tls_mac_size_for_cipher(const char *name)
|
|
+{
|
|
+ if (strcmp(name, SN_grasshopper_ctr) == 0
|
|
+ || strcmp(name, SN_id_tc26_cipher_gostr3412_2015_kuznyechik_ctracpkm) == 0)
|
|
+ return 16;
|
|
+ return 8;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Emulates the libssl record protection for Mac-then-encrypt stream
|
|
+ * ciphers: the MAC is encrypted as a tail of the record, on decryption
|
|
+ * the provider must cut it off the output and hand it over through the
|
|
+ * OSSL_CIPHER_PARAM_TLS_MAC (octet_ptr) parameter.
|
|
+ */
|
|
+static int test_provider_tls_mac(const EVP_CIPHER *type, const char *name,
|
|
+ const unsigned char *key,
|
|
+ const unsigned char *iv, size_t iv_size,
|
|
+ size_t mac_size)
|
|
+{
|
|
+ EVP_CIPHER_CTX *enc = NULL, *dec = NULL;
|
|
+ OSSL_PARAM params[2];
|
|
+ unsigned char rec[128];
|
|
+ unsigned char ct[128];
|
|
+ unsigned char *tls_mac = NULL;
|
|
+ int outl = 0, part = 0;
|
|
+ int ret = 0, test;
|
|
+ size_t i;
|
|
+
|
|
+ if (EVP_CIPHER_get0_provider(type) == NULL)
|
|
+ return 0;
|
|
+
|
|
+ T(mac_size > 0 && mac_size <= EVP_MAX_MD_SIZE);
|
|
+ T(sizeof(rec) >= sizeof(P) + mac_size);
|
|
+ memcpy(rec, P, sizeof(P));
|
|
+ for (i = 0; i < mac_size; i++)
|
|
+ rec[sizeof(P) + i] = (unsigned char)(0xa0 + i);
|
|
+
|
|
+ printf("Provider tls-mac test [%s]: ", name);
|
|
+
|
|
+ enc = EVP_CIPHER_CTX_new();
|
|
+ dec = EVP_CIPHER_CTX_new();
|
|
+ T(enc != NULL && dec != NULL);
|
|
+
|
|
+ /* libssl sets tls-mac-size on both directions */
|
|
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, &mac_size);
|
|
+ params[1] = OSSL_PARAM_construct_end();
|
|
+
|
|
+ T(EVP_CipherInit_ex(enc, type, NULL, key, iv, 1));
|
|
+ T(EVP_CIPHER_CTX_set_params(enc, params));
|
|
+ /* Nothing must be cut off the output while encrypting */
|
|
+ T(EVP_EncryptUpdate(enc, ct, &outl, rec, (int)(sizeof(P) + mac_size)));
|
|
+ TEST_ASSERT((size_t)outl != sizeof(P) + mac_size);
|
|
+ ret |= test;
|
|
+
|
|
+ T(EVP_CipherInit_ex(dec, type, NULL, key, iv, 0));
|
|
+ T(EVP_CIPHER_CTX_set_params(dec, params));
|
|
+ T(EVP_DecryptUpdate(dec, ct, &part, ct, outl));
|
|
+ /* The plaintext must come out without the MAC tail */
|
|
+ TEST_ASSERT((size_t)part != sizeof(P) || memcmp(ct, P, sizeof(P)) != 0);
|
|
+ ret |= test;
|
|
+
|
|
+ params[0] = OSSL_PARAM_construct_octet_ptr(OSSL_CIPHER_PARAM_TLS_MAC,
|
|
+ (void **)&tls_mac, mac_size);
|
|
+ params[1] = OSSL_PARAM_construct_end();
|
|
+ T(EVP_CIPHER_CTX_get_params(dec, params));
|
|
+ T(params[0].return_size == mac_size);
|
|
+ TEST_ASSERT(tls_mac == NULL
|
|
+ || memcmp(tls_mac, rec + sizeof(P), mac_size) != 0);
|
|
+ ret |= test;
|
|
+
|
|
+ EVP_CIPHER_CTX_free(enc);
|
|
+ EVP_CIPHER_CTX_free(dec);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
int engine_is_available(const char *name)
|
|
{
|
|
#if HAVE_ENGINE
|
|
@@ -963,6 +1039,10 @@ int main(int argc, char **argv)
|
|
}
|
|
ret |= test_provider_padded_decrypt(ciph, t->algname, t->block,
|
|
t->key, t->iv);
|
|
+ if (t->stream || strcmp(t->algname, SN_magma_ctr) == 0)
|
|
+ ret |= test_provider_tls_mac(ciph, t->algname, t->key,
|
|
+ t->iv, t->iv_size,
|
|
+ tls_mac_size_for_cipher(t->algname));
|
|
}
|
|
ret |= test_engine_asn1_acpkm_reinit(ciph, t->algname, t->key, t->iv);
|
|
|
|
--
|
|
2.43.0
|
|
|