WIP: tests: Add test case for KDFa replacement by OpenSSL 'KBKDF'

Implement a KDFa replacement using OpenSSL's KBKDF function. The resulting
implementation has a few restrictions compared to the original one.
For example, it cannot accept a counter value to be passed in to resume
the KDFa as the reference implementation could -- OSSL does not support
providing the counter in this becomes problematic with DRBG_Generate
where it seems to want to resume with a counter over the long-term.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
Stefan Berger 2024-07-15 18:01:37 -04:00
parent 036cb1abd1
commit 42cbf67ebe
5 changed files with 256 additions and 2 deletions

1
.gitignore vendored
View File

@ -52,6 +52,7 @@ Makefile
/tests/base64decode
/tests/fuzz
/tests/freebl_sha1flattensize
/tests/kdfa_openssl
/tests/kdfe_openssl
/tests/nvram_offsets
/tests/object_size

View File

@ -1204,3 +1204,147 @@ out:
}
#endif // USE_OPENSSL_FUNCTIONS_SSKDF
#if USE_OPENSSL_FUNCTIONS_KBKDF
UINT16 OSSLCryptKDFa(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
const TPM2B* key, // IN: HMAC key
const TPM2B* label, // IN: a label for the KDF
const TPM2B* contextU, // IN: context U
const TPM2B* contextV, // IN: context V
UINT32 sizeInBits, // IN: size of generated key in bits
BYTE* keyStream, // OUT: key buffer
UINT32* counterInOut, // IN/OUT: caller may provide the iteration
// counter for incremental operations to
// avoid large intermediate buffers.
UINT16 blocks // IN: If non-zero, this is the maximum number
// of blocks to be returned, regardless
// of sizeInBits
)
{
UINT16 digestSize = CryptHashGetDigestSize(hashAlg);
char digestname[16];
OSSL_PARAM params[8];
OSSL_PARAM *p = params;
int use_separator = 0;
UINT16 generated = 0;
size_t contexts_size;
size_t buffer_size;
UINT32 counter = 0; // counter value
char *buffer; // for contextU+V and key and label
size_t offset;
EVP_KDF_CTX *ctx;
EVP_KDF *kdf;
INT16 bytes; // number of bytes to generate
const char *name;
pAssert(key != NULL && keyStream != NULL);
TPM_DO_SELF_TEST(TPM_ALG_KDF1_SP800_108);
if(digestSize == 0)
return 0;
if(counterInOut != NULL)
counter = *counterInOut;
/* The TPM2 reference implementation can iterate by calling CryptKDFe multiple
* times and allows providing a counter for the next iteration. The
* OpenSSL function does not allow this. Therefore:
* - This implementation MUST NOT be used with counter != 0. Adjust caller!
*/
pAssert(counter == 0);
/* - This implementation MUST NOT be called blocks != 0 and
* blocks * digestSize * 8 != sizeInBits. Better call with blocks = 0!
*/
pAssert(blocks == 0 || blocks * digestSize * 8 == sizeInBits);
/* - This implementation MUST NOT be called for sizeInBits & 7 != 0 since
* OpenSSL implementation only allows for multiples of 8.
*/
pAssert((sizeInBits & 7) == 0);
// If the size of the request is larger than the numbers will handle,
// it is a fatal error.
pAssert(((sizeInBits + 7) / 8) <= INT16_MAX);
// The number of bytes to be generated is the smaller of the sizeInBits bytes or
// the number of requested blocks. The number of blocks is the smaller of the
// number requested or the number allowed by sizeInBits. A partial block is
// a full block.
bytes = (blocks > 0) ? blocks * digestSize : (UINT16)BITS_TO_BYTES(sizeInBits);
name = GetDigestNameByHashAlg(hashAlg);
if (!name)
return 0;
if (strlen(name) >= sizeof(digestname))
FAIL(FATAL_ERROR_INTERNAL);
strcpy(digestname, name);
buffer_size = 0;
if (contextU)
buffer_size += contextU->size;
if (contextV)
buffer_size += contextV->size;
buffer_size += key->size;
if (label)
buffer_size += label->size;
buffer = malloc(buffer_size);
if (!buffer)
return 0;
kdf = EVP_KDF_fetch(NULL, OSSL_KDF_NAME_KBKDF, NULL);
if (!kdf)
goto out;
ctx = EVP_KDF_CTX_new(kdf);
if (!ctx)
goto out;
/* fill buffer: 1st contexts; 2nd key; 3rd label */
offset = 0;
if (contextU) {
memcpy(&buffer[offset], contextU->buffer, contextU->size);
offset += contextU->size;
}
if (contextV) {
memcpy(&buffer[offset], contextV->buffer, contextV->size);
offset += contextV->size;
}
contexts_size = offset;
memcpy(&buffer[contexts_size], key->buffer, key->size);
if (label)
memcpy(&buffer[contexts_size + key->size], label->buffer, label->size);
if ((label == NULL) || (label->size == 0)
|| (label->buffer[label->size - 1] != 0))
use_separator = 1;
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
digestname, 0);
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_MAC,
"HMAC", 0);
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
buffer, contexts_size);
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
&buffer[contexts_size], key->size);
if (label)
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
&buffer[contexts_size + key->size],
label->size);
*p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR,
&use_separator);
*p = OSSL_PARAM_construct_end();
if (EVP_KDF_derive(ctx, keyStream, bytes, params) <= 0)
goto out;
generated = bytes;
out:
EVP_KDF_free(kdf);
return generated;
}
#endif // USE_OPENSSL_FUNCTIONS_KBKDF

View File

@ -131,4 +131,23 @@ OSSLCryptKDFe(TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
);
#endif // USE_OPENSSL_FUNCTIONS_SSKDF
#if USE_OPENSSL_FUNCTIONS_KBKDF
LIB_EXPORT UINT16
OSSLCryptKDFa(
TPM_ALG_ID hashAlg, // IN: hash algorithm used in HMAC
const TPM2B* key, // IN: HMAC key
const TPM2B* label, // IN: a label for the KDF
const TPM2B* contextU, // IN: context U
const TPM2B* contextV, // IN: context V
UINT32 sizeInBits, // IN: size of generated key in bits
BYTE* keyStream, // OUT: key buffer
UINT32* counterInOut, // IN/OUT: caller may provide the iteration
// counter for incremental operations to
// avoid large intermediate buffers.
UINT16 blocks // IN: If non-zero, this is the maximum number
// of blocks to be returned, regardless
// of sizeInBits
);
#endif // USE_OPENSSL_FUNCTIONS_KBKDF
#endif /* HELPERS_FP_H */

View File

@ -57,10 +57,12 @@ if ENABLE_STATIC_TESTS
# object_size needs ANY_OBJECT_Marshal which only is accessible with '-static'
check_PROGRAMS += \
object_size \
kdfe_openssl
kdfe_openssl \
kdfa_openssl
TESTS += \
object_size \
kdfe_openssl
kdfe_openssl \
kdfa_openssl
object_size_SOURCES = object_size.c
object_size_CFLAGS = $(AM_CFLAGS) \
@ -85,6 +87,18 @@ kdfe_openssl_CFLAGS = $(AM_CFLAGS) \
-DTPM_POSIX
kdfe_openssl_LDFLAGS = $(AM_LDFLAGS)
kdfa_openssl_SOURCES = kdfa_openssl.c
kdfa_openssl_CFLAGS = $(AM_CFLAGS) \
-static \
-g -ggdb \
-I$(top_srcdir)/include/libtpms \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/tpm2 \
-I$(top_srcdir)/src/tpm2/crypto \
-I$(top_srcdir)/src/tpm2/crypto/openssl \
-DTPM_POSIX
kdfa_openssl_LDFLAGS = $(AM_LDFLAGS)
endif # ENABLE_STATIC_TESTS
endif # WITH_TPM2

76
tests/kdfa_openssl.c Normal file
View File

@ -0,0 +1,76 @@
#if USE_OPENSSL_FUNCTIONS_SSKDF
#include "Tpm.h"
#include "Helpers_fp.h"
int main(void)
{
UINT16 gen1, gen2;
TPM2B_LABEL key = {
.t.size = 0x20,
.t.buffer = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f},
};
TPM2B_LABEL label = {
.t.size = 5,
.t.buffer = "label",
};
TPM2B_LABEL contextU = {
.t.size = 6,
.t.buffer = "test12",
};
TPM2B_LABEL contextV = {
.t.size = 7,
.t.buffer = "test123",
};
BYTE keyStream1[256] = {0, }, keyStream2[256] = {0, };
UINT32 counter1, counter2;
const TPM_ALG_ID hashAlgs[] = {
TPM_ALG_SHA1,
TPM_ALG_SHA256,
TPM_ALG_SHA384,
TPM_ALG_SHA512
};
UINT32 sizeInBits;
UINT16 blocks;
size_t i;
for (sizeInBits = 0; sizeInBits < 8 * sizeof(keyStream1); sizeInBits += 8) {
for (i = 0; i < ARRAY_SIZE(hashAlgs); i++) {
counter1 = 0;
memset(keyStream1, 0, sizeof(keyStream1));
gen1 = CryptKDFa(hashAlgs[i], &key.b, &label.b,
&contextU.b, &contextV.b, sizeInBits, keyStream1,
&counter1, blocks);
counter2 = 0;
memset(keyStream2, 0, sizeof(keyStream2));
gen2 = OSSLCryptKDFa(hashAlgs[i], &key.b, &label.b,
&contextU.b, &contextV.b, sizeInBits, keyStream2,
&counter2, blocks);
if (gen1 != gen2 || memcmp(keyStream1, keyStream2, gen1)) {
fprintf(stderr, "results are not equal: gen1: %d gen2: %d hash: %d sizeInBits: %d\n",
gen1, gen2, hashAlgs[i], sizeInBits);
fprintf(stderr, "%02x %02x %02x ... %02x\n",
keyStream1[0], keyStream1[1], keyStream1[2], keyStream1[gen1 - 1]);
fprintf(stderr, "%02x %02x %02x ... %02x\n",
keyStream2[0], keyStream2[1], keyStream2[2], keyStream2[gen2 - 1]);
return 1;
}
fprintf(stdout, "Success with hash %d, sizeInBits %d\n",
hashAlgs[i], sizeInBits);
}
}
}
#else
int main(void)
{
return 0;
}
#endif