Skip to content

Commit 27dacc9

Browse files
committed
Add DH KDF tests
1 parent 2b40cd6 commit 27dacc9

3 files changed

Lines changed: 280 additions & 0 deletions

File tree

test/test_dh.c

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -912,4 +912,278 @@ int test_dh_pad(void *data)
912912
return err;
913913
}
914914

915+
#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256)
916+
/* Apply X9.63 KDF using OpenSSL's reference implementation. */
917+
static int test_dh_x963_kdf_ref(const unsigned char* secret, size_t secLen,
918+
const char* mdName, const unsigned char* ukm, size_t ukmLen,
919+
unsigned char* out, size_t outLen)
920+
{
921+
int err = 0;
922+
EVP_KDF *kdf = NULL;
923+
EVP_KDF_CTX *kctx = NULL;
924+
OSSL_PARAM params[4];
925+
OSSL_PARAM *p = params;
926+
927+
kdf = EVP_KDF_fetch(osslLibCtx, OSSL_KDF_NAME_X963KDF, NULL);
928+
err = kdf == NULL;
929+
if (err == 0) {
930+
kctx = EVP_KDF_CTX_new(kdf);
931+
err = kctx == NULL;
932+
}
933+
if (err == 0) {
934+
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
935+
(char*)mdName, 0);
936+
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
937+
(unsigned char*)secret, secLen);
938+
if (ukm != NULL && ukmLen > 0) {
939+
*p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
940+
(unsigned char*)ukm, ukmLen);
941+
}
942+
*p = OSSL_PARAM_construct_end();
943+
944+
err = EVP_KDF_derive(kctx, out, outLen, params) <= 0;
945+
}
946+
947+
EVP_KDF_CTX_free(kctx);
948+
EVP_KDF_free(kdf);
949+
return err;
950+
}
951+
952+
/* Derive raw DH shared secret (no KDF) using wolfProvider. Caller frees. */
953+
static int test_dh_derive_raw(EVP_PKEY *key, EVP_PKEY *peerKey,
954+
unsigned char **pSecret, size_t *pSecretLen)
955+
{
956+
int err = 0;
957+
EVP_PKEY_CTX *ctx = NULL;
958+
unsigned char *secret = NULL;
959+
size_t len = 0;
960+
961+
ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, key, NULL);
962+
err = ctx == NULL;
963+
if (err == 0) {
964+
err = EVP_PKEY_derive_init(ctx) <= 0;
965+
}
966+
if (err == 0) {
967+
err = EVP_PKEY_derive_set_peer(ctx, peerKey) <= 0;
968+
}
969+
if (err == 0) {
970+
err = EVP_PKEY_derive(ctx, NULL, &len) <= 0;
971+
}
972+
if (err == 0) {
973+
secret = (unsigned char*)OPENSSL_malloc(len);
974+
err = secret == NULL;
975+
}
976+
if (err == 0) {
977+
err = EVP_PKEY_derive(ctx, secret, &len) <= 0;
978+
}
979+
if (err == 0) {
980+
*pSecret = secret;
981+
*pSecretLen = len;
982+
secret = NULL;
983+
}
984+
985+
OPENSSL_free(secret);
986+
EVP_PKEY_CTX_free(ctx);
987+
return err;
988+
}
989+
990+
/* Derive via wolfProvider with X9.63 KDF parameters set. */
991+
static int test_dh_derive_with_x963(EVP_PKEY *key, EVP_PKEY *peerKey,
992+
const char* mdName, size_t outLen, const unsigned char* ukm, size_t ukmLen,
993+
unsigned char *out, size_t outBufLen)
994+
{
995+
int err = 0;
996+
EVP_PKEY_CTX *ctx = NULL;
997+
OSSL_PARAM params[5];
998+
OSSL_PARAM *p = params;
999+
size_t derivedLen = outBufLen;
1000+
1001+
ctx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, key, NULL);
1002+
err = ctx == NULL;
1003+
if (err == 0) {
1004+
err = EVP_PKEY_derive_init(ctx) <= 0;
1005+
}
1006+
if (err == 0) {
1007+
err = EVP_PKEY_derive_set_peer(ctx, peerKey) <= 0;
1008+
}
1009+
if (err == 0) {
1010+
/* wolfProvider maps the X942KDF-ASN1 type string to its internal X963
1011+
* KDF implementation (wc_X963_KDF). */
1012+
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
1013+
(char*)OSSL_KDF_NAME_X942KDF_ASN1, 0);
1014+
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
1015+
(char*)mdName, 0);
1016+
*p++ = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
1017+
&outLen);
1018+
if (ukm != NULL && ukmLen > 0) {
1019+
*p++ = OSSL_PARAM_construct_octet_string(
1020+
OSSL_EXCHANGE_PARAM_KDF_UKM, (unsigned char*)ukm, ukmLen);
1021+
}
1022+
*p = OSSL_PARAM_construct_end();
1023+
1024+
err = EVP_PKEY_CTX_set_params(ctx, params) != 1;
1025+
}
1026+
if (err == 0) {
1027+
err = EVP_PKEY_derive(ctx, out, &derivedLen) <= 0;
1028+
}
1029+
if (err == 0 && derivedLen != outLen) {
1030+
PRINT_ERR_MSG("KDF output length %zu != requested %zu", derivedLen,
1031+
outLen);
1032+
err = 1;
1033+
}
1034+
1035+
EVP_PKEY_CTX_free(ctx);
1036+
return err;
1037+
}
1038+
1039+
/**
1040+
* Test DH key derivation through the X9.63 KDF path.
1041+
*
1042+
* The provider's WP_KDF_X963 branch in wp_dh_derive (1) allocates a temporary
1043+
* buffer sized to the prime length, (2) runs the raw DH agreement into it,
1044+
* (3) feeds the result through wc_X963_KDF, and (4) securely frees the
1045+
* temporary. We validate by computing the same KDF output independently via
1046+
* OpenSSL's X963KDF and comparing.
1047+
*/
1048+
int test_dh_x963_kdf(void *data)
1049+
{
1050+
int err = 0;
1051+
DH *dh = NULL;
1052+
EVP_PKEY *params = NULL;
1053+
BIGNUM *p = NULL;
1054+
BIGNUM *g = NULL;
1055+
EVP_PKEY_CTX *kgCtx = NULL;
1056+
EVP_PKEY *keyA = NULL;
1057+
EVP_PKEY *keyB = NULL;
1058+
unsigned char *raw = NULL;
1059+
size_t rawLen = 0;
1060+
unsigned char wpOut[96];
1061+
unsigned char refOut[96];
1062+
unsigned char tooSmallBuf[8];
1063+
size_t tooSmallLen;
1064+
static const unsigned char ukm[] = {
1065+
0xa1, 0xb2, 0xc3, 0xd4, 0xe5, 0xf6, 0x07, 0x18,
1066+
0x29, 0x3a, 0x4b, 0x5c, 0x6d, 0x7e, 0x8f, 0x90
1067+
};
1068+
static const struct {
1069+
const char* md;
1070+
size_t outLen;
1071+
int withUkm;
1072+
} cases[] = {
1073+
{ "SHA256", 16, 0 },
1074+
{ "SHA256", 32, 0 },
1075+
{ "SHA256", 48, 0 },
1076+
{ "SHA256", 64, 0 },
1077+
{ "SHA256", 32, 1 },
1078+
};
1079+
size_t i;
1080+
1081+
(void)data;
1082+
1083+
PRINT_MSG("DH X9.63 KDF derivation");
1084+
1085+
dh = DH_new();
1086+
err = dh == NULL;
1087+
if (err == 0) {
1088+
p = BN_bin2bn(dh_p, sizeof(dh_p), NULL);
1089+
err = p == NULL;
1090+
}
1091+
if (err == 0) {
1092+
g = BN_bin2bn(dh_g, sizeof(dh_g), NULL);
1093+
err = g == NULL;
1094+
}
1095+
if (err == 0) {
1096+
err = DH_set0_pqg(dh, p, NULL, g) == 0;
1097+
if (err == 0) {
1098+
p = NULL;
1099+
g = NULL;
1100+
}
1101+
}
1102+
if (err == 0) {
1103+
params = EVP_PKEY_new();
1104+
err = params == NULL;
1105+
}
1106+
if (err == 0) {
1107+
err = EVP_PKEY_set1_DH(params, dh) != 1;
1108+
}
1109+
1110+
/* Generate one fresh key pair for both ends. */
1111+
if (err == 0) {
1112+
kgCtx = EVP_PKEY_CTX_new_from_pkey(wpLibCtx, params, NULL);
1113+
err = kgCtx == NULL;
1114+
}
1115+
if (err == 0) {
1116+
err = EVP_PKEY_keygen_init(kgCtx) != 1;
1117+
}
1118+
if (err == 0) {
1119+
err = EVP_PKEY_keygen(kgCtx, &keyA) != 1;
1120+
}
1121+
if (err == 0) {
1122+
err = EVP_PKEY_keygen(kgCtx, &keyB) != 1;
1123+
}
1124+
1125+
/* Snapshot the raw DH shared secret once - it's the same input the KDF
1126+
* branch feeds to wc_X963_KDF regardless of requested output length. */
1127+
if (err == 0) {
1128+
err = test_dh_derive_raw(keyA, keyB, &raw, &rawLen);
1129+
}
1130+
1131+
for (i = 0; (err == 0) && (i < sizeof(cases) / sizeof(cases[0])); ++i) {
1132+
const unsigned char *ukmPtr = cases[i].withUkm ? ukm : NULL;
1133+
size_t ukmLen = cases[i].withUkm ? sizeof(ukm) : 0;
1134+
1135+
memset(wpOut, 0, sizeof(wpOut));
1136+
memset(refOut, 0, sizeof(refOut));
1137+
1138+
err = test_dh_derive_with_x963(keyA, keyB, cases[i].md,
1139+
cases[i].outLen, ukmPtr, ukmLen, wpOut, sizeof(wpOut));
1140+
if (err == 0) {
1141+
err = test_dh_x963_kdf_ref(raw, rawLen, cases[i].md, ukmPtr,
1142+
ukmLen, refOut, cases[i].outLen);
1143+
}
1144+
if (err == 0 && memcmp(wpOut, refOut, cases[i].outLen) != 0) {
1145+
PRINT_ERR_MSG("X9.63 KDF output mismatch (md=%s outLen=%zu ukm=%d)",
1146+
cases[i].md, cases[i].outLen, cases[i].withUkm);
1147+
PRINT_BUFFER("wolfProvider", wpOut, cases[i].outLen);
1148+
PRINT_BUFFER("OpenSSL X963KDF", refOut, cases[i].outLen);
1149+
err = 1;
1150+
}
1151+
/* No bytes beyond the requested length should have been written. */
1152+
if (err == 0) {
1153+
size_t j;
1154+
for (j = cases[i].outLen; j < sizeof(wpOut); ++j) {
1155+
if (wpOut[j] != 0) {
1156+
PRINT_ERR_MSG("KDF wrote past requested length at byte %zu",
1157+
j);
1158+
err = 1;
1159+
break;
1160+
}
1161+
}
1162+
}
1163+
}
1164+
1165+
/* Failure mode: caller's buffer smaller than the requested KDF output.
1166+
* wp_dh_kdf_derive should return failure rather than overflow. */
1167+
if (err == 0) {
1168+
tooSmallLen = sizeof(tooSmallBuf);
1169+
if (test_dh_derive_with_x963(keyA, keyB, "SHA256", 32, NULL, 0,
1170+
tooSmallBuf, tooSmallLen) == 0) {
1171+
PRINT_ERR_MSG("DH X963 KDF derive accepted under-sized buffer");
1172+
err = 1;
1173+
}
1174+
}
1175+
1176+
OPENSSL_free(raw);
1177+
EVP_PKEY_CTX_free(kgCtx);
1178+
EVP_PKEY_free(keyA);
1179+
EVP_PKEY_free(keyB);
1180+
EVP_PKEY_free(params);
1181+
BN_free(p);
1182+
BN_free(g);
1183+
DH_free(dh);
1184+
1185+
return err;
1186+
}
1187+
#endif /* HAVE_X963_KDF && WP_HAVE_SHA256 */
1188+
9151189
#endif /* WP_HAVE_DH */

test/unit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ TEST_CASE test_case[] = {
305305
TEST_DECL(test_dh_decode, NULL),
306306
TEST_DECL(test_dh_krb5_keygen, NULL),
307307
TEST_DECL(test_dh_pad, NULL),
308+
#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256)
309+
TEST_DECL(test_dh_x963_kdf, NULL),
310+
#endif
308311
#ifndef WOLFPROV_QUICKTEST
309312
TEST_DECL(test_dh_get_params, NULL),
310313
#endif

test/unit.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,9 @@ int test_dh_decode(void *data);
309309
int test_dh_get_params(void *data);
310310
int test_dh_krb5_keygen(void *data);
311311
int test_dh_pad(void *data);
312+
#if defined(HAVE_X963_KDF) && defined(WP_HAVE_SHA256)
313+
int test_dh_x963_kdf(void *data);
314+
#endif
312315
#endif /* WP_HAVE_DH */
313316

314317
#ifdef WP_HAVE_ECC

0 commit comments

Comments
 (0)