/* Rexx */
/*-------------------------------------------------------------------*/
/* This sample testcase will create a hybrid quantum safe algorithm */
/* (QSA) key exchange scheme using NIST approved ML-KEM keys. Refer */
/* to the ICSF Application Programmer's Guide for a detailed */
/* description of the callable services used in this sample. */
/*-------------------------------------------------------------------*/
/* The following is required for ML-KEM support: */
/* 1. OA66395 on ICSF HCR77D2 or higher */
/* 2. Crypto Express8 CCA coprocessor with release 8.4 or later */
/* licensed internal code (LIC). */
/*-------------------------------------------------------------------*/
/* STEP 1: Generate ALICE's keys
MLKEM-priv-A, MLKEM-pub-A: ML-KEM (1024) key pair
EC-priv-A, EC-pub-A: ECC key pair for key agreement */
PKB_rule_array = 'QSA-PAIR'||'U-DATENC'
PKB_kvs = '06'x ||, /* ML-KEM */
'00'x ||, /* clear key format skeleton */
'1024'x ||, /* algorithm parameter */
'0000'x ||, /* clear key length */
'0000'x ; /* reserved */
CALL PKB
PKG_rule_array = 'MASTER ' ;
PKG_skeleton_key_id = PKB_token ;
CALL PKG
MLKEM_priv_A = PKG_token ;
PKX_source_key = MLKEM_priv_A ;
CALL PKX
MLKEM_pub_A = PKX_token ;
PKB_rule_array = 'ECC-PAIR'||'KEY-MGMT'
PKB_kvs = '00'x ||, /* ECC Prime curve */
'00'x ||, /* reserved */
'0100'x ||, /* 256 bits */
'0000'x ||, /* pvt key length */
'0000'x ; /* pub key length */
CALL PKB
PKG_rule_array = 'MASTER ' ;
PKG_skeleton_key_id = PKB_token ;
CALL PKG
EC_priv_A = PKG_token ;
PKX_source_key = EC_priv_A ;
CALL PKX
EC_pub_A = PKX_token ;
/* ALICE sends MLKEM-pub-A and EC-pub-A to BOB */
/* STEP 2: Generate BOB's keys
EC-priv-B, EC-pub-B: ECC key pair for key agreement */
PKB_rule_array = 'ECC-PAIR'||'KEY-MGMT'
kvs = '00'x ||, /* Prime curve */
'00'x ||, /* reserved */
'0100'x ||, /* 256 bits */
'0000'x ||, /* pvt key length */
'0000'x ; /* pub key length */
CALL PKB
CALL PKG
EC_priv_B = PKG_token ;
PKX_source_key = PKG_token ;
CALL PKX
EC_pub_B = PKX_token ;
/* STEP 3: Bob uses his own AES CIPHER key and ALICE's ML-KEM public
key to generate an encrypted RANDOM secret key encrypted by his
AES key and Alice's public key. */
AES_ciph_B = , /* BOB's AES CIPHER key */
'010000880500000003012058C870E9D3194F0000000000000000020200000100'x||,
'001A0000000002800002000102C000FF0003E800000002029300F1E4326C2A6E'x||,
'802300A246C4FAEBB08C9FCCD0FB3FF8561D0A031EE7662545E9AC3E1EF4FEB4'x||,
'03A2341B3AD11E52A762E307278566AC9C692154B8BD27A1B6BF16EC301319A1'x||,
'3BE6A6755CEE1FB0'x
PKE_rule_array = 'ZERO-PAD'||'RANDOM '||'AES-ENC ' ;
PKE_keyvalue = '01010101010101010202020202020202'x||,
'00000000000000000000000000000000'x ;
PKE_sym_key_identifier = AES_ciph_B ;
PKE_public_key_identifier = MLKEM_pub_A ;
CALL PKE
/* PKE returns a random 32-byte value encrypted with BOB's AES key
in the keyvalue parameter. The random keyvalue is also encrypted
with ALICE's ML-KEM public key and returned in the
PKA_enciph_keyvalue parameter. */
/* STEP 4: Bob calls EDH using his private ECC key, Alice's public
ECC key, and his own encrypted random symmetric key to generate
a shared secret key. */
EDH_rule_array = 'DERIV01 '||'KEY-AES '||'QSA-ECDH'||'IHKEYAES' ;
EDH_priv_key_id = EC_priv_B ;
EDH_priv_kek_id = '' ;
EDH_publ_key_id = EC_pub_A ;
EDH_hybrid_key_id = AES_ciph_B ;
EDH_party_identifier = 'ALICE01234BOB56789' ;
EDH_key_bit_length = d2c(256,4) ;
EDH_initial_vector = '01010101010101010202020202020202'x ;
EDH_hybrid_ciphertext = PKE_keyvalue ;
EDH_output_kek_id = '' ;
EDH_output_key_id = AES_CIPHER_skeleton ;
CALL EDH
/* calculate VP of BOB's secret key */
KYT2_rule_array = 'AES '||'GENERATE'||'CMACZERO' ;
KYT2_key_identifier = EDH_output_key_id ;
CALL KYT2 ;
BOB_VP = KYT2_VP ;
/* STEP 5: ALICE calls EDH on her side with her ECC private key,
BOB's ECC public key, and her own ML-KEM private key to
generate a shared secret key. */
EDH_rule_array = 'DERIV01 '||'KEY-AES '||'QSA-ECDH'||'IHKEYKYB'
EDH_priv_key_id = EC_priv_A ;
EDH_priv_kek_id = '' ;
EDH_publ_key_id = EC_pub_B ;
EDH_hybrid_key_id = MLKEM_priv_A ;
EDH_party_identifier = 'ALICE01234BOB56789' ;
EDH_key_bit_length = d2c(256,4) ;
EDH_initial_vector = '' ;
EDH_hybrid_ciphertext = PKE_PKA_enciph_keyvalue ; /* output from PKE */
EDH_output_kek_id = '' ;
EDH_output_key_id = AES_CIPHER_skeleton ;
CALL EDH
/* calculate VP of ALICE's secret key */
KYT2_rule_array = 'AES '||'GENERATE'||'CMACZERO' ;
KYT2_key_identifier = EDH_output_key_id ;
CALL KYT2
ALICE_VP = KYT2_VP ;
/* Step 6: Check that the VP of BOB's secret key matches the VP of
ALICE's secret key to ensure both keys match. */
IF ALICE_VP = BOB_VP THEN
SAY 'BOB and ALICE shared secret VPs match. SUCCESS!!'
/*-------------------------------------------------------------------*/
GETOUT:
EXIT
/*-------------------------------------------------------------------*/
/* PKA Key Token Build */
/*-------------------------------------------------------------------*/
PKB:
PKB_rc = 'FFFFFFFF'x ;
PKB_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x ;
exit_data = '' ;
PKB_rule_count = d2c(length(PKB_rule_array)/8,4) ;
PKB_kvs_length = d2c(length(PKB_kvs),4) ;
PKB_private_name_length = '00000000'x ;
PKB_private_name = '' ;
PKB_user_assoc_data_length = '00000000'x ;
PKB_user_assoc_data = '' ;
PKB_key_deriv_data_length = '00000000'x ;
PKB_key_deriv_data = '' ;
PKB_reserved_field3_length = '00000000'x ;
PKB_reserved_field3 = '' ;
PKB_reserved_field4_length = '00000000'x ;
PKB_reserved_field4 = '' ;
PKB_reserved_field5_length = '00000000'x ;
PKB_reserved_field5 = '' ;
PKB_token_length = d2c(8000,4) ; /* max */
PKB_token = d2c(0,8000) ;
ADDRESS LINKPGM 'CSNDPKB' ,
'PKB_rc' 'PKB_rs' ,
'exit_data_length' 'exit_data' ,
'PKB_rule_count' 'PKB_rule_array' ,
'PKB_kvs_length' 'PKB_kvs' ,
'PKB_private_name_length' 'PKB_private_name' ,
'PKB_user_assoc_data_length' 'PKB_user_assoc_data' ,
'PKB_key_deriv_data_length' 'PKB_key_deriv_data' ,
'PKB_reserved_field3_length' 'PKB_reserved_field3' ,
'PKB_reserved_field4_length' 'PKB_reserved_field4' ,
'PKB_reserved_field5_length' 'PKB_reserved_field5' ,
'PKB_token_length' 'PKB_token' ;
SAY 'PKB: rc =' c2x(PKB_rc) 'rs =' c2x(PKB_rs) ;
IF PKB_rc \= '00000000'x THEN
DO
SAY 'PKB FAILED' ;
SIGNAL GETOUT ;
END
ELSE
DO
PKB_token = substr(PKB_token,1,c2d(PKB_token_length)) ;
SAY 'PKB token length:' c2x(PKB_token_length) ;
SAY 'PKB token:' ;
SAY c2x(PKB_token) ;
END
SAY
RETURN
/*-------------------------------------------------------------------*/
/* PKA Key Generate */
/*-------------------------------------------------------------------*/
PKG:
PKG_rc = 'FFFFFFFF'x ;
PKG_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x ;
exit_data = '' ;
PKG_rule_array_count = d2c(length(PKG_rule_array)/8,4) ;
PKG_regen_data_length = '00000000'x ;
PKG_regen_data = '' ;
PKG_skeleton_key_id_length = d2c(length(PKG_skeleton_key_id),4) ;
PKG_transport_key_id = ''
PKG_token_length = d2c(8000,4) ;
PKG_token = d2c(0,8000) ;
ADDRESS LINKPGM 'CSNDPKG' ,
'PKG_rc' 'PKG_rs' ,
'exit_data_length' 'exit_data' ,
'PKG_rule_array_count' 'PKG_rule_array' ,
'PKG_regen_data_length' 'PKG_regen_data' ,
'PKG_skeleton_key_id_length' 'PKG_skeleton_key_id' ,
'PKG_transport_key_id' ,
'PKG_token_length' 'PKG_token' ;
SAY 'PKG: rc =' c2x(PKG_rc) 'rs =' c2x(PKG_rs) ;
IF PKG_rc \= '00000000'x THEN
DO
SAY 'PKG FAILED' ;
SIGNAL GETOUT ;
END
ELSE
DO
PKG_token = SUBSTR(PKG_token,1,c2d(PKG_token_length)) ;
SAY 'PKG generated token length:' c2x(PKG_token_length) ;
SAY 'PKG generated token:' ;
SAY c2x(PKG_token) ;
END
SAY
RETURN
/*-------------------------------------------------------------------*/
/* PKA Public Key Extract */
/*-------------------------------------------------------------------*/
PKX:
PKX_rc = 'FFFFFFFF'x ;
PKX_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x ;
exit_data = '' ;
PKX_rule_array_count = '00000000'x ;
PKX_rule_array = '' ;
PKX_source_key_length = d2c(length(PKX_source_key),4) ;
PKX_token_length = d2c(8000,4) ;
PKX_token = copies('00'x,8000) ;
ADDRESS LINKPGM 'CSNDPKX' ,
'PKX_rc' ,
'PKX_rs' ,
'exit_data_length' ,
'exit_data' ,
'PKX_rule_array_count' ,
'PKX_rule_array' ,
'PKX_source_key_length' ,
'PKX_source_key' ,
'PKX_token_length' ,
'PKX_token' ;
SAY 'PKX: rc =' c2x(PKX_rc) 'rs =' c2x(PKX_rs)
IF PKX_rc /= '00000000'x THEN
DO ;
SAY 'PKX FAILED' ;
SIGNAL GETOUT ;
END ;
ELSE
DO ;
PKX_token = substr(PKX_token,1,c2d(PKX_token_length)) ;
SAY 'PKX public key token length:' c2x(PKX_token_length) ;
SAY 'PKX token:' ;
SAY c2x(PKX_token)
END
SAY
RETURN
/*-------------------------------------------------------------------*/
/* PKA Encrypt */
/*-------------------------------------------------------------------*/
PKE:
PKE_rc = 'FFFFFFFF'x ;
PKE_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x
exit_data = ''
PKE_rule_array_count = d2c(length(PKE_rule_array)/8,4) ;
PKE_keyvalue_length = d2c(length(PKE_keyvalue),4) ;
PKE_sym_key_identifier_length = d2c(length(PKE_sym_key_identifier),4) ;
PKE_public_key_identifier_length = ,
d2c(length(PKE_public_key_identifier),4) ;
PKE_PKA_enciph_keyvalue_length = d2c(1568,4) ;
PKE_PKA_enciph_keyvalue = d2c(0,1568) ;
ADDRESS LINKPGM 'CSNDPKE' ,
'PKE_rc' ,
'PKE_rs' ,
'exit_data_length' ,
'exit_data' ,
'PKE_rule_array_count' ,
'PKE_rule_array' ,
'PKE_keyvalue_length' ,
'PKE_keyvalue' ,
'PKE_sym_key_identifier_length' ,
'PKE_sym_key_identifier' ,
'PKE_public_key_identifier_length' ,
'PKE_public_key_identifier' ,
'PKE_PKA_enciph_keyvalue_length' ,
'PKE_PKA_enciph_keyvalue' ;
SAY 'PKE: rc =' c2x(PKE_rc) 'rs =' c2x(PKE_rs)
IF PKE_rc /= '00000000'x THEN
DO
SAY 'PKE FAILED' ;
SIGNAL GETOUT ;
END
ELSE
DO
PKE_PKA_enciph_keyvalue = ,
substr(PKE_PKA_enciph_keyvalue,1,,
c2d(PKE_PKA_enciph_keyvalue_length))
SAY 'PKE_PKA_enciph_keyvalue_length:' ,
c2x(PKE_PKA_enciph_keyvalue_length) ;
SAY 'PKE_PKA_enciph_keyvalue:' ;
SAY c2x(PKE_PKA_enciph_keyvalue) ;
SAY
SAY 'PKE_keyvalue_length:' c2x(PKE_keyvalue_length) ;
SAY 'PKE_keyvalue:' ;
SAY c2x(PKE_keyvalue)
END
SAY
RETURN
/*-------------------------------------------------------------------*/
/* ECC Diffie-Hellman */
/*-------------------------------------------------------------------*/
EDH:
EDH_rc = 'FFFFFFFF'x ;
EDH_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x
exit_data = ''
EDH_rule_array_count = d2c(length(EDH_rule_array)/8,4) ;
EDH_priv_key_id_length = d2c(length(EDH_priv_key_id),4) ;
EDH_priv_kek_id_length = d2c(length(EDH_priv_KEK_id),4) ;
EDH_publ_key_id_length = d2c(length(EDH_publ_key_id),4) ;
EDH_hybrid_key_id_length = d2c(length(EDH_hybrid_key_id),4) ;
EDH_party_identifier_length = d2c(length(EDH_party_identifier),4) ;
EDH_initial_vector_length = d2c(length(EDH_initial_vector),4) ;
EDH_hybrid_ciphertext_length = d2c(length(EDH_hybrid_ciphertext),4) ;
EDH_reserved3_length = '00000000'x ;
EDH_reserved3 = '' ;
EDH_reserved4_length = '00000000'x ;
EDH_reserved4 = '' ;
EDH_reserved5_length = '00000000'x ;
EDH_reserved5 = '' ;
EDH_output_kek_id_length = d2c(length(EDH_output_KEK_id),4)
AES_CIPHER_skeleton = , /* created using CSNBKTB2 */
'0100003805000000000000000000000000000000000000000000020200000100'x||,
'001A0000000000000002000102C000000003E00000000000'x ;
EDH_output_key_id_length = d2c(9992,4) ;
EDH_output_key_id = left(AES_CIPHER_skeleton,9992) ;
ADDRESS LINKPGM 'CSNDEDH' ,
'EDH_rc' ,
'EDH_rs' ,
'exit_data_length' ,
'exit_data' ,
'EDH_rule_array_count' ,
'EDH_rule_array' ,
'EDH_priv_key_id_length' ,
'EDH_priv_key_id' ,
'EDH_priv_kek_id_length' ,
'EDH_priv_kek_id' ,
'EDH_publ_key_id_length' ,
'EDH_publ_key_id' ,
'EDH_hybrid_key_id_length' ,
'EDH_hybrid_key_id' ,
'EDH_party_identifier_length' ,
'EDH_party_identifier' ,
'EDH_key_bit_length' ,
'EDH_initial_vector_length' ,
'EDH_initial_vector' ,
'EDH_hybrid_ciphertext_length' ,
'EDH_hybrid_ciphertext' ,
'EDH_reserved3_length' ,
'EDH_reserved3' ,
'EDH_reserved4_length' ,
'EDH_reserved4' ,
'EDH_reserved5_length' ,
'EDH_reserved5' ,
'EDH_output_kek_id_length' ,
'EDH_output_kek_id' ,
'EDH_output_key_id_length' ,
'EDH_output_key_id' ;
SAY 'EDH: rc =' c2x(EDH_rc) 'rs =' c2x(EDH_rs) ;
IF EDH_rc /= '00000000'x THEN
DO
SAY 'EDH FAILED' ;
SIGNAL GETOUT ;
END
ELSE
DO
EDH_output_key_id = ,
substr(EDH_output_key_id,1,c2d(EDH_output_key_id_length)) ;
SAY 'EDH output_key_identifier_length:' ,
c2x(EDH_output_key_id_length) ;
SAY 'EDH output_key_identifier:' ;
SAY c2x(EDH_output_key_id) ;
END
SAY
RETURN
/*-------------------------------------------------------------------*/
/* Key Test2 */
/*-------------------------------------------------------------------*/
KYT2:
KYT2_rc = 'FFFFFFFF'x ;
KYT2_rs = 'FFFFFFFF'x ;
exit_data_length = '00000000'x
exit_data = ''
KYT2_rule_array_count = d2c(length(KYT2_rule_array)/8,4) ;
KYT2_key_identifier_length = d2c(length(KYT2_key_identifier),4) ;
KYT2_kek_identifier_length = '00000000'x ;
KYT2_kek_identifier = ''
KYT2_reserved_length = d2c(0,4) ;
KYT2_reserved = 'ignore me' ;
KYT2_VP_length = d2c(8,4) ;
KYT2_VP = d2c(0,c2d(KYT2_VP_length)) ;
ADDRESS LINKPGM 'CSNBKYT2' ,
'KYT2_rc' 'KYT2_rs' ,
'exit_data_length' 'exit_data' ,
'KYT2_rule_array_count' 'KYT2_rule_array' ,
'KYT2_key_identifier_length' 'KYT2_key_identifier' ,
'KYT2_kek_identifier_length' 'KYT2_kek_identifier' ,
'KYT2_reserved_length' 'KYT2_reserved' ,
'KYT2_VP_length' 'KYT2_VP' ;
IF KYT2_rc /= '00000000'x THEN
DO
SAY 'KYT2 failed: rc =' c2x(KYT2_rc) 'rs =' c2x(KYT2_rs) ;
SIGNAL GETOUT ;
END
ELSE
DO
SAY 'KYT2 VP length:' c2x(KYT2_VP_length) ;
SAY 'KYT2 VP:' c2x(KYT2_VP) ;
END
SAY
RETURN