@@ -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 */
0 commit comments