Skip to content

Commit 3cc5bae

Browse files
committed
WIP: TLS-MAC support
Signed-off-by: Simo Sorce <simo@redhat.com>
1 parent de9b87f commit 3cc5bae

2 files changed

Lines changed: 269 additions & 17 deletions

File tree

src/cipher.c

Lines changed: 251 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
#if SKEY_SUPPORT == 1
77

88
#include "cipher.h"
9-
#include <string.h>
109
#include "openssl/prov_ssl.h"
10+
#include "openssl/rand.h"
11+
#include <string.h>
12+
13+
#define MAX_PADDING 256;
14+
#define AESBLOCK 16 /* 128 bits for all AES modes */
1115

1216
DISPATCH_CIPHER_FN(cipher, freectx);
1317
DISPATCH_CIPHER_FN(aes, dupctx);
@@ -35,6 +39,14 @@ struct p11prov_cipher_ctx {
3539
CK_FLAGS operation;
3640

3741
P11PROV_SESSION *session;
42+
43+
/* OpenSSL violates layering separation and decided
44+
* to process AES CBC MAC/padding handling in TLS 1.x < 1.3
45+
* in the lower cipher layer, so we have to do it here as well
46+
* for compatibility ... */
47+
unsigned int tlsver;
48+
size_t tlsmacsize;
49+
unsigned char *tlsmac;
3850
};
3951

4052
static void *p11prov_cipher_newctx(void *provctx, int size, CK_ULONG mechanism)
@@ -157,7 +169,7 @@ static int p11prov_aes_get_params(OSSL_PARAM params[], int size, int mode,
157169
int ciph_mode = 0;
158170
int flags = mode & MODE_flags_mask;
159171
size_t keysize = size / 8;
160-
size_t blocksize = 16; /* 128 bits for all AES modes */
172+
size_t blocksize = AESBLOCK;
161173
size_t ivsize = 16; /* 128 bits for all modes but ECB */
162174

163175
switch (mode & MODE_modes_mask) {
@@ -200,6 +212,7 @@ static void p11prov_cipher_freectx(void *ctx)
200212
p11prov_obj_free(cctx->key);
201213
p11prov_return_session(cctx->session);
202214
OPENSSL_clear_free(cctx->mech.pParameter, cctx->mech.ulParameterLen);
215+
OPENSSL_clear_free(cctx->tlsmac, cctx->tlsmacsize);
203216
OPENSSL_clear_free(cctx, sizeof(struct p11prov_cipher_ctx));
204217
}
205218

@@ -330,6 +343,12 @@ static CK_RV p11prov_cipher_session_init(struct p11prov_cipher_ctx *cctx)
330343
return CKR_SLOT_ID_INVALID;
331344
}
332345

346+
if (cctx->tlsver != 0 && cctx->mech.mechanism == CKM_AES_CBC_PAD) {
347+
/* In the special TLS mode we handle de-padding and mac extraction
348+
* outside the pkcs11 module to conform to what OpenSSL does */
349+
cctx->mech.mechanism = CKM_AES_CBC;
350+
}
351+
333352
rv = p11prov_get_session(cctx->provctx, &slotid, NULL, NULL,
334353
cctx->mech.mechanism, NULL, NULL, true, false,
335354
&cctx->session);
@@ -447,32 +466,210 @@ static int p11prov_cipher_decrypt_skey_init(void *ctx, void *keydata,
447466
return RET_OSSL_OK;
448467
}
449468

469+
/* This function needs to be executed in constant time */
470+
static CK_RV tlsunpad(struct p11prov_cipher_ctx *cctx, unsigned char *out,
471+
CK_ULONG inlen, CK_ULONG *outlen)
472+
{
473+
CK_RV rv = CKR_GENERAL_ERROR;
474+
CK_ULONG overhead = cctx->tlsmacsize + 1; /* mac size + padlen byte */
475+
CK_ULONG maxcheck = MAX_PADDING;
476+
CK_ULONG padsize = out[inlen - 1];
477+
CK_ULONG olen = inlen;
478+
CK_ULONG pass;
479+
480+
/* Remove explicit IV for TLS 1.1 and 1.2 */
481+
if (cctx->tlsver != 0x301) {
482+
/* This is a bad interface as it make it seem that
483+
* the returned output buffer is incorrectly pointing
484+
* at the IV and not the data, but OpenSSL will in turn
485+
* offset the buffer later, based on knoledge that this
486+
* cipher return a length that excludes the IV from the
487+
* count. */
488+
out += AESBLOCK;
489+
olen = inlen - AESBLOCK;
490+
}
491+
492+
/* olen is public known so can be checked normally */
493+
if (olen < overhead) {
494+
return CKR_BUFFER_TOO_SMALL;
495+
}
496+
497+
if (olen < cctx->tlsmacsize) {
498+
return CKR_BUFFER_TOO_SMALL;
499+
}
500+
501+
if (maxcheck > olen) {
502+
maxcheck = olen;
503+
}
504+
505+
/* olen must not be smaller than padsize + overhead */
506+
pass = ~constant_smaller_mask(olen, overhead + padsize);
507+
508+
/* creates a mask so that we check only the padding bytes
509+
* without revealing the padding length in a conditional.
510+
* mask is 0xff when i < padsize, and 0 otherwise, allowing
511+
* us to scan the whole buffer while really only testing for
512+
* equality only the padding part, as the xoring with non-pad
513+
* data is ignored my the empty mask. We skip checking the
514+
* last value itself as that is always == padsize */
515+
for (int i = 0; i < maxcheck - 1; i++) {
516+
unsigned char mask = constant_smaller_mask(i, padsize);
517+
unsigned char data = out[olen - i - 2];
518+
519+
pass &= ~(mask & (padsize ^ data));
520+
}
521+
522+
/* renormalize to a CK_ULONG */
523+
pass = constant_equal_mask(pass, 0xff);
524+
525+
if (cctx->tlsmacsize > 0) {
526+
unsigned char randmac[EVP_MAX_MD_SIZE];
527+
size_t mac_pos = olen - cctx->tlsmacsize - (pass & (padsize + 1));
528+
size_t mac_area = 0;
529+
int err = RET_OSSL_ERR;
530+
531+
/* allocate space for the mac */
532+
cctx->tlsmac = OPENSSL_zalloc(cctx->tlsmacsize);
533+
if (!cctx->tlsmac) {
534+
return CKR_GENERAL_ERROR;
535+
}
536+
537+
/* random mac we return if something is wrong */
538+
err = RAND_bytes_ex(p11prov_ctx_get_libctx(cctx->provctx), randmac,
539+
sizeof(randmac), 0);
540+
if (err != RET_OSSL_OK) {
541+
return CKR_GENERAL_ERROR;
542+
}
543+
544+
/* olen and mac size are public data, so we can do this
545+
* assignment without bothering with constant time */
546+
if (olen > cctx->tlsmacsize + 256) {
547+
mac_area = olen - cctx->tlsmacsize - 256;
548+
}
549+
550+
for (size_t i = mac_area; i < olen; i++) {
551+
for (int j = 0; j < cctx->tlsmacsize; j++) {
552+
unsigned char mask =
553+
~constant_smaller_mask(i, mac_pos)
554+
& constant_smaller_mask(i, mac_pos + cctx->tlsmacsize)
555+
& constant_equal_mask(i, j + mac_pos);
556+
cctx->tlsmac[j] |= out[i] & mask;
557+
}
558+
}
559+
560+
/* on depadding failure overwrite with random data */
561+
for (int j = 0; j < cctx->tlsmacsize; j++) {
562+
cctx->tlsmac[j] =
563+
constant_select_byte_mask(cctx->tlsmac[j], randmac[j], pass);
564+
}
565+
566+
rv = CKR_OK;
567+
} else {
568+
/* no MAC to check just return the result */
569+
if (pass + 1 == 0) {
570+
rv = CKR_OK;
571+
}
572+
}
573+
574+
*outlen = olen - cctx->tlsmacsize - (pass & (padsize + 1));
575+
return rv;
576+
}
577+
450578
static int p11prov_cipher_update(void *ctx, unsigned char *out, size_t *outl,
451579
size_t outsize, const unsigned char *in,
452580
size_t inl)
453581
{
454582
struct p11prov_cipher_ctx *cctx = (struct p11prov_cipher_ctx *)ctx;
583+
CK_SESSION_HANDLE session_handle;
455584
CK_ULONG outlen = outsize;
456585
CK_ULONG inlen = inl;
457586
CK_RV rv;
458587

588+
if (cctx->tlsver != 0) {
589+
/* Special OpenSSL layering violating mode.
590+
* A single update is a full record.
591+
* Inputs need to be consistent with stricter requirements */
592+
if (!in || in != out || outsize < inl || !cctx->pad) {
593+
ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
594+
return 0;
595+
}
596+
}
597+
459598
if (!cctx->session) {
460599
rv = p11prov_cipher_session_init(cctx);
461600
if (rv != CKR_OK) {
462601
return RET_OSSL_ERR;
463602
}
464603
}
604+
session_handle = p11prov_session_handle(cctx->session);
465605

466606
switch (cctx->operation) {
467607
case CKF_ENCRYPT:
468-
rv = p11prov_EncryptUpdate(cctx->provctx,
469-
p11prov_session_handle(cctx->session),
470-
(void *)in, inlen, out, &outlen);
608+
if (cctx->tlsver != 0) {
609+
/* Custom handle padding */
610+
size_t padsize = AESBLOCK - (inl % AESBLOCK);
611+
unsigned char padval = (unsigned char)(padsize - 1);
612+
613+
if (outsize < inl + padsize) {
614+
rv = CKR_BUFFER_TOO_SMALL;
615+
P11PROV_raise(cctx->provctx, rv, "Output buffer too small");
616+
return RET_OSSL_ERR;
617+
}
618+
inlen += padsize;
619+
if ((inlen % AESBLOCK) != 0) {
620+
rv = CKR_ARGUMENTS_BAD;
621+
P11PROV_raise(cctx->provctx, rv, "Invalid input buffer size");
622+
return RET_OSSL_ERR;
623+
}
624+
/* add the padding, relies on in == out and therefore enough
625+
* space available in the buffer */
626+
memset(&out[inl], padval, padsize);
627+
628+
/* in TLS mode we must use signle shot decryption to properly
629+
* auto-finalize the session as OpenSSL won't */
630+
rv = p11prov_Encrypt(cctx->provctx, session_handle, (void *)in,
631+
inlen, out, &outlen);
632+
633+
/* unconditionally return the session */
634+
p11prov_return_session(cctx->session);
635+
cctx->session = NULL;
636+
} else {
637+
rv = p11prov_EncryptUpdate(cctx->provctx, session_handle,
638+
(void *)in, inlen, out, &outlen);
639+
}
471640
break;
472641
case CKF_DECRYPT:
473-
rv = p11prov_DecryptUpdate(cctx->provctx,
474-
p11prov_session_handle(cctx->session),
475-
(void *)in, inlen, out, &outlen);
642+
if (cctx->tlsver != 0) {
643+
if ((inlen % AESBLOCK) != 0) {
644+
rv = CKR_ARGUMENTS_BAD;
645+
P11PROV_raise(cctx->provctx, rv, "Invalid input buffer size");
646+
return RET_OSSL_ERR;
647+
}
648+
/* in TLS mode we must use signle shot decryption to properly
649+
* auto-finalize the session as OpenSSL won't */
650+
rv = p11prov_Decrypt(cctx->provctx, session_handle, (void *)in,
651+
inlen, out, &outlen);
652+
653+
/* unconditionally return the session */
654+
p11prov_return_session(cctx->session);
655+
cctx->session = NULL;
656+
657+
if (rv != CKR_OK) {
658+
P11PROV_raise(cctx->provctx, rv, "Decryption failure");
659+
return RET_OSSL_ERR;
660+
}
661+
/* remove padding and fill in tlsmac as needed */
662+
if (cctx->tlsmac) {
663+
OPENSSL_clear_free(cctx->tlsmac, cctx->tlsmacsize);
664+
cctx->tlsmac = NULL;
665+
}
666+
667+
/* Assumes inlen = outlen on correct decryption */
668+
rv = tlsunpad(cctx, out, inlen, &outlen);
669+
} else {
670+
rv = p11prov_DecryptUpdate(cctx->provctx, session_handle,
671+
(void *)in, inlen, out, &outlen);
672+
}
476673
break;
477674
default:
478675
rv = CKR_GENERAL_ERROR;
@@ -604,9 +801,11 @@ static int p11prov_aes_get_ctx_params(void *ctx, OSSL_PARAM params[])
604801

605802
p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC);
606803
if (p) {
607-
/* TODO: ? (octet_ptr) */
608-
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
609-
return RET_OSSL_ERR;
804+
ret = OSSL_PARAM_set_octet_ptr(p, cctx->tlsmac, cctx->tlsmacsize);
805+
if (ret != RET_OSSL_OK) {
806+
ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
807+
return RET_OSSL_ERR;
808+
}
610809
}
611810

612811
return RET_OSSL_OK;
@@ -679,6 +878,43 @@ static int p11prov_aes_set_ctx_params(void *vctx, const OSSL_PARAM params[])
679878
}
680879
}
681880

881+
p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION);
882+
if (p) {
883+
CK_RV rv = CKR_MECHANISM_PARAM_INVALID;
884+
unsigned int version;
885+
int ret = OSSL_PARAM_get_uint(p, &version);
886+
if (ret != RET_OSSL_OK) {
887+
P11PROV_raise(ctx->provctx, rv, "Invalid TLS Version parameter");
888+
return RET_OSSL_ERR;
889+
}
890+
switch (version) {
891+
case 0x301: /* TLS 1.0 */
892+
case 0x302: /* TLS 1.1 */
893+
case 0x303: /* TLS 1.2 */
894+
ctx->tlsver = version;
895+
break;
896+
default:
897+
P11PROV_raise(ctx->provctx, rv, "Unsupported TLS Version");
898+
return RET_OSSL_ERR;
899+
}
900+
}
901+
902+
p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE);
903+
if (p) {
904+
CK_RV rv = CKR_MECHANISM_PARAM_INVALID;
905+
size_t macsize;
906+
int ret = OSSL_PARAM_get_size_t(p, &macsize);
907+
if (ret != RET_OSSL_OK) {
908+
P11PROV_raise(ctx->provctx, rv, "Invalid TLS MAC Size parameter");
909+
return RET_OSSL_ERR;
910+
}
911+
if (macsize > EVP_MAX_MD_SIZE) {
912+
P11PROV_raise(ctx->provctx, rv, "Invalid TLS Mac Size");
913+
return RET_OSSL_ERR;
914+
}
915+
ctx->tlsmacsize = macsize;
916+
}
917+
682918
return RET_OSSL_OK;
683919
}
684920

@@ -689,9 +925,7 @@ static const OSSL_PARAM p11prov_aes_generic_gettable_ctx_params[] = {
689925
OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL),
690926
OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
691927
OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
692-
/* Supported by OpenSSL but not here yet
693-
* OSSL_CIPHER_PARAM_TLS_MAC
694-
*/
928+
OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS_MAC, NULL, 0),
695929
OSSL_PARAM_END
696930
};
697931

@@ -729,12 +963,12 @@ static const OSSL_PARAM *p11prov_aes_gettable_ctx_params(void *vctx,
729963
/* Supported by OpenSSL but not here:
730964
* OSSL_CIPHER_PARAM_NUM (uint)
731965
* OSSL_CIPHER_PARAM_USE_BITS (uint)
732-
* OSSL_CIPHER_PARAM_TLS_VERSION (uint)
733-
* OSSL_CIPHER_PARAM_TLS_MAC_SIZE (size_t)
734966
*/
735967

736968
static const OSSL_PARAM p11prov_aes_generic_settable_ctx_params[] = {
737-
GENERIC_SETTABLE_CTX_PARAMS(), OSSL_PARAM_END
969+
GENERIC_SETTABLE_CTX_PARAMS(),
970+
OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL),
971+
OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL), OSSL_PARAM_END
738972
};
739973

740974
static const OSSL_PARAM p11prov_aes_cts_settable_ctx_params[] = {

src/util.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,21 @@ CK_RV p11prov_mutex_destroy(P11PROV_CTX *provctx, pthread_mutex_t *lock,
106106

107107
void p11prov_force_rwlock_reinit(pthread_rwlock_t *lock);
108108

109+
static inline CK_ULONG constant_smaller_mask(CK_ULONG a, CK_ULONG b)
110+
{
111+
return 0 - ((a ^ ((a ^ b) | ((a - b) ^ b))) >> (sizeof(CK_ULONG) * 8 - 1));
112+
}
113+
109114
static inline CK_ULONG constant_equal(CK_ULONG a, CK_ULONG b)
110115
{
111116
return ((a ^ b) - 1U) >> (sizeof(CK_ULONG) * 8 - 1);
112117
}
113118

119+
static inline CK_ULONG constant_equal_mask(CK_ULONG a, CK_ULONG b)
120+
{
121+
return 0 - constant_equal(a, b);
122+
}
123+
114124
static inline int constant_select_int(CK_ULONG cond, int a, int b)
115125
{
116126
volatile unsigned int A = (unsigned int)a;
@@ -120,6 +130,14 @@ static inline int constant_select_int(CK_ULONG cond, int a, int b)
120130
return (int)((A & mask) | (B & ~mask));
121131
}
122132

133+
static inline uint8_t constant_select_byte_mask(uint8_t a, uint8_t b,
134+
uint8_t mask)
135+
{
136+
volatile uint8_t A = a & mask;
137+
volatile uint8_t B = b & ~mask;
138+
return A | B;
139+
}
140+
123141
static inline void constant_select_buf(CK_ULONG cond, CK_ULONG size,
124142
unsigned char *dst, unsigned char *a,
125143
unsigned char *b)

0 commit comments

Comments
 (0)