aboutsummaryrefslogtreecommitdiff
path: root/crypto/openssl/providers/implementations
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/openssl/providers/implementations')
-rw-r--r--crypto/openssl/providers/implementations/asymciphers/build.info11
-rw-r--r--crypto/openssl/providers/implementations/asymciphers/rsa_enc.c648
-rw-r--r--crypto/openssl/providers/implementations/asymciphers/sm2_enc.c234
-rw-r--r--crypto/openssl/providers/implementations/build.info2
-rw-r--r--crypto/openssl/providers/implementations/ciphers/build.info208
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes.c93
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes.h61
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c442
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h65
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c795
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c846
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c72
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h48
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c73
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc38
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc60
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc71
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc268
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc36
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc94
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c68
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h45
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c155
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc47
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc108
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc155
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc63
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc118
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc312
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc52
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc204
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c316
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h76
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c372
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c95
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c163
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc84
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc34
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc102
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc135
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc204
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc96
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c568
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h39
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c209
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c296
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h36
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c139
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c335
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c318
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h60
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c23
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c333
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc167
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria.c84
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria.h30
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c59
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h22
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c40
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c59
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h22
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c37
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c52
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c58
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h24
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c42
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_camellia.c92
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_camellia.h30
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc94
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c74
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc84
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_cast.h24
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_cast5.c59
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c42
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c226
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h34
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c122
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c367
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h43
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c412
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_cts.c377
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_cts.h52
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_des.c201
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_des.h33
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c197
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_desx.c21
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c79
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_idea.c57
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_idea.h24
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c63
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_null.c197
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc2.c295
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc2.h28
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c43
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4.c119
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4.h21
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c245
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h36
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c233
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c44
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc5.c186
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc5.h25
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c41
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_seed.c56
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_seed.h24
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c42
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4.c52
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4.h26
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c54
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h23
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c69
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc41
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c55
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h22
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c99
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc42
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c149
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc52
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c281
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h46
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c99
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc43
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes.c25
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes.h110
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c206
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c35
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h25
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c148
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c98
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c209
-rw-r--r--crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c20
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon.c736
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c173
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c456
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c69
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c596
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c69
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c194
-rw-r--r--crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h16
-rw-r--r--crypto/openssl/providers/implementations/digests/blake2_impl.h118
-rw-r--r--crypto/openssl/providers/implementations/digests/blake2_prov.c198
-rw-r--r--crypto/openssl/providers/implementations/digests/blake2b_prov.c334
-rw-r--r--crypto/openssl/providers/implementations/digests/blake2s_prov.c322
-rw-r--r--crypto/openssl/providers/implementations/digests/build.info62
-rw-r--r--crypto/openssl/providers/implementations/digests/digestcommon.c54
-rw-r--r--crypto/openssl/providers/implementations/digests/md2_prov.c24
-rw-r--r--crypto/openssl/providers/implementations/digests/md4_prov.c24
-rw-r--r--crypto/openssl/providers/implementations/digests/md5_prov.c24
-rw-r--r--crypto/openssl/providers/implementations/digests/md5_sha1_prov.c61
-rw-r--r--crypto/openssl/providers/implementations/digests/mdc2_prov.c61
-rw-r--r--crypto/openssl/providers/implementations/digests/null_prov.c52
-rw-r--r--crypto/openssl/providers/implementations/digests/ripemd_prov.c24
-rw-r--r--crypto/openssl/providers/implementations/digests/sha2_prov.c98
-rw-r--r--crypto/openssl/providers/implementations/digests/sha3_prov.c700
-rw-r--r--crypto/openssl/providers/implementations/digests/sm3_prov.c18
-rw-r--r--crypto/openssl/providers/implementations/digests/wp_prov.c24
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/build.info32
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_der2key.c1307
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c202
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c289
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c282
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c287
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c169
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/encode_key2any.c1764
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c179
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c234
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/encode_key2text.c737
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/endecoder_common.c104
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/endecoder_local.h28
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c92
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h98
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c449
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h39
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c488
-rw-r--r--crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h39
-rw-r--r--crypto/openssl/providers/implementations/exchange/build.info31
-rw-r--r--crypto/openssl/providers/implementations/exchange/dh_exch.c562
-rw-r--r--crypto/openssl/providers/implementations/exchange/ecdh_exch.c650
-rw-r--r--crypto/openssl/providers/implementations/exchange/ecx_exch.c227
-rw-r--r--crypto/openssl/providers/implementations/exchange/kdf_exch.c257
-rw-r--r--crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H22
-rw-r--r--crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H26
-rw-r--r--crypto/openssl/providers/implementations/include/prov/blake2.h138
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ciphercommon.h375
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h58
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h106
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h133
-rw-r--r--crypto/openssl/providers/implementations/include/prov/decoders.h20
-rw-r--r--crypto/openssl/providers/implementations/include/prov/digestcommon.h133
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ecx.h31
-rw-r--r--crypto/openssl/providers/implementations/include/prov/hmac_drbg.h33
-rw-r--r--crypto/openssl/providers/implementations/include/prov/implementations.h849
-rw-r--r--crypto/openssl/providers/implementations/include/prov/kdfexchange.h23
-rw-r--r--crypto/openssl/providers/implementations/include/prov/macsignature.h29
-rw-r--r--crypto/openssl/providers/implementations/include/prov/md5_sha1.h36
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ml_dsa.h14
-rw-r--r--crypto/openssl/providers/implementations/include/prov/ml_kem.h14
-rw-r--r--crypto/openssl/providers/implementations/include/prov/mlx_kem.h47
-rw-r--r--crypto/openssl/providers/implementations/include/prov/names.h435
-rw-r--r--crypto/openssl/providers/implementations/include/prov/seeding.h30
-rw-r--r--crypto/openssl/providers/implementations/kdfs/argon2.c1560
-rw-r--r--crypto/openssl/providers/implementations/kdfs/build.info46
-rw-r--r--crypto/openssl/providers/implementations/kdfs/hkdf.c954
-rw-r--r--crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c263
-rw-r--r--crypto/openssl/providers/implementations/kdfs/kbkdf.c525
-rw-r--r--crypto/openssl/providers/implementations/kdfs/krb5kdf.c497
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pbkdf1.c270
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pbkdf2.c467
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pbkdf2.h14
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c20
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c319
-rw-r--r--crypto/openssl/providers/implementations/kdfs/pvkkdf.c248
-rw-r--r--crypto/openssl/providers/implementations/kdfs/scrypt.c549
-rw-r--r--crypto/openssl/providers/implementations/kdfs/sshkdf.c411
-rw-r--r--crypto/openssl/providers/implementations/kdfs/sskdf.c786
-rw-r--r--crypto/openssl/providers/implementations/kdfs/tls1_prf.c585
-rw-r--r--crypto/openssl/providers/implementations/kdfs/x942kdf.c679
-rw-r--r--crypto/openssl/providers/implementations/kem/build.info26
-rw-r--r--crypto/openssl/providers/implementations/kem/ec_kem.c815
-rw-r--r--crypto/openssl/providers/implementations/kem/eckem.h13
-rw-r--r--crypto/openssl/providers/implementations/kem/ecx_kem.c705
-rw-r--r--crypto/openssl/providers/implementations/kem/kem_util.c37
-rw-r--r--crypto/openssl/providers/implementations/kem/ml_kem_kem.c268
-rw-r--r--crypto/openssl/providers/implementations/kem/mlx_kem.c341
-rw-r--r--crypto/openssl/providers/implementations/kem/rsa_kem.c449
-rw-r--r--crypto/openssl/providers/implementations/kem/template_kem.c193
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/build.info65
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c912
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c748
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c1536
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc109
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c1292
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c102
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c573
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c595
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c859
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c820
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c744
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c480
-rw-r--r--crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c440
-rw-r--r--crypto/openssl/providers/implementations/macs/blake2_mac_impl.c254
-rw-r--r--crypto/openssl/providers/implementations/macs/blake2b_mac.c33
-rw-r--r--crypto/openssl/providers/implementations/macs/blake2s_mac.c32
-rw-r--r--crypto/openssl/providers/implementations/macs/build.info30
-rw-r--r--crypto/openssl/providers/implementations/macs/cmac_prov.c307
-rw-r--r--crypto/openssl/providers/implementations/macs/gmac_prov.c259
-rw-r--r--crypto/openssl/providers/implementations/macs/hmac_prov.c404
-rw-r--r--crypto/openssl/providers/implementations/macs/kmac_prov.c697
-rw-r--r--crypto/openssl/providers/implementations/macs/poly1305_prov.c187
-rw-r--r--crypto/openssl/providers/implementations/macs/siphash_prov.c237
-rw-r--r--crypto/openssl/providers/implementations/rands/build.info11
-rw-r--r--crypto/openssl/providers/implementations/rands/drbg.c1030
-rw-r--r--crypto/openssl/providers/implementations/rands/drbg_ctr.c857
-rw-r--r--crypto/openssl/providers/implementations/rands/drbg_hash.c655
-rw-r--r--crypto/openssl/providers/implementations/rands/drbg_hmac.c579
-rw-r--r--crypto/openssl/providers/implementations/rands/drbg_local.h243
-rw-r--r--crypto/openssl/providers/implementations/rands/fips_crng_test.c428
-rw-r--r--crypto/openssl/providers/implementations/rands/seed_src.c249
-rw-r--r--crypto/openssl/providers/implementations/rands/seed_src_jitter.c359
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/build.info15
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c67
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c107
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c48
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_unix.c802
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_vms.c616
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c146
-rw-r--r--crypto/openssl/providers/implementations/rands/seeding/rand_win.c163
-rw-r--r--crypto/openssl/providers/implementations/rands/test_rng.c355
-rw-r--r--crypto/openssl/providers/implementations/signature/build.info45
-rw-r--r--crypto/openssl/providers/implementations/signature/dsa_sig.c1085
-rw-r--r--crypto/openssl/providers/implementations/signature/ecdsa_sig.c1108
-rw-r--r--crypto/openssl/providers/implementations/signature/eddsa_sig.c1146
-rw-r--r--crypto/openssl/providers/implementations/signature/mac_legacy_sig.c265
-rw-r--r--crypto/openssl/providers/implementations/signature/ml_dsa_sig.c368
-rw-r--r--crypto/openssl/providers/implementations/signature/rsa_sig.c2144
-rw-r--r--crypto/openssl/providers/implementations/signature/slh_dsa_sig.c388
-rw-r--r--crypto/openssl/providers/implementations/signature/sm2_sig.c585
-rw-r--r--crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c52
-rw-r--r--crypto/openssl/providers/implementations/skeymgmt/build.info8
-rw-r--r--crypto/openssl/providers/implementations/skeymgmt/generic.c93
-rw-r--r--crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h19
-rw-r--r--crypto/openssl/providers/implementations/storemgmt/build.info9
-rw-r--r--crypto/openssl/providers/implementations/storemgmt/file_store.c829
-rw-r--r--crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c314
-rw-r--r--crypto/openssl/providers/implementations/storemgmt/file_store_local.h11
-rw-r--r--crypto/openssl/providers/implementations/storemgmt/winstore_store.c343
286 files changed, 70594 insertions, 0 deletions
diff --git a/crypto/openssl/providers/implementations/asymciphers/build.info b/crypto/openssl/providers/implementations/asymciphers/build.info
new file mode 100644
index 000000000000..dbca47368488
--- /dev/null
+++ b/crypto/openssl/providers/implementations/asymciphers/build.info
@@ -0,0 +1,11 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$RSA_GOAL=../../libdefault.a ../../libfips.a
+$SM2_GOAL=../../libdefault.a
+
+SOURCE[$RSA_GOAL]=rsa_enc.c
+
+IF[{- !$disabled{"sm2"} -}]
+ SOURCE[$SM2_GOAL]=sm2_enc.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c b/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c
new file mode 100644
index 000000000000..e6b676d0f8fa
--- /dev/null
+++ b/crypto/openssl/providers/implementations/asymciphers/rsa_enc.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/rsa.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+/* Just for SSL_MAX_MASTER_KEY_LENGTH */
+#include <openssl/prov_ssl.h>
+#include "internal/constant_time.h"
+#include "internal/sizes.h"
+#include "crypto/rsa.h"
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/securitycheck.h"
+#include <stdlib.h>
+
+static OSSL_FUNC_asym_cipher_newctx_fn rsa_newctx;
+static OSSL_FUNC_asym_cipher_encrypt_init_fn rsa_encrypt_init;
+static OSSL_FUNC_asym_cipher_encrypt_fn rsa_encrypt;
+static OSSL_FUNC_asym_cipher_decrypt_init_fn rsa_decrypt_init;
+static OSSL_FUNC_asym_cipher_decrypt_fn rsa_decrypt;
+static OSSL_FUNC_asym_cipher_freectx_fn rsa_freectx;
+static OSSL_FUNC_asym_cipher_dupctx_fn rsa_dupctx;
+static OSSL_FUNC_asym_cipher_get_ctx_params_fn rsa_get_ctx_params;
+static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn rsa_gettable_ctx_params;
+static OSSL_FUNC_asym_cipher_set_ctx_params_fn rsa_set_ctx_params;
+static OSSL_FUNC_asym_cipher_settable_ctx_params_fn rsa_settable_ctx_params;
+
+static OSSL_ITEM padding_item[] = {
+ { RSA_PKCS1_PADDING, OSSL_PKEY_RSA_PAD_MODE_PKCSV15 },
+ { RSA_NO_PADDING, OSSL_PKEY_RSA_PAD_MODE_NONE },
+ { RSA_PKCS1_OAEP_PADDING, OSSL_PKEY_RSA_PAD_MODE_OAEP }, /* Correct spelling first */
+ { RSA_PKCS1_OAEP_PADDING, "oeap" },
+ { 0, NULL }
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes RSA structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ RSA *rsa;
+ int pad_mode;
+ int operation;
+ /* OAEP message digest */
+ EVP_MD *oaep_md;
+ /* message digest for MGF1 */
+ EVP_MD *mgf1_md;
+ /* OAEP label */
+ unsigned char *oaep_label;
+ size_t oaep_labellen;
+ /* TLS padding */
+ unsigned int client_version;
+ unsigned int alt_version;
+ /* PKCS#1 v1.5 decryption mode */
+ unsigned int implicit_rejection;
+ OSSL_FIPS_IND_DECLARE
+} PROV_RSA_CTX;
+
+static void *rsa_newctx(void *provctx)
+{
+ PROV_RSA_CTX *prsactx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX));
+ if (prsactx == NULL)
+ return NULL;
+ prsactx->libctx = PROV_LIBCTX_OF(provctx);
+ OSSL_FIPS_IND_INIT(prsactx)
+
+ return prsactx;
+}
+
+static int rsa_init(void *vprsactx, void *vrsa, const OSSL_PARAM params[],
+ int operation, const char *desc)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int protect = 0;
+
+ if (!ossl_prov_is_running() || prsactx == NULL || vrsa == NULL)
+ return 0;
+
+ if (!ossl_rsa_key_op_get_protect(vrsa, operation, &protect))
+ return 0;
+ if (!RSA_up_ref(vrsa))
+ return 0;
+ RSA_free(prsactx->rsa);
+ prsactx->rsa = vrsa;
+ prsactx->operation = operation;
+ prsactx->implicit_rejection = 1;
+
+ switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ prsactx->pad_mode = RSA_PKCS1_PADDING;
+ break;
+ default:
+ /* This should not happen due to the check above */
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ OSSL_FIPS_IND_SET_APPROVED(prsactx)
+ if (!rsa_set_ctx_params(prsactx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx),
+ OSSL_FIPS_IND_SETTABLE0, prsactx->libctx,
+ prsactx->rsa, desc, protect))
+ return 0;
+#endif
+ return 1;
+}
+
+static int rsa_encrypt_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ return rsa_init(vprsactx, vrsa, params, EVP_PKEY_OP_ENCRYPT,
+ "RSA Encrypt Init");
+}
+
+static int rsa_decrypt_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ return rsa_init(vprsactx, vrsa, params, EVP_PKEY_OP_DECRYPT,
+ "RSA Decrypt Init");
+}
+
+static int rsa_encrypt(void *vprsactx, unsigned char *out, size_t *outlen,
+ size_t outsize, const unsigned char *in, size_t inlen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ size_t len = RSA_size(prsactx->rsa);
+ int ret;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef FIPS_MODULE
+ if ((prsactx->pad_mode == RSA_PKCS1_PADDING
+ || prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING)
+ && !OSSL_FIPS_IND_ON_UNAPPROVED(prsactx, OSSL_FIPS_IND_SETTABLE1,
+ prsactx->libctx, "RSA Encrypt",
+ "PKCS#1 v1.5 padding",
+ ossl_fips_config_rsa_pkcs15_padding_disabled)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE);
+ return 0;
+ }
+#endif
+
+ if (len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ if (out == NULL) {
+ *outlen = len;
+ return 1;
+ }
+
+ if (outsize < len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+ int rsasize = RSA_size(prsactx->rsa);
+ unsigned char *tbuf;
+
+ if ((tbuf = OPENSSL_malloc(rsasize)) == NULL)
+ return 0;
+ if (prsactx->oaep_md == NULL) {
+ prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA-1", NULL);
+ if (prsactx->oaep_md == NULL) {
+ OPENSSL_free(tbuf);
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ }
+ ret =
+ ossl_rsa_padding_add_PKCS1_OAEP_mgf1_ex(prsactx->libctx, tbuf,
+ rsasize, in, inlen,
+ prsactx->oaep_label,
+ prsactx->oaep_labellen,
+ prsactx->oaep_md,
+ prsactx->mgf1_md);
+
+ if (!ret) {
+ OPENSSL_free(tbuf);
+ return 0;
+ }
+ ret = RSA_public_encrypt(rsasize, tbuf, out, prsactx->rsa,
+ RSA_NO_PADDING);
+ OPENSSL_free(tbuf);
+ } else {
+ ret = RSA_public_encrypt(inlen, in, out, prsactx->rsa,
+ prsactx->pad_mode);
+ }
+ /* A ret value of 0 is not an error */
+ if (ret < 0)
+ return ret;
+ *outlen = ret;
+ return 1;
+}
+
+static int rsa_decrypt(void *vprsactx, unsigned char *out, size_t *outlen,
+ size_t outsize, const unsigned char *in, size_t inlen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int ret;
+ int pad_mode;
+ size_t len = RSA_size(prsactx->rsa);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) {
+ if (out == NULL) {
+ *outlen = SSL_MAX_MASTER_KEY_LENGTH;
+ return 1;
+ }
+ if (outsize < SSL_MAX_MASTER_KEY_LENGTH) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
+ return 0;
+ }
+ } else {
+ if (out == NULL) {
+ if (len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+ *outlen = len;
+ return 1;
+ }
+
+ if (outsize < len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
+ return 0;
+ }
+ }
+
+ if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING
+ || prsactx->pad_mode == RSA_PKCS1_WITH_TLS_PADDING) {
+ unsigned char *tbuf;
+
+ if ((tbuf = OPENSSL_malloc(len)) == NULL)
+ return 0;
+ ret = RSA_private_decrypt(inlen, in, tbuf, prsactx->rsa,
+ RSA_NO_PADDING);
+ /*
+ * With no padding then, on success ret should be len, otherwise an
+ * error occurred (non-constant time)
+ */
+ if (ret != (int)len) {
+ OPENSSL_free(tbuf);
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_DECRYPT);
+ return 0;
+ }
+ if (prsactx->pad_mode == RSA_PKCS1_OAEP_PADDING) {
+ if (prsactx->oaep_md == NULL) {
+ prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA-1", NULL);
+ if (prsactx->oaep_md == NULL) {
+ OPENSSL_free(tbuf);
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ }
+ ret = RSA_padding_check_PKCS1_OAEP_mgf1(out, outsize, tbuf,
+ len, len,
+ prsactx->oaep_label,
+ prsactx->oaep_labellen,
+ prsactx->oaep_md,
+ prsactx->mgf1_md);
+ } else {
+ /* RSA_PKCS1_WITH_TLS_PADDING */
+ if (prsactx->client_version <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_TLS_CLIENT_VERSION);
+ OPENSSL_free(tbuf);
+ return 0;
+ }
+ ret = ossl_rsa_padding_check_PKCS1_type_2_TLS(
+ prsactx->libctx, out, outsize, tbuf, len,
+ prsactx->client_version, prsactx->alt_version);
+ }
+ OPENSSL_free(tbuf);
+ } else {
+ if ((prsactx->implicit_rejection == 0) &&
+ (prsactx->pad_mode == RSA_PKCS1_PADDING))
+ pad_mode = RSA_PKCS1_NO_IMPLICIT_REJECT_PADDING;
+ else
+ pad_mode = prsactx->pad_mode;
+ ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, pad_mode);
+ }
+ *outlen = constant_time_select_s(constant_time_msb_s(ret), *outlen, ret);
+ ret = constant_time_select_int(constant_time_msb(ret), 0, 1);
+ return ret;
+}
+
+static void rsa_freectx(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ RSA_free(prsactx->rsa);
+
+ EVP_MD_free(prsactx->oaep_md);
+ EVP_MD_free(prsactx->mgf1_md);
+ OPENSSL_free(prsactx->oaep_label);
+
+ OPENSSL_free(prsactx);
+}
+
+static void *rsa_dupctx(void *vprsactx)
+{
+ PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx;
+ PROV_RSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) {
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ if (dstctx->oaep_md != NULL && !EVP_MD_up_ref(dstctx->oaep_md)) {
+ RSA_free(dstctx->rsa);
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ if (dstctx->mgf1_md != NULL && !EVP_MD_up_ref(dstctx->mgf1_md)) {
+ RSA_free(dstctx->rsa);
+ EVP_MD_free(dstctx->oaep_md);
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ return dstctx;
+}
+
+static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ OSSL_PARAM *p;
+
+ if (prsactx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+ if (p != NULL)
+ switch (p->data_type) {
+ case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */
+ if (!OSSL_PARAM_set_int(p, prsactx->pad_mode))
+ return 0;
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ {
+ int i;
+ const char *word = NULL;
+
+ for (i = 0; padding_item[i].id != 0; i++) {
+ if (prsactx->pad_mode == (int)padding_item[i].id) {
+ word = padding_item[i].ptr;
+ break;
+ }
+ }
+
+ if (word != NULL) {
+ if (!OSSL_PARAM_set_utf8_string(p, word))
+ return 0;
+ } else {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->oaep_md == NULL
+ ? ""
+ : EVP_MD_get0_name(prsactx->oaep_md)))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST);
+ if (p != NULL) {
+ EVP_MD *mgf1_md = prsactx->mgf1_md == NULL ? prsactx->oaep_md
+ : prsactx->mgf1_md;
+
+ if (!OSSL_PARAM_set_utf8_string(p, mgf1_md == NULL
+ ? ""
+ : EVP_MD_get0_name(mgf1_md)))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL);
+ if (p != NULL &&
+ !OSSL_PARAM_set_octet_ptr(p, prsactx->oaep_label,
+ prsactx->oaep_labellen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->client_version))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->alt_version))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->implicit_rejection))
+ return 0;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(prsactx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0),
+ OSSL_PARAM_DEFN(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, OSSL_PARAM_OCTET_PTR,
+ NULL, 0),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_gettable_ctx_params(ossl_unused void *vprsactx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ const OSSL_PARAM *p;
+ char mdname[OSSL_MAX_NAME_SIZE];
+ char mdprops[OSSL_MAX_PROPQUERY_SIZE] = { '\0' };
+ char *str = NULL;
+
+ if (prsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_ASYM_CIPHER_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_ASYM_CIPHER_PARAM_FIPS_RSA_PKCS15_PAD_DISABLED))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST);
+ if (p != NULL) {
+ str = mdname;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdname)))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params,
+ OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS);
+ if (p != NULL) {
+ str = mdprops;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+ return 0;
+ }
+
+ EVP_MD_free(prsactx->oaep_md);
+ prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, mdname, mdprops);
+
+ if (prsactx->oaep_md == NULL)
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_PAD_MODE);
+ if (p != NULL) {
+ int pad_mode = 0;
+
+ switch (p->data_type) {
+ case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */
+ if (!OSSL_PARAM_get_int(p, &pad_mode))
+ return 0;
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ {
+ int i;
+
+ if (p->data == NULL)
+ return 0;
+
+ for (i = 0; padding_item[i].id != 0; i++) {
+ if (strcmp(p->data, padding_item[i].ptr) == 0) {
+ pad_mode = padding_item[i].id;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ /*
+ * PSS padding is for signatures only so is not compatible with
+ * asymmetric cipher use.
+ */
+ if (pad_mode == RSA_PKCS1_PSS_PADDING)
+ return 0;
+ if (pad_mode == RSA_PKCS1_OAEP_PADDING && prsactx->oaep_md == NULL) {
+ prsactx->oaep_md = EVP_MD_fetch(prsactx->libctx, "SHA1", mdprops);
+ if (prsactx->oaep_md == NULL)
+ return 0;
+ }
+ prsactx->pad_mode = pad_mode;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST);
+ if (p != NULL) {
+ str = mdname;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdname)))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params,
+ OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS);
+ if (p != NULL) {
+ str = mdprops;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+ return 0;
+ } else {
+ str = NULL;
+ }
+
+ EVP_MD_free(prsactx->mgf1_md);
+ prsactx->mgf1_md = EVP_MD_fetch(prsactx->libctx, mdname, str);
+
+ if (prsactx->mgf1_md == NULL)
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL);
+ if (p != NULL) {
+ void *tmp_label = NULL;
+ size_t tmp_labellen;
+
+ if (!OSSL_PARAM_get_octet_string(p, &tmp_label, 0, &tmp_labellen))
+ return 0;
+ OPENSSL_free(prsactx->oaep_label);
+ prsactx->oaep_label = (unsigned char *)tmp_label;
+ prsactx->oaep_labellen = tmp_labellen;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION);
+ if (p != NULL) {
+ unsigned int client_version;
+
+ if (!OSSL_PARAM_get_uint(p, &client_version))
+ return 0;
+ prsactx->client_version = client_version;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION);
+ if (p != NULL) {
+ unsigned int alt_version;
+
+ if (!OSSL_PARAM_get_uint(p, &alt_version))
+ return 0;
+ prsactx->alt_version = alt_version;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION);
+ if (p != NULL) {
+ unsigned int implicit_rejection;
+
+ if (!OSSL_PARAM_get_uint(p, &implicit_rejection))
+ return 0;
+ prsactx->implicit_rejection = implicit_rejection;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_OAEP_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_MGF1_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL, NULL, 0),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_CLIENT_VERSION, NULL),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_TLS_NEGOTIATED_VERSION, NULL),
+ OSSL_PARAM_uint(OSSL_ASYM_CIPHER_PARAM_IMPLICIT_REJECTION, NULL),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_ASYM_CIPHER_PARAM_FIPS_KEY_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_ASYM_CIPHER_PARAM_FIPS_RSA_PKCS15_PAD_DISABLED)
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_settable_ctx_params(ossl_unused void *vprsactx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_rsa_asym_cipher_functions[] = {
+ { OSSL_FUNC_ASYM_CIPHER_NEWCTX, (void (*)(void))rsa_newctx },
+ { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, (void (*)(void))rsa_encrypt_init },
+ { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, (void (*)(void))rsa_encrypt },
+ { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, (void (*)(void))rsa_decrypt_init },
+ { OSSL_FUNC_ASYM_CIPHER_DECRYPT, (void (*)(void))rsa_decrypt },
+ { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))rsa_freectx },
+ { OSSL_FUNC_ASYM_CIPHER_DUPCTX, (void (*)(void))rsa_dupctx },
+ { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS,
+ (void (*)(void))rsa_get_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))rsa_gettable_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS,
+ (void (*)(void))rsa_set_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))rsa_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c b/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c
new file mode 100644
index 000000000000..747190f3f9b5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/asymciphers/sm2_enc.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "crypto/sm2.h"
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_util.h"
+
+static OSSL_FUNC_asym_cipher_newctx_fn sm2_newctx;
+static OSSL_FUNC_asym_cipher_encrypt_init_fn sm2_init;
+static OSSL_FUNC_asym_cipher_encrypt_fn sm2_asym_encrypt;
+static OSSL_FUNC_asym_cipher_decrypt_init_fn sm2_init;
+static OSSL_FUNC_asym_cipher_decrypt_fn sm2_asym_decrypt;
+static OSSL_FUNC_asym_cipher_freectx_fn sm2_freectx;
+static OSSL_FUNC_asym_cipher_dupctx_fn sm2_dupctx;
+static OSSL_FUNC_asym_cipher_get_ctx_params_fn sm2_get_ctx_params;
+static OSSL_FUNC_asym_cipher_gettable_ctx_params_fn sm2_gettable_ctx_params;
+static OSSL_FUNC_asym_cipher_set_ctx_params_fn sm2_set_ctx_params;
+static OSSL_FUNC_asym_cipher_settable_ctx_params_fn sm2_settable_ctx_params;
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ EC_KEY *key;
+ PROV_DIGEST md;
+} PROV_SM2_CTX;
+
+static void *sm2_newctx(void *provctx)
+{
+ PROV_SM2_CTX *psm2ctx = OPENSSL_zalloc(sizeof(PROV_SM2_CTX));
+
+ if (psm2ctx == NULL)
+ return NULL;
+ psm2ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+ return psm2ctx;
+}
+
+static int sm2_init(void *vpsm2ctx, void *vkey, const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx == NULL || vkey == NULL || !EC_KEY_up_ref(vkey))
+ return 0;
+ EC_KEY_free(psm2ctx->key);
+ psm2ctx->key = vkey;
+
+ return sm2_set_ctx_params(psm2ctx, params);
+}
+
+static const EVP_MD *sm2_get_md(PROV_SM2_CTX *psm2ctx)
+{
+ const EVP_MD *md = ossl_prov_digest_md(&psm2ctx->md);
+
+ if (md == NULL)
+ md = ossl_prov_digest_fetch(&psm2ctx->md, psm2ctx->libctx, "SM3", NULL);
+
+ return md;
+}
+
+static int sm2_asym_encrypt(void *vpsm2ctx, unsigned char *out, size_t *outlen,
+ size_t outsize, const unsigned char *in,
+ size_t inlen)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ const EVP_MD *md = sm2_get_md(psm2ctx);
+
+ if (md == NULL)
+ return 0;
+
+ if (out == NULL) {
+ if (!ossl_sm2_ciphertext_size(psm2ctx->key, md, inlen, outlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+ return 1;
+ }
+
+ return ossl_sm2_encrypt(psm2ctx->key, md, in, inlen, out, outlen);
+}
+
+static int sm2_asym_decrypt(void *vpsm2ctx, unsigned char *out, size_t *outlen,
+ size_t outsize, const unsigned char *in,
+ size_t inlen)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ const EVP_MD *md = sm2_get_md(psm2ctx);
+
+ if (md == NULL)
+ return 0;
+
+ if (out == NULL) {
+ if (!ossl_sm2_plaintext_size(in, inlen, outlen))
+ return 0;
+ return 1;
+ }
+
+ return ossl_sm2_decrypt(psm2ctx->key, md, in, inlen, out, outlen);
+}
+
+static void sm2_freectx(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ EC_KEY_free(psm2ctx->key);
+ ossl_prov_digest_reset(&psm2ctx->md);
+
+ OPENSSL_free(psm2ctx);
+}
+
+static void *sm2_dupctx(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *srcctx = (PROV_SM2_CTX *)vpsm2ctx;
+ PROV_SM2_CTX *dstctx;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ memset(&dstctx->md, 0, sizeof(dstctx->md));
+
+ if (dstctx->key != NULL && !EC_KEY_up_ref(dstctx->key)) {
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ if (!ossl_prov_digest_copy(&dstctx->md, &srcctx->md)) {
+ sm2_freectx(dstctx);
+ return NULL;
+ }
+
+ return dstctx;
+}
+
+static int sm2_get_ctx_params(void *vpsm2ctx, OSSL_PARAM *params)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ OSSL_PARAM *p;
+
+ if (vpsm2ctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ASYM_CIPHER_PARAM_DIGEST);
+ if (p != NULL) {
+ const EVP_MD *md = ossl_prov_digest_md(&psm2ctx->md);
+
+ if (!OSSL_PARAM_set_utf8_string(p, md == NULL ? ""
+ : EVP_MD_get0_name(md)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *sm2_gettable_ctx_params(ossl_unused void *vpsm2ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int sm2_set_ctx_params(void *vpsm2ctx, const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_prov_digest_load_from_params(&psm2ctx->md, params,
+ psm2ctx->libctx))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ASYM_CIPHER_PARAM_ENGINE, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *sm2_settable_ctx_params(ossl_unused void *vpsm2ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[] = {
+ { OSSL_FUNC_ASYM_CIPHER_NEWCTX, (void (*)(void))sm2_newctx },
+ { OSSL_FUNC_ASYM_CIPHER_ENCRYPT_INIT, (void (*)(void))sm2_init },
+ { OSSL_FUNC_ASYM_CIPHER_ENCRYPT, (void (*)(void))sm2_asym_encrypt },
+ { OSSL_FUNC_ASYM_CIPHER_DECRYPT_INIT, (void (*)(void))sm2_init },
+ { OSSL_FUNC_ASYM_CIPHER_DECRYPT, (void (*)(void))sm2_asym_decrypt },
+ { OSSL_FUNC_ASYM_CIPHER_FREECTX, (void (*)(void))sm2_freectx },
+ { OSSL_FUNC_ASYM_CIPHER_DUPCTX, (void (*)(void))sm2_dupctx },
+ { OSSL_FUNC_ASYM_CIPHER_GET_CTX_PARAMS,
+ (void (*)(void))sm2_get_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))sm2_gettable_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_SET_CTX_PARAMS,
+ (void (*)(void))sm2_set_ctx_params },
+ { OSSL_FUNC_ASYM_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))sm2_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/build.info b/crypto/openssl/providers/implementations/build.info
new file mode 100644
index 000000000000..5cf054f5f32b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/build.info
@@ -0,0 +1,2 @@
+SUBDIRS=digests ciphers rands macs kdfs exchange keymgmt signature asymciphers \
+ encode_decode storemgmt kem skeymgmt
diff --git a/crypto/openssl/providers/implementations/ciphers/build.info b/crypto/openssl/providers/implementations/ciphers/build.info
new file mode 100644
index 000000000000..1837070c2111
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/build.info
@@ -0,0 +1,208 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+#
+# $TDES_1_GOAL and $TDES_2_GOAL separate FIPSable and non-FIPSable TDES.
+# The latter may become legacy sooner, so it's comfortable to have two
+# variables already now, to switch the non-FIPSable TDES to legacy if needed.
+
+$COMMON_GOAL=../../libcommon.a
+
+$NULL_GOAL=../../libdefault.a
+$AES_GOAL=../../libdefault.a ../../libfips.a
+$TDES_1_GOAL=../../libdefault.a ../../libfips.a
+$TDES_2_GOAL=../../libdefault.a
+$ARIA_GOAL=../../libdefault.a
+$CAMELLIA_GOAL=../../libdefault.a
+$DES_GOAL=../../liblegacy.a
+$BLOWFISH_GOAL=../../liblegacy.a
+$IDEA_GOAL=../../liblegacy.a
+$CAST5_GOAL=../../liblegacy.a
+$RC2_GOAL=../../liblegacy.a
+$RC4_GOAL=../../liblegacy.a
+$RC5_GOAL=../../liblegacy.a
+$SEED_GOAL=../../liblegacy.a
+$SM4_GOAL=../../libdefault.a
+$CHACHA_GOAL=../../libdefault.a
+$CHACHAPOLY_GOAL=../../libdefault.a
+$SIV_GOAL=../../libdefault.a
+$SIV_GCM_GOAL=../../libdefault.a
+
+IF[{- !$disabled{asm} -}]
+ $GHASHDEF_x86=GHASH_ASM
+ $GHASHDEF_x86_sse2=OPENSSL_IA32_SSE2
+
+ $GHASHDEF_x86_64=GHASH_ASM
+ $GHASHDEF_x86_64_sse2=OPENSSL_IA32_SSE2
+
+ # ghash-ia64.s doesn't work on VMS
+ IF[{- $config{target} !~ /^vms-/ -}]
+ $GHASHDEF_ia64=GHASH_ASM
+ ENDIF
+
+ $GHASHDEF_sparcv9=GHASH_ASM
+
+ $GHASHDEF_alpha=GHASH_ASM
+
+ $GHASHDEF_s390x=GHASH_ASM
+
+ $GHASHDEF_armv4=GHASH_ASM
+ $GHASHDEF_aarch64=
+
+ $GHASHDEF_parisc11=GHASH_ASM
+ $GHASHDEF_parisc20_64=$GHASHDEF_parisc11
+
+ $GHASHDEF_ppc32=
+ $GHASHDEF_ppc64=$GHASHDEF_ppc32
+
+ $GHASHDEF_c64xplus=GHASH_ASM
+
+ $GHASHDEF_riscv64=GHASH_ASM
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
+
+ IF[$GHASHDEF_{- $target{asm_arch} -}]
+ $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -}
+ IF[{- !$disabled{sse2} -}]
+ IF[$GHASHDEF_{- $target{asm_arch} -}_sse2]
+ $GHASHDEF=$GHASHDEF_{- $target{asm_arch} -}_sse2
+ ENDIF
+ ENDIF
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{asm} -}]
+ IF[{- ($target{perlasm_scheme} // '') ne '31' -}]
+ $AESXTSDEF_s390x=AES_XTS_S390X
+ ENDIF
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
+
+ IF[$AESXTSDEF_{- $target{asm_arch} -}]
+ $AESXTSDEF=$AESXTSDEF_{- $target{asm_arch} -}
+ ENDIF
+ENDIF
+
+# This source is common building blocks for all ciphers in all our providers.
+SOURCE[$COMMON_GOAL]=\
+ ciphercommon.c ciphercommon_hw.c ciphercommon_block.c \
+ ciphercommon_gcm.c ciphercommon_gcm_hw.c \
+ ciphercommon_ccm.c ciphercommon_ccm_hw.c
+
+IF[{- !$disabled{des} -}]
+ SOURCE[$TDES_1_GOAL]=cipher_tdes.c cipher_tdes_common.c cipher_tdes_hw.c
+ENDIF
+
+SOURCE[$NULL_GOAL]=\
+ cipher_null.c
+
+SOURCE[$AES_GOAL]=\
+ cipher_aes.c cipher_aes_hw.c \
+ cipher_aes_xts.c cipher_aes_xts_hw.c \
+ cipher_aes_gcm.c cipher_aes_gcm_hw.c \
+ cipher_aes_ccm.c cipher_aes_ccm_hw.c \
+ cipher_aes_wrp.c \
+ cipher_aes_cbc_hmac_sha.c \
+ cipher_aes_cbc_hmac_sha256_hw.c cipher_aes_cbc_hmac_sha1_hw.c \
+ cipher_cts.c
+DEFINE[$AES_GOAL]=$AESXTSDEF
+
+# Extra code to satisfy the FIPS and non-FIPS separation.
+# When the AES-xxx-XTS moves to legacy, cipher_aes_xts_fips.c can be removed.
+SOURCE[$AES_GOAL]=cipher_aes_xts_fips.c
+
+IF[{- !$disabled{siv} -}]
+ DEFINE[$SIV_GCM_GOAL]=$GHASHDEF
+ SOURCE[$SIV_GCM_GOAL]=\
+ cipher_aes_gcm_siv.c cipher_aes_gcm_siv_hw.c \
+ cipher_aes_gcm_siv_polyval.c
+ SOURCE[$SIV_GOAL]=cipher_aes_siv.c cipher_aes_siv_hw.c
+ENDIF
+
+IF[{- !$disabled{des} -}]
+ SOURCE[$TDES_2_GOAL]=\
+ cipher_tdes_default.c cipher_tdes_default_hw.c \
+ cipher_tdes_wrap.c cipher_tdes_wrap_hw.c
+ SOURCE[$DES_GOAL]=\
+ cipher_desx.c cipher_desx_hw.c \
+ cipher_des.c cipher_des_hw.c
+ IF[{- !$disabled{module} -}]
+ SOURCE[$DES_GOAL]=\
+ cipher_tdes_common.c
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{aria} -}]
+ SOURCE[$ARIA_GOAL]=\
+ cipher_aria.c cipher_aria_hw.c \
+ cipher_aria_gcm.c cipher_aria_gcm_hw.c \
+ cipher_aria_ccm.c cipher_aria_ccm_hw.c
+ENDIF
+
+IF[{- !$disabled{camellia} -}]
+ SOURCE[$CAMELLIA_GOAL]=\
+ cipher_camellia.c cipher_camellia_hw.c
+ENDIF
+
+IF[{- !$disabled{bf} -}]
+ SOURCE[$BLOWFISH_GOAL]=\
+ cipher_blowfish.c cipher_blowfish_hw.c
+ENDIF
+
+IF[{- !$disabled{idea} -}]
+ SOURCE[$IDEA_GOAL]=\
+ cipher_idea.c cipher_idea_hw.c
+ENDIF
+
+IF[{- !$disabled{cast} -}]
+ SOURCE[$CAST5_GOAL]=\
+ cipher_cast5.c cipher_cast5_hw.c
+ENDIF
+
+IF[{- !$disabled{seed} -}]
+ SOURCE[$SEED_GOAL]=\
+ cipher_seed.c cipher_seed_hw.c
+ENDIF
+
+IF[{- !$disabled{sm4} -}]
+ SOURCE[$SM4_GOAL]=\
+ cipher_sm4.c cipher_sm4_hw.c \
+ cipher_sm4_gcm.c cipher_sm4_gcm_hw.c \
+ cipher_sm4_ccm.c cipher_sm4_ccm_hw.c \
+ cipher_sm4_xts.c cipher_sm4_xts_hw.c
+
+ENDIF
+
+IF[{- !$disabled{ocb} -}]
+ SOURCE[$AES_GOAL]=\
+ cipher_aes_ocb.c cipher_aes_ocb_hw.c
+ENDIF
+
+IF[{- !$disabled{rc4} -}]
+ SOURCE[$RC4_GOAL]=\
+ cipher_rc4.c cipher_rc4_hw.c
+ IF[{- !$disabled{md5} -}]
+ SOURCE[$RC4_GOAL]=\
+ cipher_rc4_hmac_md5.c cipher_rc4_hmac_md5_hw.c
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{rc5} -}]
+ SOURCE[$RC5_GOAL]=\
+ cipher_rc5.c cipher_rc5_hw.c
+ENDIF
+
+IF[{- !$disabled{rc2} -}]
+ SOURCE[$RC2_GOAL]=\
+ cipher_rc2.c cipher_rc2_hw.c
+ENDIF
+
+IF[{- !$disabled{chacha} -}]
+ SOURCE[$CHACHA_GOAL]=\
+ cipher_chacha20.c cipher_chacha20_hw.c
+ IF[{- !$disabled{poly1305} -}]
+ SOURCE[$CHACHAPOLY_GOAL]=\
+ cipher_chacha20_poly1305.c cipher_chacha20_poly1305_hw.c
+ ENDIF
+ENDIF
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes.c
new file mode 100644
index 000000000000..280be2dddcaf
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for AES cipher modes ecb, cbc, ofb, cfb, ctr */
+
+#include "cipher_aes.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn aes_freectx;
+static OSSL_FUNC_cipher_dupctx_fn aes_dupctx;
+
+static void aes_freectx(void *vctx)
+{
+ PROV_AES_CTX *ctx = (PROV_AES_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *aes_dupctx(void *ctx)
+{
+ PROV_AES_CTX *in = (PROV_AES_CTX *)ctx;
+ PROV_AES_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+/* ossl_aes256ecb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 256, 128, 0, block)
+/* ossl_aes192ecb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 192, 128, 0, block)
+/* ossl_aes128ecb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ecb, ECB, 0, 128, 128, 0, block)
+/* ossl_aes256cbc_functions */
+IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 256, 128, 128, block)
+/* ossl_aes192cbc_functions */
+IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 192, 128, 128, block)
+/* ossl_aes128cbc_functions */
+IMPLEMENT_generic_cipher(aes, AES, cbc, CBC, 0, 128, 128, 128, block)
+/* ossl_aes256ofb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 256, 8, 128, stream)
+/* ossl_aes192ofb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 192, 8, 128, stream)
+/* ossl_aes128ofb_functions */
+IMPLEMENT_generic_cipher(aes, AES, ofb, OFB, 0, 128, 8, 128, stream)
+/* ossl_aes256cfb_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 256, 8, 128, stream)
+/* ossl_aes192cfb_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 192, 8, 128, stream)
+/* ossl_aes128cfb_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb, CFB, 0, 128, 8, 128, stream)
+/* ossl_aes256cfb1_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 256, 8, 128, stream)
+/* ossl_aes192cfb1_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 192, 8, 128, stream)
+/* ossl_aes128cfb1_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb1, CFB, 0, 128, 8, 128, stream)
+/* ossl_aes256cfb8_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 256, 8, 128, stream)
+/* ossl_aes192cfb8_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 192, 8, 128, stream)
+/* ossl_aes128cfb8_functions */
+IMPLEMENT_generic_cipher(aes, AES, cfb8, CFB, 0, 128, 8, 128, stream)
+/* ossl_aes256ctr_functions */
+IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 256, 8, 128, stream)
+/* ossl_aes192ctr_functions */
+IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 192, 8, 128, stream)
+/* ossl_aes128ctr_functions */
+IMPLEMENT_generic_cipher(aes, AES, ctr, CTR, 0, 128, 8, 128, stream)
+
+#include "cipher_aes_cts.inc"
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes.h
new file mode 100644
index 000000000000..c62ac5e7eaeb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+typedef struct prov_aes_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ks;
+
+ /* Platform specific data */
+ union {
+ int dummy;
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+ struct {
+ union {
+ OSSL_UNION_ALIGN;
+ /*-
+ * KM-AES parameter block - begin
+ * (see z/Architecture Principles of Operation >= SA22-7832-06)
+ */
+ struct {
+ unsigned char k[32];
+ } km;
+ /* KM-AES parameter block - end */
+ /*-
+ * KMO-AES/KMF-AES parameter block - begin
+ * (see z/Architecture Principles of Operation >= SA22-7832-08)
+ */
+ struct {
+ unsigned char cv[16];
+ unsigned char k[32];
+ } kmo_kmf;
+ /* KMO-AES/KMF-AES parameter block - end */
+ } param;
+ unsigned int fc;
+ } s390x;
+#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */
+ } plat;
+
+} PROV_AES_CTX;
+
+#define ossl_prov_cipher_hw_aes_ofb ossl_prov_cipher_hw_aes_ofb128
+#define ossl_prov_cipher_hw_aes_cfb ossl_prov_cipher_hw_aes_cfb128
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ofb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb1(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_cfb8(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ctr(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
new file mode 100644
index 000000000000..76f53e0c7cce
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for AES_CBC_HMAC_SHA ciphers */
+
+/* For SSL3_VERSION and TLS1_VERSION */
+#include <openssl/prov_ssl.h>
+#include <openssl/proverr.h>
+#include "cipher_aes_cbc_hmac_sha.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#ifndef AES_CBC_HMAC_SHA_CAPABLE
+# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags) \
+const OSSL_DISPATCH ossl_##nm##kbits##sub##_functions[] = { \
+ OSSL_DISPATCH_END \
+};
+#else
+
+# define AES_CBC_HMAC_SHA_FLAGS (PROV_CIPHER_FLAG_AEAD \
+ | PROV_CIPHER_FLAG_TLS1_MULTIBLOCK)
+
+static OSSL_FUNC_cipher_encrypt_init_fn aes_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn aes_dinit;
+static OSSL_FUNC_cipher_freectx_fn aes_cbc_hmac_sha1_freectx;
+static OSSL_FUNC_cipher_freectx_fn aes_cbc_hmac_sha256_freectx;
+static OSSL_FUNC_cipher_get_ctx_params_fn aes_get_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn aes_gettable_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_set_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn aes_settable_ctx_params;
+# define aes_gettable_params ossl_cipher_generic_gettable_params
+# define aes_update ossl_cipher_generic_stream_update
+# define aes_final ossl_cipher_generic_stream_final
+# define aes_cipher ossl_cipher_generic_cipher
+
+static int aes_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return aes_set_ctx_params(ctx, params);
+}
+
+static int aes_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return aes_set_ctx_params(ctx, params);
+}
+
+static const OSSL_PARAM cipher_aes_known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_MAC_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0),
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN, NULL, 0),
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *aes_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return cipher_aes_known_settable_ctx_params;
+}
+
+static int aes_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_CIPHER_HW_AES_HMAC_SHA *hw =
+ (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw;
+ const OSSL_PARAM *p;
+ int ret = 1;
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
+# endif
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_MAC_KEY);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ hw->init_mac_key(ctx, p->data, p->data_size);
+ }
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ p = OSSL_PARAM_locate_const(params,
+ OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_SEND_FRAGMENT);
+ if (p != NULL
+ && !OSSL_PARAM_get_size_t(p, &ctx->multiblock_max_send_fragment)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /*
+ * The inputs to tls1_multiblock_aad are:
+ * mb_param->inp
+ * mb_param->len
+ * mb_param->interleave
+ * The outputs of tls1_multiblock_aad are written to:
+ * ctx->multiblock_interleave
+ * ctx->multiblock_aad_packlen
+ */
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD);
+ if (p != NULL) {
+ const OSSL_PARAM *p1 = OSSL_PARAM_locate_const(params,
+ OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || p1 == NULL
+ || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ mb_param.inp = p->data;
+ mb_param.len = p->data_size;
+ if (hw->tls1_multiblock_aad(vctx, &mb_param) <= 0)
+ return 0;
+ }
+
+ /*
+ * The inputs to tls1_multiblock_encrypt are:
+ * mb_param->inp
+ * mb_param->len
+ * mb_param->interleave
+ * mb_param->out
+ * The outputs of tls1_multiblock_encrypt are:
+ * ctx->multiblock_encrypt_len
+ */
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC);
+ if (p != NULL) {
+ const OSSL_PARAM *p1 = OSSL_PARAM_locate_const(params,
+ OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+ const OSSL_PARAM *pin = OSSL_PARAM_locate_const(params,
+ OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_IN);
+
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || pin == NULL
+ || pin->data_type != OSSL_PARAM_OCTET_STRING
+ || p1 == NULL
+ || !OSSL_PARAM_get_uint(p1, &mb_param.interleave)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ mb_param.out = p->data;
+ mb_param.inp = pin->data;
+ mb_param.len = pin->data_size;
+ if (hw->tls1_multiblock_encrypt(vctx, &mb_param) <= 0)
+ return 0;
+ }
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (hw->set_tls1_aad(ctx, p->data, p->data_size) <= 0)
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t keylen;
+
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->base.keylen != keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &ctx->base.tlsversion)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->base.tlsversion == SSL3_VERSION
+ || ctx->base.tlsversion == TLS1_VERSION) {
+ if (!ossl_assert(ctx->base.removetlsfixed >= AES_BLOCK_SIZE)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ /*
+ * There is no explicit IV with these TLS versions, so don't attempt
+ * to remove it.
+ */
+ ctx->base.removetlsfixed -= AES_BLOCK_SIZE;
+ }
+ }
+ return ret;
+}
+
+static int aes_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ OSSL_PARAM *p;
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE);
+ if (p != NULL) {
+ PROV_CIPHER_HW_AES_HMAC_SHA *hw =
+ (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->hw;
+ size_t len = hw->tls1_multiblock_max_bufsize(ctx);
+
+ if (!OSSL_PARAM_set_size_t(p, len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_interleave)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->multiblock_aad_packlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->multiblock_encrypt_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p, ctx->base.oiv, ctx->base.ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.oiv, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.iv, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM cipher_aes_known_gettable_ctx_params[] = {
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_MAX_BUFSIZE, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_INTERLEAVE, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_AAD_PACKLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK_ENC_LEN, NULL),
+# endif /* !defined(OPENSSL_NO_MULTIBLOCK) */
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *aes_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return cipher_aes_known_gettable_ctx_params;
+}
+
+static void base_init(void *provctx, PROV_AES_HMAC_SHA_CTX *ctx,
+ const PROV_CIPHER_HW_AES_HMAC_SHA *meths,
+ size_t kbits, size_t blkbits, size_t ivbits,
+ uint64_t flags)
+{
+ ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits,
+ EVP_CIPH_CBC_MODE, flags,
+ &meths->base, provctx);
+ ctx->hw = (PROV_CIPHER_HW_AES_HMAC_SHA *)ctx->base.hw;
+}
+
+static void *aes_cbc_hmac_sha1_newctx(void *provctx, size_t kbits,
+ size_t blkbits, size_t ivbits,
+ uint64_t flags)
+{
+ PROV_AES_HMAC_SHA1_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ base_init(provctx, &ctx->base_ctx,
+ ossl_prov_cipher_hw_aes_cbc_hmac_sha1(), kbits, blkbits,
+ ivbits, flags);
+ return ctx;
+}
+
+static void *aes_cbc_hmac_sha1_dupctx(void *provctx)
+{
+ PROV_AES_HMAC_SHA1_CTX *ctx = provctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ return OPENSSL_memdup(ctx, sizeof(*ctx));
+}
+
+static void aes_cbc_hmac_sha1_freectx(void *vctx)
+{
+ PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+
+ if (ctx != NULL) {
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+static void *aes_cbc_hmac_sha256_newctx(void *provctx, size_t kbits,
+ size_t blkbits, size_t ivbits,
+ uint64_t flags)
+{
+ PROV_AES_HMAC_SHA256_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ base_init(provctx, &ctx->base_ctx,
+ ossl_prov_cipher_hw_aes_cbc_hmac_sha256(), kbits, blkbits,
+ ivbits, flags);
+ return ctx;
+}
+
+static void *aes_cbc_hmac_sha256_dupctx(void *provctx)
+{
+ PROV_AES_HMAC_SHA256_CTX *ctx = provctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ return OPENSSL_memdup(ctx, sizeof(*ctx));
+}
+
+static void aes_cbc_hmac_sha256_freectx(void *vctx)
+{
+ PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+
+ if (ctx != NULL) {
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+# define IMPLEMENT_CIPHER(nm, sub, kbits, blkbits, ivbits, flags) \
+static OSSL_FUNC_cipher_newctx_fn nm##_##kbits##_##sub##_newctx; \
+static void *nm##_##kbits##_##sub##_newctx(void *provctx) \
+{ \
+ return nm##_##sub##_newctx(provctx, kbits, blkbits, ivbits, flags); \
+} \
+static OSSL_FUNC_cipher_get_params_fn nm##_##kbits##_##sub##_get_params; \
+static int nm##_##kbits##_##sub##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_CBC_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+const OSSL_DISPATCH ossl_##nm##kbits##sub##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))nm##_##kbits##_##sub##_newctx },\
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))nm##_##sub##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))nm##_##sub##_dupctx}, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))nm##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))nm##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))nm##_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))nm##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))nm##_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))nm##_##kbits##_##sub##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))nm##_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))nm##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))nm##_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))nm##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))nm##_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
+
+/* ossl_aes128cbc_hmac_sha1_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* ossl_aes256cbc_hmac_sha1_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha1, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* ossl_aes128cbc_hmac_sha256_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 128, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
+/* ossl_aes256cbc_hmac_sha256_functions */
+IMPLEMENT_CIPHER(aes, cbc_hmac_sha256, 256, 128, 128, AES_CBC_HMAC_SHA_FLAGS)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h
new file mode 100644
index 000000000000..6aaf3f06fb49
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+int ossl_cipher_capable_aes_cbc_hmac_sha1(void);
+int ossl_cipher_capable_aes_cbc_hmac_sha256(void);
+
+typedef struct prov_cipher_hw_aes_hmac_sha_ctx_st {
+ PROV_CIPHER_HW base; /* must be first */
+ void (*init_mac_key)(void *ctx, const unsigned char *inkey, size_t inlen);
+ int (*set_tls1_aad)(void *ctx, unsigned char *aad_rec, int aad_len);
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ int (*tls1_multiblock_max_bufsize)(void *ctx);
+ int (*tls1_multiblock_aad)(
+ void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param);
+ int (*tls1_multiblock_encrypt)(
+ void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param);
+# endif /* OPENSSL_NO_MULTIBLOCK) */
+} PROV_CIPHER_HW_AES_HMAC_SHA;
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void);
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void);
+
+#ifdef AES_CBC_HMAC_SHA_CAPABLE
+# include <openssl/aes.h>
+# include <openssl/sha.h>
+
+typedef struct prov_aes_hmac_sha_ctx_st {
+ PROV_CIPHER_CTX base;
+ AES_KEY ks;
+ size_t payload_length; /* AAD length in decrypt case */
+ union {
+ unsigned int tls_ver;
+ unsigned char tls_aad[16]; /* 13 used */
+ } aux;
+ const PROV_CIPHER_HW_AES_HMAC_SHA *hw;
+ /* some value that are setup by set methods - that can be retrieved */
+ unsigned int multiblock_interleave;
+ unsigned int multiblock_aad_packlen;
+ size_t multiblock_max_send_fragment;
+ size_t multiblock_encrypt_len;
+ size_t tls_aad_pad;
+} PROV_AES_HMAC_SHA_CTX;
+
+typedef struct prov_aes_hmac_sha1_ctx_st {
+ PROV_AES_HMAC_SHA_CTX base_ctx;
+ SHA_CTX head, tail, md;
+} PROV_AES_HMAC_SHA1_CTX;
+
+typedef struct prov_aes_hmac_sha256_ctx_st {
+ PROV_AES_HMAC_SHA_CTX base_ctx;
+ SHA256_CTX head, tail, md;
+} PROV_AES_HMAC_SHA256_CTX;
+
+# define NO_PAYLOAD_LENGTH ((size_t)-1)
+
+#endif /* AES_CBC_HMAC_SHA_CAPABLE */
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
new file mode 100644
index 000000000000..76674d530434
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha1_hw.c
@@ -0,0 +1,795 @@
+/*
+ * Copyright 2011-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * All low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_cbc_hmac_sha.h"
+
+#if !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE)
+int ossl_cipher_capable_aes_cbc_hmac_sha1(void)
+{
+ return 0;
+}
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void)
+{
+ return NULL;
+}
+#else
+
+# include <openssl/rand.h>
+# include "crypto/evp.h"
+# include "internal/constant_time.h"
+
+void sha1_block_data_order(void *c, const void *p, size_t len);
+void aesni_cbc_sha1_enc(const void *inp, void *out, size_t blocks,
+ const AES_KEY *key, unsigned char iv[16],
+ SHA_CTX *ctx, const void *in0);
+
+int ossl_cipher_capable_aes_cbc_hmac_sha1(void)
+{
+ return AESNI_CBC_HMAC_SHA_CAPABLE;
+}
+
+static int aesni_cbc_hmac_sha1_init_key(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+
+ if (ctx->base.enc)
+ ret = aesni_set_encrypt_key(key, keylen * 8, &ctx->ks);
+ else
+ ret = aesni_set_decrypt_key(key, keylen * 8, &ctx->ks);
+
+ SHA1_Init(&sctx->head); /* handy when benchmarking */
+ sctx->tail = sctx->head;
+ sctx->md = sctx->head;
+
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+ vctx->removetlspad = 1;
+ vctx->removetlsfixed = SHA_DIGEST_LENGTH + AES_BLOCK_SIZE;
+
+ return ret < 0 ? 0 : 1;
+}
+
+static void sha1_update(SHA_CTX *c, const void *data, size_t len)
+{
+ const unsigned char *ptr = data;
+ size_t res;
+
+ if ((res = c->num)) {
+ res = SHA_CBLOCK - res;
+ if (len < res)
+ res = len;
+ SHA1_Update(c, ptr, res);
+ ptr += res;
+ len -= res;
+ }
+
+ res = len % SHA_CBLOCK;
+ len -= res;
+
+ if (len) {
+ sha1_block_data_order(c, ptr, len / SHA_CBLOCK);
+
+ ptr += len;
+ c->Nh += len >> 29;
+ c->Nl += len <<= 3;
+ if (c->Nl < (unsigned int)len)
+ c->Nh++;
+ }
+
+ if (res)
+ SHA1_Update(c, ptr, res);
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+ unsigned int A[8], B[8], C[8], D[8], E[8];
+} SHA1_MB_CTX;
+
+typedef struct {
+ const unsigned char *ptr;
+ int blocks;
+} HASH_DESC;
+
+typedef struct {
+ const unsigned char *inp;
+ unsigned char *out;
+ int blocks;
+ u64 iv[2];
+} CIPH_DESC;
+
+void sha1_multi_block(SHA1_MB_CTX *, const HASH_DESC *, int);
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_multi_block_encrypt(void *vctx,
+ unsigned char *out,
+ const unsigned char *inp,
+ size_t inp_len, int n4x)
+{ /* n4x is 1 or 2 */
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+ HASH_DESC hash_d[8], edges[8];
+ CIPH_DESC ciph_d[8];
+ unsigned char storage[sizeof(SHA1_MB_CTX) + 32];
+ union {
+ u64 q[16];
+ u32 d[32];
+ u8 c[128];
+ } blocks[8];
+ SHA1_MB_CTX *mctx;
+ unsigned int frag, last, packlen, i;
+ unsigned int x4 = 4 * n4x, minblocks, processed = 0;
+ size_t ret = 0;
+ u8 *IVs;
+# if defined(BSWAP8)
+ u64 seqnum;
+# endif
+
+ /* ask for IVs in bulk */
+ if (RAND_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4, 0) <= 0)
+ return 0;
+
+ mctx = (SHA1_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */
+
+ frag = (unsigned int)inp_len >> (1 + n4x);
+ last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+ if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+ frag++;
+ last -= x4 - 1;
+ }
+
+ packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+
+ /* populate descriptors with pointers and IVs */
+ hash_d[0].ptr = inp;
+ ciph_d[0].inp = inp;
+ /* 5+16 is place for header and explicit IV */
+ ciph_d[0].out = out + 5 + 16;
+ memcpy(ciph_d[0].out - 16, IVs, 16);
+ memcpy(ciph_d[0].iv, IVs, 16);
+ IVs += 16;
+
+ for (i = 1; i < x4; i++) {
+ ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+ ciph_d[i].out = ciph_d[i - 1].out + packlen;
+ memcpy(ciph_d[i].out - 16, IVs, 16);
+ memcpy(ciph_d[i].iv, IVs, 16);
+ IVs += 16;
+ }
+
+# if defined(BSWAP8)
+ memcpy(blocks[0].c, sctx->md.data, 8);
+ seqnum = BSWAP8(blocks[0].q[0]);
+# endif
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag);
+# if !defined(BSWAP8)
+ unsigned int carry, j;
+# endif
+
+ mctx->A[i] = sctx->md.h0;
+ mctx->B[i] = sctx->md.h1;
+ mctx->C[i] = sctx->md.h2;
+ mctx->D[i] = sctx->md.h3;
+ mctx->E[i] = sctx->md.h4;
+
+ /* fix seqnum */
+# if defined(BSWAP8)
+ blocks[i].q[0] = BSWAP8(seqnum + i);
+# else
+ for (carry = i, j = 8; j--;) {
+ blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry;
+ carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+ }
+# endif
+ blocks[i].c[8] = ((u8 *)sctx->md.data)[8];
+ blocks[i].c[9] = ((u8 *)sctx->md.data)[9];
+ blocks[i].c[10] = ((u8 *)sctx->md.data)[10];
+ /* fix length */
+ blocks[i].c[11] = (u8)(len >> 8);
+ blocks[i].c[12] = (u8)(len);
+
+ memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+ hash_d[i].ptr += 64 - 13;
+ hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+ edges[i].ptr = blocks[i].c;
+ edges[i].blocks = 1;
+ }
+
+ /* hash 13-byte headers and first 64-13 bytes of inputs */
+ sha1_multi_block(mctx, edges, n4x);
+ /* hash bulk inputs */
+# define MAXCHUNKSIZE 2048
+# if MAXCHUNKSIZE%64
+# error "MAXCHUNKSIZE is not divisible by 64"
+# elif MAXCHUNKSIZE
+ /*
+ * goal is to minimize pressure on L1 cache by moving in shorter steps,
+ * so that hashed data is still in the cache by the time we encrypt it
+ */
+ minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+ if (minblocks > MAXCHUNKSIZE / 64) {
+ for (i = 0; i < x4; i++) {
+ edges[i].ptr = hash_d[i].ptr;
+ edges[i].blocks = MAXCHUNKSIZE / 64;
+ ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+ }
+ do {
+ sha1_multi_block(mctx, edges, n4x);
+ aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+ for (i = 0; i < x4; i++) {
+ edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+ hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+ edges[i].blocks = MAXCHUNKSIZE / 64;
+ ciph_d[i].inp += MAXCHUNKSIZE;
+ ciph_d[i].out += MAXCHUNKSIZE;
+ ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+ memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+ }
+ processed += MAXCHUNKSIZE;
+ minblocks -= MAXCHUNKSIZE / 64;
+ } while (minblocks > MAXCHUNKSIZE / 64);
+ }
+# endif
+# undef MAXCHUNKSIZE
+ sha1_multi_block(mctx, hash_d, n4x);
+
+ memset(blocks, 0, sizeof(blocks));
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag),
+ off = hash_d[i].blocks * 64;
+ const unsigned char *ptr = hash_d[i].ptr + off;
+
+ off = (len - processed) - (64 - 13) - off; /* remainder actually */
+ memcpy(blocks[i].c, ptr, off);
+ blocks[i].c[off] = 0x80;
+ len += 64 + 13; /* 64 is HMAC header */
+ len *= 8; /* convert to bits */
+ if (off < (64 - 8)) {
+# ifdef BSWAP4
+ blocks[i].d[15] = BSWAP4(len);
+# else
+ PUTU32(blocks[i].c + 60, len);
+# endif
+ edges[i].blocks = 1;
+ } else {
+# ifdef BSWAP4
+ blocks[i].d[31] = BSWAP4(len);
+# else
+ PUTU32(blocks[i].c + 124, len);
+# endif
+ edges[i].blocks = 2;
+ }
+ edges[i].ptr = blocks[i].c;
+ }
+
+ /* hash input tails and finalize */
+ sha1_multi_block(mctx, edges, n4x);
+
+ memset(blocks, 0, sizeof(blocks));
+ for (i = 0; i < x4; i++) {
+# ifdef BSWAP4
+ blocks[i].d[0] = BSWAP4(mctx->A[i]);
+ mctx->A[i] = sctx->tail.h0;
+ blocks[i].d[1] = BSWAP4(mctx->B[i]);
+ mctx->B[i] = sctx->tail.h1;
+ blocks[i].d[2] = BSWAP4(mctx->C[i]);
+ mctx->C[i] = sctx->tail.h2;
+ blocks[i].d[3] = BSWAP4(mctx->D[i]);
+ mctx->D[i] = sctx->tail.h3;
+ blocks[i].d[4] = BSWAP4(mctx->E[i]);
+ mctx->E[i] = sctx->tail.h4;
+ blocks[i].c[20] = 0x80;
+ blocks[i].d[15] = BSWAP4((64 + 20) * 8);
+# else
+ PUTU32(blocks[i].c + 0, mctx->A[i]);
+ mctx->A[i] = sctx->tail.h0;
+ PUTU32(blocks[i].c + 4, mctx->B[i]);
+ mctx->B[i] = sctx->tail.h1;
+ PUTU32(blocks[i].c + 8, mctx->C[i]);
+ mctx->C[i] = sctx->tail.h2;
+ PUTU32(blocks[i].c + 12, mctx->D[i]);
+ mctx->D[i] = sctx->tail.h3;
+ PUTU32(blocks[i].c + 16, mctx->E[i]);
+ mctx->E[i] = sctx->tail.h4;
+ blocks[i].c[20] = 0x80;
+ PUTU32(blocks[i].c + 60, (64 + 20) * 8);
+# endif /* BSWAP */
+ edges[i].ptr = blocks[i].c;
+ edges[i].blocks = 1;
+ }
+
+ /* finalize MACs */
+ sha1_multi_block(mctx, edges, n4x);
+
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+ unsigned char *out0 = out;
+
+ memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+ ciph_d[i].inp = ciph_d[i].out;
+
+ out += 5 + 16 + len;
+
+ /* write MAC */
+ PUTU32(out + 0, mctx->A[i]);
+ PUTU32(out + 4, mctx->B[i]);
+ PUTU32(out + 8, mctx->C[i]);
+ PUTU32(out + 12, mctx->D[i]);
+ PUTU32(out + 16, mctx->E[i]);
+ out += 20;
+ len += 20;
+
+ /* pad */
+ pad = 15 - len % 16;
+ for (j = 0; j <= pad; j++)
+ *(out++) = pad;
+ len += pad + 1;
+
+ ciph_d[i].blocks = (len - processed) / 16;
+ len += 16; /* account for explicit iv */
+
+ /* arrange header */
+ out0[0] = ((u8 *)sctx->md.data)[8];
+ out0[1] = ((u8 *)sctx->md.data)[9];
+ out0[2] = ((u8 *)sctx->md.data)[10];
+ out0[3] = (u8)(len >> 8);
+ out0[4] = (u8)(len);
+
+ ret += len + 5;
+ inp += frag;
+ }
+
+ aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+ OPENSSL_cleanse(blocks, sizeof(blocks));
+ OPENSSL_cleanse(mctx, sizeof(*mctx));
+
+ ctx->multiblock_encrypt_len = ret;
+ return ret;
+}
+# endif /* OPENSSL_NO_MULTIBLOCK */
+
+static int aesni_cbc_hmac_sha1_cipher(PROV_CIPHER_CTX *vctx,
+ unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+ unsigned int l;
+ size_t plen = ctx->payload_length;
+ size_t iv = 0; /* explicit IV in TLS 1.1 and later */
+ size_t aes_off = 0, blocks;
+ size_t sha_off = SHA_CBLOCK - sctx->md.num;
+
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+ if (len % AES_BLOCK_SIZE)
+ return 0;
+
+ if (ctx->base.enc) {
+ if (plen == NO_PAYLOAD_LENGTH)
+ plen = len;
+ else if (len !=
+ ((plen + SHA_DIGEST_LENGTH +
+ AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+ return 0;
+ else if (ctx->aux.tls_ver >= TLS1_1_VERSION)
+ iv = AES_BLOCK_SIZE;
+
+ if (plen > (sha_off + iv)
+ && (blocks = (plen - (sha_off + iv)) / SHA_CBLOCK)) {
+ sha1_update(&sctx->md, in + iv, sha_off);
+
+ aesni_cbc_sha1_enc(in, out, blocks, &ctx->ks, ctx->base.iv,
+ &sctx->md, in + iv + sha_off);
+ blocks *= SHA_CBLOCK;
+ aes_off += blocks;
+ sha_off += blocks;
+ sctx->md.Nh += blocks >> 29;
+ sctx->md.Nl += blocks <<= 3;
+ if (sctx->md.Nl < (unsigned int)blocks)
+ sctx->md.Nh++;
+ } else {
+ sha_off = 0;
+ }
+ sha_off += iv;
+ sha1_update(&sctx->md, in + sha_off, plen - sha_off);
+
+ if (plen != len) { /* "TLS" mode of operation */
+ if (in != out)
+ memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+ /* calculate HMAC and append it to payload */
+ SHA1_Final(out + plen, &sctx->md);
+ sctx->md = sctx->tail;
+ sha1_update(&sctx->md, out + plen, SHA_DIGEST_LENGTH);
+ SHA1_Final(out + plen, &sctx->md);
+
+ /* pad the payload|hmac */
+ plen += SHA_DIGEST_LENGTH;
+ for (l = len - plen - 1; plen < len; plen++)
+ out[plen] = l;
+ /* encrypt HMAC|padding at once */
+ aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+ &ctx->ks, ctx->base.iv, 1);
+ } else {
+ aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+ &ctx->ks, ctx->base.iv, 1);
+ }
+ } else {
+ union {
+ unsigned int u[SHA_DIGEST_LENGTH / sizeof(unsigned int)];
+ unsigned char c[32 + SHA_DIGEST_LENGTH];
+ } mac, *pmac;
+
+ /* arrange cache line alignment */
+ pmac = (void *)(((size_t)mac.c + 31) & ((size_t)0 - 32));
+
+ if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+ size_t inp_len, mask, j, i;
+ unsigned int res, maxpad, pad, bitlen;
+ int ret = 1;
+ union {
+ unsigned int u[SHA_LBLOCK];
+ unsigned char c[SHA_CBLOCK];
+ } *data = (void *)sctx->md.data;
+
+ if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3])
+ >= TLS1_1_VERSION) {
+ if (len < (AES_BLOCK_SIZE + SHA_DIGEST_LENGTH + 1))
+ return 0;
+
+ /* omit explicit iv */
+ memcpy(ctx->base.iv, in, AES_BLOCK_SIZE);
+
+ in += AES_BLOCK_SIZE;
+ out += AES_BLOCK_SIZE;
+ len -= AES_BLOCK_SIZE;
+ } else if (len < (SHA_DIGEST_LENGTH + 1))
+ return 0;
+
+ /* decrypt HMAC|padding at once */
+ aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0);
+
+ /* figure out payload length */
+ pad = out[len - 1];
+ maxpad = len - (SHA_DIGEST_LENGTH + 1);
+ maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+ maxpad &= 255;
+
+ mask = constant_time_ge(maxpad, pad);
+ ret &= mask;
+ /*
+ * If pad is invalid then we will fail the above test but we must
+ * continue anyway because we are in constant time code. However,
+ * we'll use the maxpad value instead of the supplied pad to make
+ * sure we perform well defined pointer arithmetic.
+ */
+ pad = constant_time_select(mask, pad, maxpad);
+
+ inp_len = len - (SHA_DIGEST_LENGTH + pad + 1);
+
+ ctx->aux.tls_aad[plen - 2] = inp_len >> 8;
+ ctx->aux.tls_aad[plen - 1] = inp_len;
+
+ /* calculate HMAC */
+ sctx->md = sctx->head;
+ sha1_update(&sctx->md, ctx->aux.tls_aad, plen);
+
+ /* code containing lucky-13 fix */
+ len -= SHA_DIGEST_LENGTH; /* amend mac */
+ if (len >= (256 + SHA_CBLOCK)) {
+ j = (len - (256 + SHA_CBLOCK)) & (0 - SHA_CBLOCK);
+ j += SHA_CBLOCK - sctx->md.num;
+ sha1_update(&sctx->md, out, j);
+ out += j;
+ len -= j;
+ inp_len -= j;
+ }
+
+ /* but pretend as if we hashed padded payload */
+ bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */
+# ifdef BSWAP4
+ bitlen = BSWAP4(bitlen);
+# else
+ mac.c[0] = 0;
+ mac.c[1] = (unsigned char)(bitlen >> 16);
+ mac.c[2] = (unsigned char)(bitlen >> 8);
+ mac.c[3] = (unsigned char)bitlen;
+ bitlen = mac.u[0];
+# endif /* BSWAP */
+
+ pmac->u[0] = 0;
+ pmac->u[1] = 0;
+ pmac->u[2] = 0;
+ pmac->u[3] = 0;
+ pmac->u[4] = 0;
+
+ for (res = sctx->md.num, j = 0; j < len; j++) {
+ size_t c = out[j];
+ mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+ c &= mask;
+ c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+ data->c[res++] = (unsigned char)c;
+
+ if (res != SHA_CBLOCK)
+ continue;
+
+ /* j is not incremented yet */
+ mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+ data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+ sha1_block_data_order(&sctx->md, data, 1);
+ mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h0 & mask;
+ pmac->u[1] |= sctx->md.h1 & mask;
+ pmac->u[2] |= sctx->md.h2 & mask;
+ pmac->u[3] |= sctx->md.h3 & mask;
+ pmac->u[4] |= sctx->md.h4 & mask;
+ res = 0;
+ }
+
+ for (i = res; i < SHA_CBLOCK; i++, j++)
+ data->c[i] = 0;
+
+ if (res > SHA_CBLOCK - 8) {
+ mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+ data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+ sha1_block_data_order(&sctx->md, data, 1);
+ mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h0 & mask;
+ pmac->u[1] |= sctx->md.h1 & mask;
+ pmac->u[2] |= sctx->md.h2 & mask;
+ pmac->u[3] |= sctx->md.h3 & mask;
+ pmac->u[4] |= sctx->md.h4 & mask;
+
+ memset(data, 0, SHA_CBLOCK);
+ j += 64;
+ }
+ data->u[SHA_LBLOCK - 1] = bitlen;
+ sha1_block_data_order(&sctx->md, data, 1);
+ mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h0 & mask;
+ pmac->u[1] |= sctx->md.h1 & mask;
+ pmac->u[2] |= sctx->md.h2 & mask;
+ pmac->u[3] |= sctx->md.h3 & mask;
+ pmac->u[4] |= sctx->md.h4 & mask;
+
+# ifdef BSWAP4
+ pmac->u[0] = BSWAP4(pmac->u[0]);
+ pmac->u[1] = BSWAP4(pmac->u[1]);
+ pmac->u[2] = BSWAP4(pmac->u[2]);
+ pmac->u[3] = BSWAP4(pmac->u[3]);
+ pmac->u[4] = BSWAP4(pmac->u[4]);
+# else
+ for (i = 0; i < 5; i++) {
+ res = pmac->u[i];
+ pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+ pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+ pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+ pmac->c[4 * i + 3] = (unsigned char)res;
+ }
+# endif /* BSWAP4 */
+ len += SHA_DIGEST_LENGTH;
+ sctx->md = sctx->tail;
+ sha1_update(&sctx->md, pmac->c, SHA_DIGEST_LENGTH);
+ SHA1_Final(pmac->c, &sctx->md);
+
+ /* verify HMAC */
+ out += inp_len;
+ len -= inp_len;
+ /* version of code with lucky-13 fix */
+ {
+ unsigned char *p = out + len - 1 - maxpad - SHA_DIGEST_LENGTH;
+ size_t off = out - p;
+ unsigned int c, cmask;
+
+ for (res = 0, i = 0, j = 0; j < maxpad + SHA_DIGEST_LENGTH; j++) {
+ c = p[j];
+ cmask =
+ ((int)(j - off - SHA_DIGEST_LENGTH)) >> (sizeof(int) *
+ 8 - 1);
+ res |= (c ^ pad) & ~cmask; /* ... and padding */
+ cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+ res |= (c ^ pmac->c[i]) & cmask;
+ i += 1 & cmask;
+ }
+
+ res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+ ret &= (int)~res;
+ }
+ return ret;
+ } else {
+ /* decrypt HMAC|padding at once */
+ aesni_cbc_encrypt(in, out, len, &ctx->ks, ctx->base.iv, 0);
+ sha1_update(&sctx->md, out, len);
+ }
+ }
+
+ return 1;
+}
+
+/* EVP_CTRL_AEAD_SET_MAC_KEY */
+static void aesni_cbc_hmac_sha1_set_mac_key(void *vctx,
+ const unsigned char *mac, size_t len)
+{
+ PROV_AES_HMAC_SHA1_CTX *ctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+ unsigned int i;
+ unsigned char hmac_key[64];
+
+ memset(hmac_key, 0, sizeof(hmac_key));
+
+ if (len > (int)sizeof(hmac_key)) {
+ SHA1_Init(&ctx->head);
+ sha1_update(&ctx->head, mac, len);
+ SHA1_Final(hmac_key, &ctx->head);
+ } else {
+ memcpy(hmac_key, mac, len);
+ }
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36; /* ipad */
+ SHA1_Init(&ctx->head);
+ sha1_update(&ctx->head, hmac_key, sizeof(hmac_key));
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+ SHA1_Init(&ctx->tail);
+ sha1_update(&ctx->tail, hmac_key, sizeof(hmac_key));
+
+ OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+}
+
+/* EVP_CTRL_AEAD_TLS1_AAD */
+static int aesni_cbc_hmac_sha1_set_tls1_aad(void *vctx,
+ unsigned char *aad_rec, int aad_len)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+ unsigned char *p = aad_rec;
+ unsigned int len;
+
+ if (aad_len != EVP_AEAD_TLS1_AAD_LEN)
+ return -1;
+
+ len = p[aad_len - 2] << 8 | p[aad_len - 1];
+
+ if (ctx->base.enc) {
+ ctx->payload_length = len;
+ if ((ctx->aux.tls_ver =
+ p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) {
+ if (len < AES_BLOCK_SIZE)
+ return 0;
+ len -= AES_BLOCK_SIZE;
+ p[aad_len - 2] = len >> 8;
+ p[aad_len - 1] = len;
+ }
+ sctx->md = sctx->head;
+ sha1_update(&sctx->md, p, aad_len);
+ ctx->tls_aad_pad = (int)(((len + SHA_DIGEST_LENGTH +
+ AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+ - len);
+ return 1;
+ } else {
+ memcpy(ctx->aux.tls_aad, aad_rec, aad_len);
+ ctx->payload_length = aad_len;
+ ctx->tls_aad_pad = SHA_DIGEST_LENGTH;
+ return 1;
+ }
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize(void *vctx)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+
+ OPENSSL_assert(ctx->multiblock_max_send_fragment != 0);
+ return (int)(5 + 16
+ + (((int)ctx->multiblock_max_send_fragment + 20 + 16) & -16));
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_aad(
+ void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA1_CTX *sctx = (PROV_AES_HMAC_SHA1_CTX *)vctx;
+ unsigned int n4x = 1, x4;
+ unsigned int frag, last, packlen, inp_len;
+
+ inp_len = param->inp[11] << 8 | param->inp[12];
+ ctx->multiblock_interleave = param->interleave;
+
+ if (ctx->base.enc) {
+ if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+ return -1;
+
+ if (inp_len) {
+ if (inp_len < 4096)
+ return 0; /* too short */
+
+ if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+ n4x = 2; /* AVX2 */
+ } else if ((n4x = param->interleave / 4) && n4x <= 2)
+ inp_len = param->len;
+ else
+ return -1;
+
+ sctx->md = sctx->head;
+ sha1_update(&sctx->md, param->inp, 13);
+
+ x4 = 4 * n4x;
+ n4x += 1;
+
+ frag = inp_len >> n4x;
+ last = inp_len + frag - (frag << n4x);
+ if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+ frag++;
+ last -= x4 - 1;
+ }
+
+ packlen = 5 + 16 + ((frag + 20 + 16) & -16);
+ packlen = (packlen << n4x) - packlen;
+ packlen += 5 + 16 + ((last + 20 + 16) & -16);
+
+ param->interleave = x4;
+ /* The returned values used by get need to be stored */
+ ctx->multiblock_interleave = x4;
+ ctx->multiblock_aad_packlen = packlen;
+ return 1;
+ }
+ return -1; /* not yet */
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */
+static int aesni_cbc_hmac_sha1_tls1_multiblock_encrypt(
+ void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+ return (int)tls1_multi_block_encrypt(ctx, param->out,
+ param->inp, param->len,
+ param->interleave / 4);
+}
+
+# endif /* OPENSSL_NO_MULTIBLOCK */
+
+static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha1 = {
+ {
+ aesni_cbc_hmac_sha1_init_key,
+ aesni_cbc_hmac_sha1_cipher
+ },
+ aesni_cbc_hmac_sha1_set_mac_key,
+ aesni_cbc_hmac_sha1_set_tls1_aad,
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ aesni_cbc_hmac_sha1_tls1_multiblock_max_bufsize,
+ aesni_cbc_hmac_sha1_tls1_multiblock_aad,
+ aesni_cbc_hmac_sha1_tls1_multiblock_encrypt
+# endif
+};
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha1(void)
+{
+ return &cipher_hw_aes_hmac_sha1;
+}
+
+#endif /* !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) */
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
new file mode 100644
index 000000000000..f5b2f8b6da32
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cbc_hmac_sha256_hw.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright 2011-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * All low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_cbc_hmac_sha.h"
+
+#if !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE)
+int ossl_cipher_capable_aes_cbc_hmac_sha256(void)
+{
+ return 0;
+}
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void)
+{
+ return NULL;
+}
+#else
+
+# include <openssl/rand.h>
+# include "crypto/evp.h"
+# include "internal/constant_time.h"
+
+void sha256_block_data_order(void *c, const void *p, size_t len);
+int aesni_cbc_sha256_enc(const void *inp, void *out, size_t blocks,
+ const AES_KEY *key, unsigned char iv[16],
+ SHA256_CTX *ctx, const void *in0);
+
+int ossl_cipher_capable_aes_cbc_hmac_sha256(void)
+{
+ return AESNI_CBC_HMAC_SHA_CAPABLE
+ && aesni_cbc_sha256_enc(NULL, NULL, 0, NULL, NULL, NULL, NULL);
+}
+
+static int aesni_cbc_hmac_sha256_init_key(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ int ret;
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+
+ if (ctx->base.enc)
+ ret = aesni_set_encrypt_key(key, ctx->base.keylen * 8, &ctx->ks);
+ else
+ ret = aesni_set_decrypt_key(key, ctx->base.keylen * 8, &ctx->ks);
+
+ SHA256_Init(&sctx->head); /* handy when benchmarking */
+ sctx->tail = sctx->head;
+ sctx->md = sctx->head;
+
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+ vctx->removetlspad = 1;
+ vctx->removetlsfixed = SHA256_DIGEST_LENGTH + AES_BLOCK_SIZE;
+
+ return ret < 0 ? 0 : 1;
+}
+
+void sha256_block_data_order(void *c, const void *p, size_t len);
+
+static void sha256_update(SHA256_CTX *c, const void *data, size_t len)
+{
+ const unsigned char *ptr = data;
+ size_t res;
+
+ if ((res = c->num)) {
+ res = SHA256_CBLOCK - res;
+ if (len < res)
+ res = len;
+ SHA256_Update(c, ptr, res);
+ ptr += res;
+ len -= res;
+ }
+
+ res = len % SHA256_CBLOCK;
+ len -= res;
+
+ if (len) {
+ sha256_block_data_order(c, ptr, len / SHA256_CBLOCK);
+
+ ptr += len;
+ c->Nh += len >> 29;
+ c->Nl += len <<= 3;
+ if (c->Nl < (unsigned int)len)
+ c->Nh++;
+ }
+
+ if (res)
+ SHA256_Update(c, ptr, res);
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+
+typedef struct {
+ unsigned int A[8], B[8], C[8], D[8], E[8], F[8], G[8], H[8];
+} SHA256_MB_CTX;
+
+typedef struct {
+ const unsigned char *ptr;
+ int blocks;
+} HASH_DESC;
+
+typedef struct {
+ const unsigned char *inp;
+ unsigned char *out;
+ int blocks;
+ u64 iv[2];
+} CIPH_DESC;
+
+void sha256_multi_block(SHA256_MB_CTX *, const HASH_DESC *, int);
+void aesni_multi_cbc_encrypt(CIPH_DESC *, void *, int);
+
+static size_t tls1_multi_block_encrypt(void *vctx,
+ unsigned char *out,
+ const unsigned char *inp,
+ size_t inp_len, int n4x)
+{ /* n4x is 1 or 2 */
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+ HASH_DESC hash_d[8], edges[8];
+ CIPH_DESC ciph_d[8];
+ unsigned char storage[sizeof(SHA256_MB_CTX) + 32];
+ union {
+ u64 q[16];
+ u32 d[32];
+ u8 c[128];
+ } blocks[8];
+ SHA256_MB_CTX *mctx;
+ unsigned int frag, last, packlen, i;
+ unsigned int x4 = 4 * n4x, minblocks, processed = 0;
+ size_t ret = 0;
+ u8 *IVs;
+# if defined(BSWAP8)
+ u64 seqnum;
+# endif
+
+ /* ask for IVs in bulk */
+ if (RAND_bytes_ex(ctx->base.libctx, (IVs = blocks[0].c), 16 * x4, 0) <= 0)
+ return 0;
+
+ mctx = (SHA256_MB_CTX *) (storage + 32 - ((size_t)storage % 32)); /* align */
+
+ frag = (unsigned int)inp_len >> (1 + n4x);
+ last = (unsigned int)inp_len + frag - (frag << (1 + n4x));
+ if (last > frag && ((last + 13 + 9) % 64) < (x4 - 1)) {
+ frag++;
+ last -= x4 - 1;
+ }
+
+ packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+
+ /* populate descriptors with pointers and IVs */
+ hash_d[0].ptr = inp;
+ ciph_d[0].inp = inp;
+ /* 5+16 is place for header and explicit IV */
+ ciph_d[0].out = out + 5 + 16;
+ memcpy(ciph_d[0].out - 16, IVs, 16);
+ memcpy(ciph_d[0].iv, IVs, 16);
+ IVs += 16;
+
+ for (i = 1; i < x4; i++) {
+ ciph_d[i].inp = hash_d[i].ptr = hash_d[i - 1].ptr + frag;
+ ciph_d[i].out = ciph_d[i - 1].out + packlen;
+ memcpy(ciph_d[i].out - 16, IVs, 16);
+ memcpy(ciph_d[i].iv, IVs, 16);
+ IVs += 16;
+ }
+
+# if defined(BSWAP8)
+ memcpy(blocks[0].c, sctx->md.data, 8);
+ seqnum = BSWAP8(blocks[0].q[0]);
+# endif
+
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag);
+# if !defined(BSWAP8)
+ unsigned int carry, j;
+# endif
+
+ mctx->A[i] = sctx->md.h[0];
+ mctx->B[i] = sctx->md.h[1];
+ mctx->C[i] = sctx->md.h[2];
+ mctx->D[i] = sctx->md.h[3];
+ mctx->E[i] = sctx->md.h[4];
+ mctx->F[i] = sctx->md.h[5];
+ mctx->G[i] = sctx->md.h[6];
+ mctx->H[i] = sctx->md.h[7];
+
+ /* fix seqnum */
+# if defined(BSWAP8)
+ blocks[i].q[0] = BSWAP8(seqnum + i);
+# else
+ for (carry = i, j = 8; j--;) {
+ blocks[i].c[j] = ((u8 *)sctx->md.data)[j] + carry;
+ carry = (blocks[i].c[j] - carry) >> (sizeof(carry) * 8 - 1);
+ }
+# endif
+ blocks[i].c[8] = ((u8 *)sctx->md.data)[8];
+ blocks[i].c[9] = ((u8 *)sctx->md.data)[9];
+ blocks[i].c[10] = ((u8 *)sctx->md.data)[10];
+ /* fix length */
+ blocks[i].c[11] = (u8)(len >> 8);
+ blocks[i].c[12] = (u8)(len);
+
+ memcpy(blocks[i].c + 13, hash_d[i].ptr, 64 - 13);
+ hash_d[i].ptr += 64 - 13;
+ hash_d[i].blocks = (len - (64 - 13)) / 64;
+
+ edges[i].ptr = blocks[i].c;
+ edges[i].blocks = 1;
+ }
+
+ /* hash 13-byte headers and first 64-13 bytes of inputs */
+ sha256_multi_block(mctx, edges, n4x);
+ /* hash bulk inputs */
+# define MAXCHUNKSIZE 2048
+# if MAXCHUNKSIZE%64
+# error "MAXCHUNKSIZE is not divisible by 64"
+# elif MAXCHUNKSIZE
+ /*
+ * goal is to minimize pressure on L1 cache by moving in shorter steps,
+ * so that hashed data is still in the cache by the time we encrypt it
+ */
+ minblocks = ((frag <= last ? frag : last) - (64 - 13)) / 64;
+ if (minblocks > MAXCHUNKSIZE / 64) {
+ for (i = 0; i < x4; i++) {
+ edges[i].ptr = hash_d[i].ptr;
+ edges[i].blocks = MAXCHUNKSIZE / 64;
+ ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+ }
+ do {
+ sha256_multi_block(mctx, edges, n4x);
+ aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+ for (i = 0; i < x4; i++) {
+ edges[i].ptr = hash_d[i].ptr += MAXCHUNKSIZE;
+ hash_d[i].blocks -= MAXCHUNKSIZE / 64;
+ edges[i].blocks = MAXCHUNKSIZE / 64;
+ ciph_d[i].inp += MAXCHUNKSIZE;
+ ciph_d[i].out += MAXCHUNKSIZE;
+ ciph_d[i].blocks = MAXCHUNKSIZE / 16;
+ memcpy(ciph_d[i].iv, ciph_d[i].out - 16, 16);
+ }
+ processed += MAXCHUNKSIZE;
+ minblocks -= MAXCHUNKSIZE / 64;
+ } while (minblocks > MAXCHUNKSIZE / 64);
+ }
+# endif
+# undef MAXCHUNKSIZE
+ sha256_multi_block(mctx, hash_d, n4x);
+
+ memset(blocks, 0, sizeof(blocks));
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag),
+ off = hash_d[i].blocks * 64;
+ const unsigned char *ptr = hash_d[i].ptr + off;
+
+ off = (len - processed) - (64 - 13) - off; /* remainder actually */
+ memcpy(blocks[i].c, ptr, off);
+ blocks[i].c[off] = 0x80;
+ len += 64 + 13; /* 64 is HMAC header */
+ len *= 8; /* convert to bits */
+ if (off < (64 - 8)) {
+# ifdef BSWAP4
+ blocks[i].d[15] = BSWAP4(len);
+# else
+ PUTU32(blocks[i].c + 60, len);
+# endif
+ edges[i].blocks = 1;
+ } else {
+# ifdef BSWAP4
+ blocks[i].d[31] = BSWAP4(len);
+# else
+ PUTU32(blocks[i].c + 124, len);
+# endif
+ edges[i].blocks = 2;
+ }
+ edges[i].ptr = blocks[i].c;
+ }
+
+ /* hash input tails and finalize */
+ sha256_multi_block(mctx, edges, n4x);
+
+ memset(blocks, 0, sizeof(blocks));
+ for (i = 0; i < x4; i++) {
+# ifdef BSWAP4
+ blocks[i].d[0] = BSWAP4(mctx->A[i]);
+ mctx->A[i] = sctx->tail.h[0];
+ blocks[i].d[1] = BSWAP4(mctx->B[i]);
+ mctx->B[i] = sctx->tail.h[1];
+ blocks[i].d[2] = BSWAP4(mctx->C[i]);
+ mctx->C[i] = sctx->tail.h[2];
+ blocks[i].d[3] = BSWAP4(mctx->D[i]);
+ mctx->D[i] = sctx->tail.h[3];
+ blocks[i].d[4] = BSWAP4(mctx->E[i]);
+ mctx->E[i] = sctx->tail.h[4];
+ blocks[i].d[5] = BSWAP4(mctx->F[i]);
+ mctx->F[i] = sctx->tail.h[5];
+ blocks[i].d[6] = BSWAP4(mctx->G[i]);
+ mctx->G[i] = sctx->tail.h[6];
+ blocks[i].d[7] = BSWAP4(mctx->H[i]);
+ mctx->H[i] = sctx->tail.h[7];
+ blocks[i].c[32] = 0x80;
+ blocks[i].d[15] = BSWAP4((64 + 32) * 8);
+# else
+ PUTU32(blocks[i].c + 0, mctx->A[i]);
+ mctx->A[i] = sctx->tail.h[0];
+ PUTU32(blocks[i].c + 4, mctx->B[i]);
+ mctx->B[i] = sctx->tail.h[1];
+ PUTU32(blocks[i].c + 8, mctx->C[i]);
+ mctx->C[i] = sctx->tail.h[2];
+ PUTU32(blocks[i].c + 12, mctx->D[i]);
+ mctx->D[i] = sctx->tail.h[3];
+ PUTU32(blocks[i].c + 16, mctx->E[i]);
+ mctx->E[i] = sctx->tail.h[4];
+ PUTU32(blocks[i].c + 20, mctx->F[i]);
+ mctx->F[i] = sctx->tail.h[5];
+ PUTU32(blocks[i].c + 24, mctx->G[i]);
+ mctx->G[i] = sctx->tail.h[6];
+ PUTU32(blocks[i].c + 28, mctx->H[i]);
+ mctx->H[i] = sctx->tail.h[7];
+ blocks[i].c[32] = 0x80;
+ PUTU32(blocks[i].c + 60, (64 + 32) * 8);
+# endif /* BSWAP */
+ edges[i].ptr = blocks[i].c;
+ edges[i].blocks = 1;
+ }
+
+ /* finalize MACs */
+ sha256_multi_block(mctx, edges, n4x);
+
+ for (i = 0; i < x4; i++) {
+ unsigned int len = (i == (x4 - 1) ? last : frag), pad, j;
+ unsigned char *out0 = out;
+
+ memcpy(ciph_d[i].out, ciph_d[i].inp, len - processed);
+ ciph_d[i].inp = ciph_d[i].out;
+
+ out += 5 + 16 + len;
+
+ /* write MAC */
+ PUTU32(out + 0, mctx->A[i]);
+ PUTU32(out + 4, mctx->B[i]);
+ PUTU32(out + 8, mctx->C[i]);
+ PUTU32(out + 12, mctx->D[i]);
+ PUTU32(out + 16, mctx->E[i]);
+ PUTU32(out + 20, mctx->F[i]);
+ PUTU32(out + 24, mctx->G[i]);
+ PUTU32(out + 28, mctx->H[i]);
+ out += 32;
+ len += 32;
+
+ /* pad */
+ pad = 15 - len % 16;
+ for (j = 0; j <= pad; j++)
+ *(out++) = pad;
+ len += pad + 1;
+
+ ciph_d[i].blocks = (len - processed) / 16;
+ len += 16; /* account for explicit iv */
+
+ /* arrange header */
+ out0[0] = ((u8 *)sctx->md.data)[8];
+ out0[1] = ((u8 *)sctx->md.data)[9];
+ out0[2] = ((u8 *)sctx->md.data)[10];
+ out0[3] = (u8)(len >> 8);
+ out0[4] = (u8)(len);
+
+ ret += len + 5;
+ inp += frag;
+ }
+
+ aesni_multi_cbc_encrypt(ciph_d, &ctx->ks, n4x);
+
+ OPENSSL_cleanse(blocks, sizeof(blocks));
+ OPENSSL_cleanse(mctx, sizeof(*mctx));
+
+ ctx->multiblock_encrypt_len = ret;
+ return ret;
+}
+# endif /* !OPENSSL_NO_MULTIBLOCK */
+
+static int aesni_cbc_hmac_sha256_cipher(PROV_CIPHER_CTX *vctx,
+ unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+ unsigned int l;
+ size_t plen = ctx->payload_length;
+ size_t iv = 0; /* explicit IV in TLS 1.1 and * later */
+ size_t aes_off = 0, blocks;
+ size_t sha_off = SHA256_CBLOCK - sctx->md.num;
+
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+ if (len % AES_BLOCK_SIZE)
+ return 0;
+
+ if (ctx->base.enc) {
+ if (plen == NO_PAYLOAD_LENGTH)
+ plen = len;
+ else if (len !=
+ ((plen + SHA256_DIGEST_LENGTH +
+ AES_BLOCK_SIZE) & -AES_BLOCK_SIZE))
+ return 0;
+ else if (ctx->aux.tls_ver >= TLS1_1_VERSION)
+ iv = AES_BLOCK_SIZE;
+
+ /*
+ * Assembly stitch handles AVX-capable processors, but its
+ * performance is not optimal on AMD Jaguar, ~40% worse, for
+ * unknown reasons. Incidentally processor in question supports
+ * AVX, but not AMD-specific XOP extension, which can be used
+ * to identify it and avoid stitch invocation. So that after we
+ * establish that current CPU supports AVX, we even see if it's
+ * either even XOP-capable Bulldozer-based or GenuineIntel one.
+ * But SHAEXT-capable go ahead...
+ */
+ if (((OPENSSL_ia32cap_P[2] & (1 << 29)) || /* SHAEXT? */
+ ((OPENSSL_ia32cap_P[1] & (1 << (60 - 32))) && /* AVX? */
+ ((OPENSSL_ia32cap_P[1] & (1 << (43 - 32))) /* XOP? */
+ | (OPENSSL_ia32cap_P[0] & (1 << 30))))) && /* "Intel CPU"? */
+ plen > (sha_off + iv) &&
+ (blocks = (plen - (sha_off + iv)) / SHA256_CBLOCK)) {
+ sha256_update(&sctx->md, in + iv, sha_off);
+
+ (void)aesni_cbc_sha256_enc(in, out, blocks, &ctx->ks,
+ ctx->base.iv,
+ &sctx->md, in + iv + sha_off);
+ blocks *= SHA256_CBLOCK;
+ aes_off += blocks;
+ sha_off += blocks;
+ sctx->md.Nh += blocks >> 29;
+ sctx->md.Nl += blocks <<= 3;
+ if (sctx->md.Nl < (unsigned int)blocks)
+ sctx->md.Nh++;
+ } else {
+ sha_off = 0;
+ }
+ sha_off += iv;
+ sha256_update(&sctx->md, in + sha_off, plen - sha_off);
+
+ if (plen != len) { /* "TLS" mode of operation */
+ if (in != out)
+ memcpy(out + aes_off, in + aes_off, plen - aes_off);
+
+ /* calculate HMAC and append it to payload */
+ SHA256_Final(out + plen, &sctx->md);
+ sctx->md = sctx->tail;
+ sha256_update(&sctx->md, out + plen, SHA256_DIGEST_LENGTH);
+ SHA256_Final(out + plen, &sctx->md);
+
+ /* pad the payload|hmac */
+ plen += SHA256_DIGEST_LENGTH;
+ for (l = len - plen - 1; plen < len; plen++)
+ out[plen] = l;
+ /* encrypt HMAC|padding at once */
+ aesni_cbc_encrypt(out + aes_off, out + aes_off, len - aes_off,
+ &ctx->ks, ctx->base.iv, 1);
+ } else {
+ aesni_cbc_encrypt(in + aes_off, out + aes_off, len - aes_off,
+ &ctx->ks, ctx->base.iv, 1);
+ }
+ } else {
+ union {
+ unsigned int u[SHA256_DIGEST_LENGTH / sizeof(unsigned int)];
+ unsigned char c[64 + SHA256_DIGEST_LENGTH];
+ } mac, *pmac;
+
+ /* arrange cache line alignment */
+ pmac = (void *)(((size_t)mac.c + 63) & ((size_t)0 - 64));
+
+ /* decrypt HMAC|padding at once */
+ aesni_cbc_encrypt(in, out, len, &ctx->ks,
+ ctx->base.iv, 0);
+
+ if (plen != NO_PAYLOAD_LENGTH) { /* "TLS" mode of operation */
+ size_t inp_len, mask, j, i;
+ unsigned int res, maxpad, pad, bitlen;
+ int ret = 1;
+ union {
+ unsigned int u[SHA_LBLOCK];
+ unsigned char c[SHA256_CBLOCK];
+ } *data = (void *)sctx->md.data;
+
+ if ((ctx->aux.tls_aad[plen - 4] << 8 | ctx->aux.tls_aad[plen - 3])
+ >= TLS1_1_VERSION)
+ iv = AES_BLOCK_SIZE;
+
+ if (len < (iv + SHA256_DIGEST_LENGTH + 1))
+ return 0;
+
+ /* omit explicit iv */
+ out += iv;
+ len -= iv;
+
+ /* figure out payload length */
+ pad = out[len - 1];
+ maxpad = len - (SHA256_DIGEST_LENGTH + 1);
+ maxpad |= (255 - maxpad) >> (sizeof(maxpad) * 8 - 8);
+ maxpad &= 255;
+
+ mask = constant_time_ge(maxpad, pad);
+ ret &= mask;
+ /*
+ * If pad is invalid then we will fail the above test but we must
+ * continue anyway because we are in constant time code. However,
+ * we'll use the maxpad value instead of the supplied pad to make
+ * sure we perform well defined pointer arithmetic.
+ */
+ pad = constant_time_select(mask, pad, maxpad);
+
+ inp_len = len - (SHA256_DIGEST_LENGTH + pad + 1);
+
+ ctx->aux.tls_aad[plen - 2] = inp_len >> 8;
+ ctx->aux.tls_aad[plen - 1] = inp_len;
+
+ /* calculate HMAC */
+ sctx->md = sctx->head;
+ sha256_update(&sctx->md, ctx->aux.tls_aad, plen);
+
+ /* code with lucky-13 fix */
+ len -= SHA256_DIGEST_LENGTH; /* amend mac */
+ if (len >= (256 + SHA256_CBLOCK)) {
+ j = (len - (256 + SHA256_CBLOCK)) & (0 - SHA256_CBLOCK);
+ j += SHA256_CBLOCK - sctx->md.num;
+ sha256_update(&sctx->md, out, j);
+ out += j;
+ len -= j;
+ inp_len -= j;
+ }
+
+ /* but pretend as if we hashed padded payload */
+ bitlen = sctx->md.Nl + (inp_len << 3); /* at most 18 bits */
+# ifdef BSWAP4
+ bitlen = BSWAP4(bitlen);
+# else
+ mac.c[0] = 0;
+ mac.c[1] = (unsigned char)(bitlen >> 16);
+ mac.c[2] = (unsigned char)(bitlen >> 8);
+ mac.c[3] = (unsigned char)bitlen;
+ bitlen = mac.u[0];
+# endif /* BSWAP */
+
+ pmac->u[0] = 0;
+ pmac->u[1] = 0;
+ pmac->u[2] = 0;
+ pmac->u[3] = 0;
+ pmac->u[4] = 0;
+ pmac->u[5] = 0;
+ pmac->u[6] = 0;
+ pmac->u[7] = 0;
+
+ for (res = sctx->md.num, j = 0; j < len; j++) {
+ size_t c = out[j];
+ mask = (j - inp_len) >> (sizeof(j) * 8 - 8);
+ c &= mask;
+ c |= 0x80 & ~mask & ~((inp_len - j) >> (sizeof(j) * 8 - 8));
+ data->c[res++] = (unsigned char)c;
+
+ if (res != SHA256_CBLOCK)
+ continue;
+
+ /* j is not incremented yet */
+ mask = 0 - ((inp_len + 7 - j) >> (sizeof(j) * 8 - 1));
+ data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+ sha256_block_data_order(&sctx->md, data, 1);
+ mask &= 0 - ((j - inp_len - 72) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h[0] & mask;
+ pmac->u[1] |= sctx->md.h[1] & mask;
+ pmac->u[2] |= sctx->md.h[2] & mask;
+ pmac->u[3] |= sctx->md.h[3] & mask;
+ pmac->u[4] |= sctx->md.h[4] & mask;
+ pmac->u[5] |= sctx->md.h[5] & mask;
+ pmac->u[6] |= sctx->md.h[6] & mask;
+ pmac->u[7] |= sctx->md.h[7] & mask;
+ res = 0;
+ }
+
+ for (i = res; i < SHA256_CBLOCK; i++, j++)
+ data->c[i] = 0;
+
+ if (res > SHA256_CBLOCK - 8) {
+ mask = 0 - ((inp_len + 8 - j) >> (sizeof(j) * 8 - 1));
+ data->u[SHA_LBLOCK - 1] |= bitlen & mask;
+ sha256_block_data_order(&sctx->md, data, 1);
+ mask &= 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h[0] & mask;
+ pmac->u[1] |= sctx->md.h[1] & mask;
+ pmac->u[2] |= sctx->md.h[2] & mask;
+ pmac->u[3] |= sctx->md.h[3] & mask;
+ pmac->u[4] |= sctx->md.h[4] & mask;
+ pmac->u[5] |= sctx->md.h[5] & mask;
+ pmac->u[6] |= sctx->md.h[6] & mask;
+ pmac->u[7] |= sctx->md.h[7] & mask;
+
+ memset(data, 0, SHA256_CBLOCK);
+ j += 64;
+ }
+ data->u[SHA_LBLOCK - 1] = bitlen;
+ sha256_block_data_order(&sctx->md, data, 1);
+ mask = 0 - ((j - inp_len - 73) >> (sizeof(j) * 8 - 1));
+ pmac->u[0] |= sctx->md.h[0] & mask;
+ pmac->u[1] |= sctx->md.h[1] & mask;
+ pmac->u[2] |= sctx->md.h[2] & mask;
+ pmac->u[3] |= sctx->md.h[3] & mask;
+ pmac->u[4] |= sctx->md.h[4] & mask;
+ pmac->u[5] |= sctx->md.h[5] & mask;
+ pmac->u[6] |= sctx->md.h[6] & mask;
+ pmac->u[7] |= sctx->md.h[7] & mask;
+
+# ifdef BSWAP4
+ pmac->u[0] = BSWAP4(pmac->u[0]);
+ pmac->u[1] = BSWAP4(pmac->u[1]);
+ pmac->u[2] = BSWAP4(pmac->u[2]);
+ pmac->u[3] = BSWAP4(pmac->u[3]);
+ pmac->u[4] = BSWAP4(pmac->u[4]);
+ pmac->u[5] = BSWAP4(pmac->u[5]);
+ pmac->u[6] = BSWAP4(pmac->u[6]);
+ pmac->u[7] = BSWAP4(pmac->u[7]);
+# else
+ for (i = 0; i < 8; i++) {
+ res = pmac->u[i];
+ pmac->c[4 * i + 0] = (unsigned char)(res >> 24);
+ pmac->c[4 * i + 1] = (unsigned char)(res >> 16);
+ pmac->c[4 * i + 2] = (unsigned char)(res >> 8);
+ pmac->c[4 * i + 3] = (unsigned char)res;
+ }
+# endif /* BSWAP */
+ len += SHA256_DIGEST_LENGTH;
+ sctx->md = sctx->tail;
+ sha256_update(&sctx->md, pmac->c, SHA256_DIGEST_LENGTH);
+ SHA256_Final(pmac->c, &sctx->md);
+
+ /* verify HMAC */
+ out += inp_len;
+ len -= inp_len;
+ /* code containing lucky-13 fix */
+ {
+ unsigned char *p =
+ out + len - 1 - maxpad - SHA256_DIGEST_LENGTH;
+ size_t off = out - p;
+ unsigned int c, cmask;
+
+ for (res = 0, i = 0, j = 0;
+ j < maxpad + SHA256_DIGEST_LENGTH;
+ j++) {
+ c = p[j];
+ cmask =
+ ((int)(j - off - SHA256_DIGEST_LENGTH)) >>
+ (sizeof(int) * 8 - 1);
+ res |= (c ^ pad) & ~cmask; /* ... and padding */
+ cmask &= ((int)(off - 1 - j)) >> (sizeof(int) * 8 - 1);
+ res |= (c ^ pmac->c[i]) & cmask;
+ i += 1 & cmask;
+ }
+
+ res = 0 - ((0 - res) >> (sizeof(res) * 8 - 1));
+ ret &= (int)~res;
+ }
+ return ret;
+ } else {
+ sha256_update(&sctx->md, out, len);
+ }
+ }
+
+ return 1;
+}
+
+/* EVP_CTRL_AEAD_SET_MAC_KEY */
+static void aesni_cbc_hmac_sha256_set_mac_key(void *vctx,
+ const unsigned char *mackey,
+ size_t len)
+{
+ PROV_AES_HMAC_SHA256_CTX *ctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+ unsigned int i;
+ unsigned char hmac_key[64];
+
+ memset(hmac_key, 0, sizeof(hmac_key));
+
+ if (len > sizeof(hmac_key)) {
+ SHA256_Init(&ctx->head);
+ sha256_update(&ctx->head, mackey, len);
+ SHA256_Final(hmac_key, &ctx->head);
+ } else {
+ memcpy(hmac_key, mackey, len);
+ }
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36; /* ipad */
+ SHA256_Init(&ctx->head);
+ sha256_update(&ctx->head, hmac_key, sizeof(hmac_key));
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+ SHA256_Init(&ctx->tail);
+ sha256_update(&ctx->tail, hmac_key, sizeof(hmac_key));
+
+ OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+}
+
+/* EVP_CTRL_AEAD_TLS1_AAD */
+static int aesni_cbc_hmac_sha256_set_tls1_aad(void *vctx,
+ unsigned char *aad_rec, int aad_len)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+ unsigned char *p = aad_rec;
+ unsigned int len;
+
+ if (aad_len != EVP_AEAD_TLS1_AAD_LEN)
+ return -1;
+
+ len = p[aad_len - 2] << 8 | p[aad_len - 1];
+
+ if (ctx->base.enc) {
+ ctx->payload_length = len;
+ if ((ctx->aux.tls_ver =
+ p[aad_len - 4] << 8 | p[aad_len - 3]) >= TLS1_1_VERSION) {
+ if (len < AES_BLOCK_SIZE)
+ return 0;
+ len -= AES_BLOCK_SIZE;
+ p[aad_len - 2] = len >> 8;
+ p[aad_len - 1] = len;
+ }
+ sctx->md = sctx->head;
+ sha256_update(&sctx->md, p, aad_len);
+ ctx->tls_aad_pad = (int)(((len + SHA256_DIGEST_LENGTH +
+ AES_BLOCK_SIZE) & -AES_BLOCK_SIZE)
+ - len);
+ return 1;
+ } else {
+ memcpy(ctx->aux.tls_aad, p, aad_len);
+ ctx->payload_length = aad_len;
+ ctx->tls_aad_pad = SHA256_DIGEST_LENGTH;
+ return 1;
+ }
+}
+
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize(
+ void *vctx)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+
+ OPENSSL_assert(ctx->multiblock_max_send_fragment != 0);
+ return (int)(5 + 16
+ + (((int)ctx->multiblock_max_send_fragment + 32 + 16) & -16));
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_AAD */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_aad(
+ void *vctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+ PROV_AES_HMAC_SHA_CTX *ctx = (PROV_AES_HMAC_SHA_CTX *)vctx;
+ PROV_AES_HMAC_SHA256_CTX *sctx = (PROV_AES_HMAC_SHA256_CTX *)vctx;
+ unsigned int n4x = 1, x4;
+ unsigned int frag, last, packlen, inp_len;
+
+ inp_len = param->inp[11] << 8 | param->inp[12];
+
+ if (ctx->base.enc) {
+ if ((param->inp[9] << 8 | param->inp[10]) < TLS1_1_VERSION)
+ return -1;
+
+ if (inp_len) {
+ if (inp_len < 4096)
+ return 0; /* too short */
+
+ if (inp_len >= 8192 && OPENSSL_ia32cap_P[2] & (1 << 5))
+ n4x = 2; /* AVX2 */
+ } else if ((n4x = param->interleave / 4) && n4x <= 2)
+ inp_len = param->len;
+ else
+ return -1;
+
+ sctx->md = sctx->head;
+ sha256_update(&sctx->md, param->inp, 13);
+
+ x4 = 4 * n4x;
+ n4x += 1;
+
+ frag = inp_len >> n4x;
+ last = inp_len + frag - (frag << n4x);
+ if (last > frag && ((last + 13 + 9) % 64 < (x4 - 1))) {
+ frag++;
+ last -= x4 - 1;
+ }
+
+ packlen = 5 + 16 + ((frag + 32 + 16) & -16);
+ packlen = (packlen << n4x) - packlen;
+ packlen += 5 + 16 + ((last + 32 + 16) & -16);
+
+ param->interleave = x4;
+ /* The returned values used by get need to be stored */
+ ctx->multiblock_interleave = x4;
+ ctx->multiblock_aad_packlen = packlen;
+ return 1;
+ }
+ return -1; /* not yet */
+}
+
+/* EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT */
+static int aesni_cbc_hmac_sha256_tls1_multiblock_encrypt(
+ void *ctx, EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM *param)
+{
+ return (int)tls1_multi_block_encrypt(ctx, param->out,
+ param->inp, param->len,
+ param->interleave / 4);
+}
+# endif
+
+static const PROV_CIPHER_HW_AES_HMAC_SHA cipher_hw_aes_hmac_sha256 = {
+ {
+ aesni_cbc_hmac_sha256_init_key,
+ aesni_cbc_hmac_sha256_cipher
+ },
+ aesni_cbc_hmac_sha256_set_mac_key,
+ aesni_cbc_hmac_sha256_set_tls1_aad,
+# if !defined(OPENSSL_NO_MULTIBLOCK)
+ aesni_cbc_hmac_sha256_tls1_multiblock_max_bufsize,
+ aesni_cbc_hmac_sha256_tls1_multiblock_aad,
+ aesni_cbc_hmac_sha256_tls1_multiblock_encrypt
+# endif
+};
+
+const PROV_CIPHER_HW_AES_HMAC_SHA *ossl_prov_cipher_hw_aes_cbc_hmac_sha256(void)
+{
+ return &cipher_hw_aes_hmac_sha256;
+}
+
+#endif /* !defined(AES_CBC_HMAC_SHA_CAPABLE) || !defined(AESNI_CAPABLE) */
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c
new file mode 100644
index 000000000000..e36ac03e6136
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for AES CCM mode */
+
+#include "cipher_aes_ccm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static void *aes_ccm_newctx(void *provctx, size_t keybits)
+{
+ PROV_AES_CCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_aes_hw_ccm(keybits));
+ return ctx;
+}
+
+static void *aes_ccm_dupctx(void *provctx)
+{
+ PROV_AES_CCM_CTX *ctx = provctx;
+ PROV_AES_CCM_CTX *dupctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (ctx == NULL)
+ return NULL;
+ dupctx = OPENSSL_memdup(provctx, sizeof(*ctx));
+ if (dupctx == NULL)
+ return NULL;
+ /*
+ * ossl_cm_initctx, via the ossl_prov_aes_hw_ccm functions assign a
+ * provctx->ccm.ks.ks to the ccm context key so we need to point it to
+ * the memduped copy
+ */
+ dupctx->base.ccm_ctx.key = &dupctx->ccm.ks.ks;
+
+ return dupctx;
+}
+
+static OSSL_FUNC_cipher_freectx_fn aes_ccm_freectx;
+static void aes_ccm_freectx(void *vctx)
+{
+ PROV_AES_CCM_CTX *ctx = (PROV_AES_CCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* ossl_aes128ccm_functions */
+IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 128, 8, 96);
+/* ossl_aes192ccm_functions */
+IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 192, 8, 96);
+/* ossl_aes256ccm_functions */
+IMPLEMENT_aead_cipher(aes, ccm, CCM, AEAD_FLAGS, 256, 8, 96);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h
new file mode 100644
index 000000000000..fd35080db3dd
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_ccm.h"
+#include "crypto/aes_platform.h"
+
+typedef struct prov_aes_ccm_ctx_st {
+ PROV_CCM_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ /*-
+ * Padding is chosen so that s390x.kmac.k overlaps with ks.ks and
+ * fc with ks.ks.rounds. Remember that on s390x, an AES_KEY's
+ * rounds field is used to store the function code and that the key
+ * schedule is not stored (if aes hardware support is detected).
+ */
+ struct {
+ unsigned char pad[16];
+ AES_KEY ks;
+ } ks;
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+ struct {
+ S390X_KMAC_PARAMS kmac;
+ unsigned long long blocks;
+ union {
+ unsigned long long g[2];
+ unsigned char b[AES_BLOCK_SIZE];
+ } nonce;
+ union {
+ unsigned long long g[2];
+ unsigned char b[AES_BLOCK_SIZE];
+ } buf;
+ unsigned char dummy_pad[168];
+ unsigned int fc; /* fc has same offset as ks.ks.rounds */
+ } s390x;
+#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */
+ } ccm;
+} PROV_AES_CCM_CTX;
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keylen);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c
new file mode 100644
index 000000000000..b050cf3edd88
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* AES CCM mode */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_ccm.h"
+
+#define AES_HW_CCM_SET_KEY_FN(fn_set_enc_key, fn_blk, fn_ccm_enc, fn_ccm_dec) \
+ fn_set_enc_key(key, keylen * 8, &actx->ccm.ks.ks); \
+ CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ccm.ks.ks, \
+ (block128_f)fn_blk); \
+ ctx->str = ctx->enc ? (ccm128_f)fn_ccm_enc : (ccm128_f)fn_ccm_dec; \
+ ctx->key_set = 1;
+
+static int ccm_generic_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+#ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+ AES_HW_CCM_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_encrypt, NULL, NULL);
+ } else
+#endif /* HWAES_CAPABLE */
+
+#ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ AES_HW_CCM_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_encrypt, NULL, NULL);
+ } else
+#endif
+ {
+ AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, AES_encrypt, NULL, NULL)
+ }
+ return 1;
+}
+
+static const PROV_CCM_HW aes_ccm = {
+ ccm_generic_aes_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+#if defined(S390X_aes_128_CAPABLE)
+# include "cipher_aes_ccm_hw_s390x.inc"
+#elif defined(AESNI_CAPABLE)
+# include "cipher_aes_ccm_hw_aesni.inc"
+#elif defined(SPARC_AES_CAPABLE)
+# include "cipher_aes_ccm_hw_t4.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_aes_ccm_hw_rv64i.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
+# include "cipher_aes_ccm_hw_rv32i.inc"
+#else
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ return &aes_ccm;
+}
+#endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc
new file mode 100644
index 000000000000..579e5a3d4f13
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_aesni.inc
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * AES-NI support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+static int ccm_aesni_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ AES_HW_CCM_SET_KEY_FN(aesni_set_encrypt_key, aesni_encrypt,
+ aesni_ccm64_encrypt_blocks,
+ aesni_ccm64_decrypt_blocks);
+ return 1;
+}
+
+static const PROV_CCM_HW aesni_ccm = {
+ ccm_aesni_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ return AESNI_CAPABLE ? &aesni_ccm : &aes_ccm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc
new file mode 100644
index 000000000000..7cfe0fc4ce8b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv32i.inc
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 32 ZKND ZKNE support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+static int ccm_rv32i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ AES_HW_CCM_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt,
+ NULL, NULL);
+ return 1;
+}
+
+static int ccm_rv32i_zbkb_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ AES_HW_CCM_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt,
+ NULL, NULL);
+ return 1;
+}
+
+static const PROV_CCM_HW rv32i_zknd_zkne_ccm = {
+ ccm_rv32i_zknd_zkne_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+static const PROV_CCM_HW rv32i_zbkb_zknd_zkne_ccm = {
+ ccm_rv32i_zbkb_zknd_zkne_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE())
+ return &rv32i_zbkb_zknd_zkne_ccm;
+ if (RISCV_HAS_ZKND_AND_ZKNE())
+ return &rv32i_zknd_zkne_ccm;
+ return &aes_ccm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc
new file mode 100644
index 000000000000..f2353bb3b8f8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_rv64i.inc
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 64 ZKND ZKNE support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+static int ccm_rv64i_zknd_zkne_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ AES_HW_CCM_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt,
+ NULL, NULL);
+ return 1;
+}
+
+static const PROV_CCM_HW rv64i_zknd_zkne_ccm = {
+ ccm_rv64i_zknd_zkne_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+/*-
+ * RISC-V RV64 ZVKNED support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+static int ccm_rv64i_zvkned_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ /* Zvkned only supports 128 and 256 bit keys for key schedule generation. */
+ if (keylen * 8 == 128 || keylen * 8 == 256) {
+ AES_HW_CCM_SET_KEY_FN(rv64i_zvkned_set_encrypt_key, rv64i_zvkned_encrypt,
+ NULL, NULL);
+ } else {
+ AES_HW_CCM_SET_KEY_FN(AES_set_encrypt_key, rv64i_zvkned_encrypt, NULL, NULL)
+ }
+ return 1;
+}
+
+static const PROV_CCM_HW rv64i_zvkned_ccm = {
+ ccm_rv64i_zvkned_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128)
+ return &rv64i_zvkned_ccm;
+ else if (RISCV_HAS_ZKND_AND_ZKNE())
+ return &rv64i_zknd_zkne_ccm;
+ else
+ return &aes_ccm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc
new file mode 100644
index 000000000000..7253f03a7ef1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_s390x.inc
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2001-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * S390X support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+#define S390X_CCM_AAD_FLAG 0x40
+
+static int s390x_aes_ccm_initkey(PROV_CCM_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+
+ sctx->ccm.s390x.fc = S390X_AES_FC(keylen);
+ memcpy(&sctx->ccm.s390x.kmac.k, key, keylen);
+ /* Store encoded m and l. */
+ sctx->ccm.s390x.nonce.b[0] = ((ctx->l - 1) & 0x7)
+ | (((ctx->m - 2) >> 1) & 0x7) << 3;
+ memset(sctx->ccm.s390x.nonce.b + 1, 0, sizeof(sctx->ccm.s390x.nonce.b));
+ sctx->ccm.s390x.blocks = 0;
+ ctx->key_set = 1;
+ return 1;
+}
+
+static int s390x_aes_ccm_setiv(PROV_CCM_CTX *ctx,
+ const unsigned char *nonce, size_t noncelen,
+ size_t mlen)
+{
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+
+ sctx->ccm.s390x.nonce.b[0] &= ~S390X_CCM_AAD_FLAG;
+ sctx->ccm.s390x.nonce.g[1] = mlen;
+ memcpy(sctx->ccm.s390x.nonce.b + 1, nonce, 15 - ctx->l);
+ return 1;
+}
+
+/*-
+ * Process additional authenticated data. Code is big-endian.
+ */
+static int s390x_aes_ccm_setaad(PROV_CCM_CTX *ctx,
+ const unsigned char *aad, size_t alen)
+{
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+ unsigned char *ptr;
+ int i, rem;
+
+ if (!alen)
+ return 1;
+
+ sctx->ccm.s390x.nonce.b[0] |= S390X_CCM_AAD_FLAG;
+
+ /* Suppress 'type-punned pointer dereference' warning. */
+ ptr = sctx->ccm.s390x.buf.b;
+
+ if (alen < ((1 << 16) - (1 << 8))) {
+ *(uint16_t *)ptr = alen;
+ i = 2;
+ } else if (sizeof(alen) == 8
+ && alen >= (size_t)1 << (32 % (sizeof(alen) * 8))) {
+ *(uint16_t *)ptr = 0xffff;
+ *(uint64_t *)(ptr + 2) = alen;
+ i = 10;
+ } else {
+ *(uint16_t *)ptr = 0xfffe;
+ *(uint32_t *)(ptr + 2) = alen;
+ i = 6;
+ }
+
+ while (i < 16 && alen) {
+ sctx->ccm.s390x.buf.b[i] = *aad;
+ ++aad;
+ --alen;
+ ++i;
+ }
+ while (i < 16) {
+ sctx->ccm.s390x.buf.b[i] = 0;
+ ++i;
+ }
+
+ sctx->ccm.s390x.kmac.icv.g[0] = 0;
+ sctx->ccm.s390x.kmac.icv.g[1] = 0;
+ s390x_kmac(sctx->ccm.s390x.nonce.b, 32, sctx->ccm.s390x.fc,
+ &sctx->ccm.s390x.kmac);
+ sctx->ccm.s390x.blocks += 2;
+
+ rem = alen & 0xf;
+ alen &= ~(size_t)0xf;
+ if (alen) {
+ s390x_kmac(aad, alen, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
+ sctx->ccm.s390x.blocks += alen >> 4;
+ aad += alen;
+ }
+ if (rem) {
+ for (i = 0; i < rem; i++)
+ sctx->ccm.s390x.kmac.icv.b[i] ^= aad[i];
+
+ s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
+ sctx->ccm.s390x.kmac.icv.b, sctx->ccm.s390x.fc,
+ sctx->ccm.s390x.kmac.k);
+ sctx->ccm.s390x.blocks++;
+ }
+ return 1;
+}
+
+/*-
+ * En/de-crypt plain/cipher-text. Compute tag from plaintext. Returns 1 for
+ * success.
+ */
+static int s390x_aes_ccm_auth_encdec(PROV_CCM_CTX *ctx,
+ const unsigned char *in,
+ unsigned char *out, size_t len, int enc)
+{
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+ size_t n, rem;
+ unsigned int i, l, num;
+ unsigned char flags;
+
+ flags = sctx->ccm.s390x.nonce.b[0];
+ if (!(flags & S390X_CCM_AAD_FLAG)) {
+ s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.kmac.icv.b,
+ sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
+ sctx->ccm.s390x.blocks++;
+ }
+ l = flags & 0x7;
+ sctx->ccm.s390x.nonce.b[0] = l;
+
+ /*-
+ * Reconstruct length from encoded length field
+ * and initialize it with counter value.
+ */
+ n = 0;
+ for (i = 15 - l; i < 15; i++) {
+ n |= sctx->ccm.s390x.nonce.b[i];
+ sctx->ccm.s390x.nonce.b[i] = 0;
+ n <<= 8;
+ }
+ n |= sctx->ccm.s390x.nonce.b[15];
+ sctx->ccm.s390x.nonce.b[15] = 1;
+
+ if (n != len)
+ return 0; /* length mismatch */
+
+ if (enc) {
+ /* Two operations per block plus one for tag encryption */
+ sctx->ccm.s390x.blocks += (((len + 15) >> 4) << 1) + 1;
+ if (sctx->ccm.s390x.blocks > (1ULL << 61))
+ return 0; /* too much data */
+ }
+
+ num = 0;
+ rem = len & 0xf;
+ len &= ~(size_t)0xf;
+
+ if (enc) {
+ /* mac-then-encrypt */
+ if (len)
+ s390x_kmac(in, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
+ if (rem) {
+ for (i = 0; i < rem; i++)
+ sctx->ccm.s390x.kmac.icv.b[i] ^= in[len + i];
+
+ s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
+ sctx->ccm.s390x.kmac.icv.b,
+ sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
+ }
+
+ CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks,
+ sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b,
+ &num, (ctr128_f)AES_ctr32_encrypt);
+ } else {
+ /* decrypt-then-mac */
+ CRYPTO_ctr128_encrypt_ctr32(in, out, len + rem, &sctx->ccm.ks.ks,
+ sctx->ccm.s390x.nonce.b, sctx->ccm.s390x.buf.b,
+ &num, (ctr128_f)AES_ctr32_encrypt);
+
+ if (len)
+ s390x_kmac(out, len, sctx->ccm.s390x.fc, &sctx->ccm.s390x.kmac);
+ if (rem) {
+ for (i = 0; i < rem; i++)
+ sctx->ccm.s390x.kmac.icv.b[i] ^= out[len + i];
+
+ s390x_km(sctx->ccm.s390x.kmac.icv.b, 16,
+ sctx->ccm.s390x.kmac.icv.b,
+ sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
+ }
+ }
+ /* encrypt tag */
+ for (i = 15 - l; i < 16; i++)
+ sctx->ccm.s390x.nonce.b[i] = 0;
+
+ s390x_km(sctx->ccm.s390x.nonce.b, 16, sctx->ccm.s390x.buf.b,
+ sctx->ccm.s390x.fc, sctx->ccm.s390x.kmac.k);
+ sctx->ccm.s390x.kmac.icv.g[0] ^= sctx->ccm.s390x.buf.g[0];
+ sctx->ccm.s390x.kmac.icv.g[1] ^= sctx->ccm.s390x.buf.g[1];
+
+ sctx->ccm.s390x.nonce.b[0] = flags; /* restore flags field */
+ return 1;
+}
+
+
+static int s390x_aes_ccm_gettag(PROV_CCM_CTX *ctx,
+ unsigned char *tag, size_t tlen)
+{
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+
+ if (tlen > ctx->m)
+ return 0;
+ memcpy(tag, sctx->ccm.s390x.kmac.icv.b, tlen);
+ return 1;
+}
+
+static int s390x_aes_ccm_auth_encrypt(PROV_CCM_CTX *ctx,
+ const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *tag, size_t taglen)
+{
+ int rv;
+
+ rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 1);
+ if (rv && tag != NULL)
+ rv = s390x_aes_ccm_gettag(ctx, tag, taglen);
+ return rv;
+}
+
+static int s390x_aes_ccm_auth_decrypt(PROV_CCM_CTX *ctx,
+ const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *expected_tag,
+ size_t taglen)
+{
+ int rv = 0;
+ PROV_AES_CCM_CTX *sctx = (PROV_AES_CCM_CTX *)ctx;
+
+ rv = s390x_aes_ccm_auth_encdec(ctx, in, out, len, 0);
+ if (rv) {
+ if (CRYPTO_memcmp(sctx->ccm.s390x.kmac.icv.b, expected_tag, ctx->m) != 0)
+ rv = 0;
+ }
+ if (rv == 0)
+ OPENSSL_cleanse(out, len);
+ return rv;
+}
+
+static const PROV_CCM_HW s390x_aes_ccm = {
+ s390x_aes_ccm_initkey,
+ s390x_aes_ccm_setiv,
+ s390x_aes_ccm_setaad,
+ s390x_aes_ccm_auth_encrypt,
+ s390x_aes_ccm_auth_decrypt,
+ s390x_aes_ccm_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ if ((keybits == 128 && S390X_aes_128_ccm_CAPABLE)
+ || (keybits == 192 && S390X_aes_192_ccm_CAPABLE)
+ || (keybits == 256 && S390X_aes_256_ccm_CAPABLE))
+ return &s390x_aes_ccm;
+ return &aes_ccm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc
new file mode 100644
index 000000000000..a676d411b598
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ccm_hw_t4.inc
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Fujitsu SPARC64 X support for AES CCM.
+ * This file is included by cipher_aes_ccm_hw.c
+ */
+
+static int ccm_t4_aes_initkey(PROV_CCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_CCM_CTX *actx = (PROV_AES_CCM_CTX *)ctx;
+
+ AES_HW_CCM_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_encrypt, NULL, NULL);
+ return 1;
+}
+
+static const PROV_CCM_HW t4_aes_ccm = {
+ ccm_t4_aes_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_aes_hw_ccm(size_t keybits)
+{
+ return SPARC_AES_CAPABLE ? &t4_aes_ccm : &aes_ccm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc
new file mode 100644
index 000000000000..1fb5ec3553f9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_cts.inc
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for AES CBC CTS ciphers */
+
+#include <openssl/proverr.h>
+#include "cipher_cts.h"
+
+#define CTS_FLAGS PROV_CIPHER_FLAG_CTS
+
+static OSSL_FUNC_cipher_encrypt_init_fn aes_cbc_cts_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn aes_cbc_cts_dinit;
+static OSSL_FUNC_cipher_get_ctx_params_fn aes_cbc_cts_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_cbc_cts_set_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn aes_cbc_cts_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn aes_cbc_cts_settable_ctx_params;
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(aes_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(aes_cbc_cts)
+
+static int aes_cbc_cts_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return aes_cbc_cts_set_ctx_params(ctx, params);
+}
+
+static int aes_cbc_cts_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return aes_cbc_cts_set_ctx_params(ctx, params);
+}
+
+static int aes_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS_MODE);
+ if (p != NULL) {
+ const char *name = ossl_cipher_cbc_cts_mode_id2name(ctx->cts_mode);
+
+ if (name == NULL || !OSSL_PARAM_set_utf8_string(p, name)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ return ossl_cipher_generic_get_ctx_params(vctx, params);
+}
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(aes_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(aes_cbc_cts)
+
+static int aes_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+ int id;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_CTS_MODE);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ goto err;
+ id = ossl_cipher_cbc_cts_mode_name2id(p->data);
+ if (id < 0)
+ goto err;
+
+ ctx->cts_mode = (unsigned int)id;
+ }
+ return ossl_cipher_generic_set_ctx_params(vctx, params);
+err:
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+}
+
+/* ossl_aes256cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 256, 128, 128, block)
+/* ossl_aes192cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 192, 128, 128, block)
+/* ossl_aes128cbc_cts_functions */
+IMPLEMENT_cts_cipher(aes, AES, cbc, CBC, CTS_FLAGS, 128, 128, 128, block)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c
new file mode 100644
index 000000000000..c14c1e32fe35
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for AES GCM mode */
+
+#include "cipher_aes_gcm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static void *aes_gcm_newctx(void *provctx, size_t keybits)
+{
+ PROV_AES_GCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_gcm_initctx(provctx, &ctx->base, keybits,
+ ossl_prov_aes_hw_gcm(keybits));
+ return ctx;
+}
+
+static void *aes_gcm_dupctx(void *provctx)
+{
+ PROV_AES_GCM_CTX *ctx = provctx;
+ PROV_AES_GCM_CTX *dctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.gcm.key != NULL)
+ dctx->base.gcm.key = &dctx->ks.ks;
+
+ return dctx;
+}
+
+static OSSL_FUNC_cipher_freectx_fn aes_gcm_freectx;
+static void aes_gcm_freectx(void *vctx)
+{
+ PROV_AES_GCM_CTX *ctx = (PROV_AES_GCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* ossl_aes128gcm_functions */
+IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 128, 8, 96);
+/* ossl_aes192gcm_functions */
+IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 192, 8, 96);
+/* ossl_aes256gcm_functions */
+IMPLEMENT_aead_cipher(aes, gcm, GCM, AEAD_FLAGS, 256, 8, 96);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h
new file mode 100644
index 000000000000..5e88ccca7b4d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_gcm.h"
+#include "crypto/aes_platform.h"
+
+typedef struct prov_aes_gcm_ctx_st {
+ PROV_GCM_CTX base; /* must be first entry in struct */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ks; /* AES key schedule to use */
+
+ /* Platform specific data */
+ union {
+ int dummy;
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+ struct {
+ union {
+ OSSL_UNION_ALIGN;
+ S390X_KMA_PARAMS kma;
+ } param;
+ unsigned int fc;
+ unsigned int hsflag; /* hash subkey set flag */
+ unsigned char ares[16];
+ unsigned char mres[16];
+ unsigned char kres[16];
+ int areslen;
+ int mreslen;
+ int kreslen;
+ int res;
+ } s390x;
+#endif /* defined(OPENSSL_CPUID_OBJ) && defined(__s390__) */
+ } plat;
+} PROV_AES_GCM_CTX;
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c
new file mode 100644
index 000000000000..207a16bc70e7
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for AES GCM mode */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_gcm.h"
+
+static int aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+# ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+# ifdef HWAES_ctr32_encrypt_blocks
+ GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt,
+ HWAES_ctr32_encrypt_blocks);
+# else
+ GCM_HW_SET_KEY_CTR_FN(ks, HWAES_set_encrypt_key, HWAES_encrypt, NULL);
+# endif /* HWAES_ctr32_encrypt_blocks */
+ } else
+# endif /* HWAES_CAPABLE */
+
+# ifdef BSAES_CAPABLE
+ if (BSAES_CAPABLE) {
+ GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt,
+ ossl_bsaes_ctr32_encrypt_blocks);
+ } else
+# endif /* BSAES_CAPABLE */
+
+# ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ GCM_HW_SET_KEY_CTR_FN(ks, vpaes_set_encrypt_key, vpaes_encrypt, NULL);
+ } else
+# endif /* VPAES_CAPABLE */
+
+ {
+# ifdef AES_CTR_ASM
+ GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt,
+ AES_ctr32_encrypt);
+# else
+ GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key, AES_encrypt, NULL);
+# endif /* AES_CTR_ASM */
+ }
+ return 1;
+}
+
+static int generic_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out)
+{
+ if (ctx->enc) {
+ if (ctx->ctr != NULL) {
+#if defined(AES_GCM_ASM)
+ size_t bulk = 0;
+
+ if (len >= AES_GCM_ENC_BYTES && AES_GCM_ASM(ctx)) {
+ size_t res = (16 - ctx->gcm.mres) % 16;
+
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, res))
+ return 0;
+
+ bulk = AES_gcm_encrypt(in + res, out + res, len - res,
+ ctx->gcm.key,
+ ctx->gcm.Yi.c, ctx->gcm.Xi.u);
+
+ ctx->gcm.len.u[1] += bulk;
+ bulk += res;
+ }
+ if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in + bulk, out + bulk,
+ len - bulk, ctx->ctr))
+ return 0;
+#else
+ if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr))
+ return 0;
+#endif /* AES_GCM_ASM */
+ } else {
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ } else {
+ if (ctx->ctr != NULL) {
+#if defined(AES_GCM_ASM)
+ size_t bulk = 0;
+
+ if (len >= AES_GCM_DEC_BYTES && AES_GCM_ASM(ctx)) {
+ size_t res = (16 - ctx->gcm.mres) % 16;
+
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, res))
+ return 0;
+
+ bulk = AES_gcm_decrypt(in + res, out + res, len - res,
+ ctx->gcm.key,
+ ctx->gcm.Yi.c, ctx->gcm.Xi.u);
+
+ ctx->gcm.len.u[1] += bulk;
+ bulk += res;
+ }
+ if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in + bulk, out + bulk,
+ len - bulk, ctx->ctr))
+ return 0;
+#else
+ if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr))
+ return 0;
+#endif /* AES_GCM_ASM */
+ } else {
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const PROV_GCM_HW aes_gcm = {
+ aes_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+#if defined(S390X_aes_128_CAPABLE)
+# include "cipher_aes_gcm_hw_s390x.inc"
+#elif defined(AESNI_CAPABLE)
+# include "cipher_aes_gcm_hw_aesni.inc"
+#elif defined(SPARC_AES_CAPABLE)
+# include "cipher_aes_gcm_hw_t4.inc"
+#elif defined(AES_PMULL_CAPABLE) && defined(AES_GCM_ASM)
+# include "cipher_aes_gcm_hw_armv8.inc"
+#elif defined(PPC_AES_GCM_CAPABLE) && defined(_ARCH_PPC64)
+# include "cipher_aes_gcm_hw_ppc.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_aes_gcm_hw_rv64i.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
+# include "cipher_aes_gcm_hw_rv32i.inc"
+#else
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ return &aes_gcm;
+}
+#endif
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc
new file mode 100644
index 000000000000..92f41b8cd6e1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_aesni.inc
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * AES-NI support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+static int aesni_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+ GCM_HW_SET_KEY_CTR_FN(ks, aesni_set_encrypt_key, aesni_encrypt,
+ aesni_ctr32_encrypt_blocks);
+ return 1;
+}
+
+static const PROV_GCM_HW aesni_gcm = {
+ aesni_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+#include "cipher_aes_gcm_hw_vaes_avx512.inc"
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+#ifdef VAES_GCM_ENABLED
+ if (ossl_vaes_vpclmulqdq_capable())
+ return &vaes_gcm;
+ else
+#endif
+ if (AESNI_CAPABLE)
+ return &aesni_gcm;
+ else
+ return &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc
new file mode 100644
index 000000000000..60fff493d809
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_armv8.inc
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Crypto extension support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+size_t armv8_aes_gcm_encrypt(const unsigned char *in, unsigned char *out, size_t len,
+ const void *key, unsigned char ivec[16], u64 *Xi)
+{
+ AES_KEY *aes_key = (AES_KEY *)key;
+ size_t align_bytes = len - len % 16;
+
+ switch(aes_key->rounds) {
+ case 10:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_enc_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ case 12:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_enc_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ case 14:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_enc_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ }
+ return align_bytes;
+}
+
+size_t armv8_aes_gcm_decrypt(const unsigned char *in, unsigned char *out, size_t len,
+ const void *key, unsigned char ivec[16], u64 *Xi)
+{
+ AES_KEY *aes_key = (AES_KEY *)key;
+ size_t align_bytes = len - len % 16;
+
+ switch(aes_key->rounds) {
+ case 10:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_dec_128_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ case 12:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_dec_192_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ case 14:
+ if (IS_CPU_SUPPORT_UNROLL8_EOR3()) {
+ unroll8_eor3_aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ } else {
+ aes_gcm_dec_256_kernel(in, align_bytes * 8, out, (uint64_t *)Xi, ivec, key);
+ }
+ break;
+ }
+ return align_bytes;
+}
+
+static int armv8_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ if (AES_UNROLL12_EOR3_CAPABLE) {
+ GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt,
+ aes_v8_ctr32_encrypt_blocks_unroll12_eor3);
+ } else {
+ GCM_HW_SET_KEY_CTR_FN(ks, aes_v8_set_encrypt_key, aes_v8_encrypt,
+ aes_v8_ctr32_encrypt_blocks);
+ }
+ return 1;
+}
+
+
+static const PROV_GCM_HW armv8_aes_gcm = {
+ armv8_aes_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ return AES_PMULL_CAPABLE ? &armv8_aes_gcm : &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc
new file mode 100644
index 000000000000..153eb7989171
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_ppc.inc
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * PPC support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+static int aes_ppc_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ GCM_HW_SET_KEY_CTR_FN(ks, aes_p8_set_encrypt_key, aes_p8_encrypt,
+ aes_p8_ctr32_encrypt_blocks);
+ return 1;
+}
+
+static inline u32 UTO32(unsigned char *buf)
+{
+ return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | ((u32) buf[2] << 8) | ((u32) buf[3]);
+}
+
+static inline u32 add32TOU(unsigned char buf[4], u32 n)
+{
+ u32 r;
+
+ r = UTO32(buf);
+ r += n;
+ buf[0] = (unsigned char) (r >> 24) & 0xFF;
+ buf[1] = (unsigned char) (r >> 16) & 0xFF;
+ buf[2] = (unsigned char) (r >> 8) & 0xFF;
+ buf[3] = (unsigned char) r & 0xFF;
+ return r;
+}
+
+static size_t ppc_aes_gcm_crypt(const unsigned char *in, unsigned char *out, size_t len,
+ const void *key, unsigned char ivec[16], u64 *Xi, int encrypt)
+{
+ int s = 0;
+ int ndone = 0;
+ int ctr_reset = 0;
+ u64 blocks_unused;
+ u64 nb = len / 16;
+ u64 next_ctr = 0;
+ unsigned char ctr_saved[12];
+
+ memcpy(ctr_saved, ivec, 12);
+
+ while (nb) {
+ blocks_unused = (u64) 0xffffffffU + 1 - (u64) UTO32 (ivec + 12);
+ if (nb > blocks_unused) {
+ len = blocks_unused * 16;
+ nb -= blocks_unused;
+ next_ctr = blocks_unused;
+ ctr_reset = 1;
+ } else {
+ len = nb * 16;
+ next_ctr = nb;
+ nb = 0;
+ }
+
+ s = encrypt ? ppc_aes_gcm_encrypt(in, out, len, key, ivec, Xi)
+ : ppc_aes_gcm_decrypt(in, out, len, key, ivec, Xi);
+
+ /* add counter to ivec */
+ add32TOU(ivec + 12, (u32) next_ctr);
+ if (ctr_reset) {
+ ctr_reset = 0;
+ in += len;
+ out += len;
+ }
+ memcpy(ivec, ctr_saved, 12);
+ ndone += s;
+ }
+
+ return ndone;
+}
+
+static int ppc_aes_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out)
+{
+ if (ctx->enc) {
+ if (ctx->ctr != NULL) {
+ size_t bulk = 0;
+
+ if (len >= AES_GCM_ENC_BYTES && AES_GCM_ASM_PPC(ctx)) {
+ size_t res = (16 - ctx->gcm.mres) % 16;
+
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, res))
+ return 0;
+
+ bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res,
+ ctx->gcm.key,
+ ctx->gcm.Yi.c, ctx->gcm.Xi.u, 1);
+
+ ctx->gcm.len.u[1] += bulk;
+ bulk += res;
+ }
+ if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in + bulk, out + bulk,
+ len - bulk, ctx->ctr))
+ return 0;
+ } else {
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ } else {
+ if (ctx->ctr != NULL) {
+ size_t bulk = 0;
+
+ if (len >= AES_GCM_DEC_BYTES && AES_GCM_ASM_PPC(ctx)) {
+ size_t res = (16 - ctx->gcm.mres) % 16;
+
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, res))
+ return -1;
+
+ bulk = ppc_aes_gcm_crypt(in + res, out + res, len - res,
+ ctx->gcm.key,
+ ctx->gcm.Yi.c, ctx->gcm.Xi.u, 0);
+
+ ctx->gcm.len.u[1] += bulk;
+ bulk += res;
+ }
+ if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in + bulk, out + bulk,
+ len - bulk, ctx->ctr))
+ return 0;
+ } else {
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const PROV_GCM_HW aes_ppc_gcm = {
+ aes_ppc_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ ppc_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ return PPC_AES_GCM_CAPABLE ? &aes_ppc_gcm : &aes_gcm;
+}
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc
new file mode 100644
index 000000000000..bf3f98df1631
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv32i.inc
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 32 ZKND ZKNE support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+static int rv32i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zkne_set_encrypt_key, rv32i_zkne_encrypt,
+ NULL);
+ return 1;
+}
+
+static int rv32i_zbkb_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ GCM_HW_SET_KEY_CTR_FN(ks, rv32i_zbkb_zkne_set_encrypt_key, rv32i_zkne_encrypt,
+ NULL);
+ return 1;
+}
+
+static const PROV_GCM_HW rv32i_zknd_zkne_gcm = {
+ rv32i_zknd_zkne_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+static const PROV_GCM_HW rv32i_zbkb_zknd_zkne_gcm = {
+ rv32i_zbkb_zknd_zkne_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE())
+ return &rv32i_zbkb_zknd_zkne_gcm;
+ if (RISCV_HAS_ZKND_AND_ZKNE())
+ return &rv32i_zknd_zkne_gcm;
+ return &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc
new file mode 100644
index 000000000000..105ca58fd324
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_rv64i.inc
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 64 support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+/*-
+ * RISC-V 64 ZKND and ZKNE support for AES GCM.
+ */
+static int rv64i_zknd_zkne_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+ GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zkne_set_encrypt_key, rv64i_zkne_encrypt,
+ NULL);
+ return 1;
+}
+
+static const PROV_GCM_HW rv64i_zknd_zkne_gcm = {
+ rv64i_zknd_zkne_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+/*-
+ * RISC-V RV64 ZVKNED support for AES GCM.
+ */
+static int rv64i_zvkned_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ /*
+ * Zvkned only supports 128 and 256 bit keys for key schedule generation.
+ * For AES-192 case, we could fallback to `AES_set_encrypt_key`.
+ */
+ if (keylen * 8 == 128 || keylen * 8 == 256) {
+ GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key,
+ rv64i_zvkned_encrypt, NULL);
+ } else {
+ GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key,
+ rv64i_zvkned_encrypt, NULL);
+ }
+
+ return 1;
+}
+
+static const PROV_GCM_HW rv64i_zvkned_gcm = {
+ rv64i_zvkned_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+/*-
+ * RISC-V RV64 ZVKB, ZVKG and ZVKNED support for AES GCM.
+ */
+static int rv64i_zvkb_zvkg_zvkned_gcm_initkey(PROV_GCM_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen) {
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ /*
+ * Zvkned only supports 128 and 256 bit keys for key schedule generation.
+ * For AES-192 case, we could fallback to `AES_set_encrypt_key`.
+ */
+ if (keylen * 8 == 128 || keylen * 8 == 256) {
+ GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvkned_set_encrypt_key,
+ rv64i_zvkned_encrypt,
+ rv64i_zvkb_zvkned_ctr32_encrypt_blocks);
+ } else {
+ GCM_HW_SET_KEY_CTR_FN(ks, AES_set_encrypt_key,
+ rv64i_zvkned_encrypt,
+ rv64i_zvkb_zvkned_ctr32_encrypt_blocks);
+ }
+
+ return 1;
+}
+
+static const PROV_GCM_HW rv64i_zvkb_zvkg_zvkned_gcm = {
+ rv64i_zvkb_zvkg_zvkned_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits) {
+ if (RISCV_HAS_ZVKNED()) {
+ if (RISCV_HAS_ZVKB() && RISCV_HAS_ZVKG() && riscv_vlen() >= 128) {
+ return &rv64i_zvkb_zvkg_zvkned_gcm;
+ }
+ return &rv64i_zvkned_gcm;
+ }
+
+ if (RISCV_HAS_ZKND_AND_ZKNE()) {
+ return &rv64i_zknd_zkne_gcm;
+ }
+
+ return &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc
new file mode 100644
index 000000000000..a36c48e3ec49
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_s390x.inc
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * IBM S390X support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+/* iv + padding length for iv lengths != 12 */
+#define S390X_gcm_ivpadlen(i) ((((i) + 15) >> 4 << 4) + 16)
+
+/* Additional flag or'ed to fc for decryption */
+#define S390X_gcm_decrypt_flag(ctx) (((ctx)->enc) ? 0 : S390X_DECRYPT)
+
+#define S390X_gcm_fc(A,C) ((A)->plat.s390x.fc | (A)->plat.s390x.hsflag |\
+ S390X_gcm_decrypt_flag((C)))
+
+static int s390x_aes_gcm_initkey(PROV_GCM_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+
+ ctx->key_set = 1;
+ memcpy(&actx->plat.s390x.param.kma.k, key, keylen);
+ actx->plat.s390x.fc = S390X_AES_FC(keylen);
+ return 1;
+}
+
+static int s390x_aes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv,
+ size_t ivlen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+
+ kma->t.g[0] = 0;
+ kma->t.g[1] = 0;
+ kma->tpcl = 0;
+ kma->taadl = 0;
+ actx->plat.s390x.mreslen = 0;
+ actx->plat.s390x.areslen = 0;
+ actx->plat.s390x.kreslen = 0;
+
+ if (ivlen == GCM_IV_DEFAULT_SIZE) {
+ memcpy(&kma->j0, iv, ivlen);
+ kma->j0.w[3] = 1;
+ kma->cv.w = 1;
+ actx->plat.s390x.hsflag = 0;
+ } else {
+ unsigned long long ivbits = ivlen << 3;
+ size_t len = S390X_gcm_ivpadlen(ivlen);
+ unsigned char iv_zero_pad[S390X_gcm_ivpadlen(GCM_IV_MAX_SIZE)];
+ /*
+ * The IV length needs to be zero padded to be a multiple of 16 bytes
+ * followed by 8 bytes of zeros and 8 bytes for the IV length.
+ * The GHASH of this value can then be calculated.
+ */
+ memcpy(iv_zero_pad, iv, ivlen);
+ memset(iv_zero_pad + ivlen, 0, len - ivlen);
+ memcpy(iv_zero_pad + len - sizeof(ivbits), &ivbits, sizeof(ivbits));
+ /*
+ * Calculate the ghash of the iv - the result is stored into the tag
+ * param.
+ */
+ s390x_kma(iv_zero_pad, len, NULL, 0, NULL, actx->plat.s390x.fc, kma);
+ actx->plat.s390x.hsflag = S390X_KMA_HS; /* The hash subkey is set */
+
+ /* Copy the 128 bit GHASH result into J0 and clear the tag */
+ kma->j0.g[0] = kma->t.g[0];
+ kma->j0.g[1] = kma->t.g[1];
+ kma->t.g[0] = 0;
+ kma->t.g[1] = 0;
+ /* Set the 32 bit counter */
+ kma->cv.w = kma->j0.w[3];
+ }
+ return 1;
+}
+
+static int s390x_aes_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+ unsigned char out[AES_BLOCK_SIZE];
+ unsigned int fc;
+ int rc;
+
+ kma->taadl <<= 3;
+ kma->tpcl <<= 3;
+ fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
+ s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
+ actx->plat.s390x.mres, actx->plat.s390x.mreslen, out,
+ fc, kma);
+
+ /* gctx->mres already returned to the caller */
+ OPENSSL_cleanse(out, actx->plat.s390x.mreslen);
+
+ if (ctx->enc) {
+ ctx->taglen = GCM_TAG_MAX_SIZE;
+ memcpy(tag, kma->t.b, ctx->taglen);
+ rc = 1;
+ } else {
+ rc = (CRYPTO_memcmp(tag, kma->t.b, ctx->taglen) == 0);
+ }
+ return rc;
+}
+
+static int s390x_aes_gcm_one_shot(PROV_GCM_CTX *ctx,
+ unsigned char *aad, size_t aad_len,
+ const unsigned char *in, size_t in_len,
+ unsigned char *out,
+ unsigned char *tag, size_t taglen)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+ unsigned int fc;
+ int rc;
+
+ kma->taadl = aad_len << 3;
+ kma->tpcl = in_len << 3;
+ fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD | S390X_KMA_LPC;
+ s390x_kma(aad, aad_len, in, in_len, out, fc, kma);
+
+ if (ctx->enc) {
+ memcpy(tag, kma->t.b, taglen);
+ rc = 1;
+ } else {
+ rc = (CRYPTO_memcmp(tag, kma->t.b, taglen) == 0);
+ }
+ return rc;
+}
+
+/*
+ * Process additional authenticated data. Returns 1 on success. Code is
+ * big-endian.
+ */
+static int s390x_aes_gcm_aad_update(PROV_GCM_CTX *ctx,
+ const unsigned char *aad, size_t len)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+ unsigned long long alen;
+ unsigned int fc;
+ int n, rem;
+
+ /* If already processed pt/ct then error */
+ if (kma->tpcl != 0)
+ return 0;
+
+ /* update the total aad length */
+ alen = kma->taadl + len;
+ if (alen > (U64(1) << 61) || (sizeof(len) == 8 && alen < len))
+ return 0;
+ kma->taadl = alen;
+
+ /* check if there is any existing aad data from a previous add */
+ n = actx->plat.s390x.areslen;
+ if (n) {
+ /* add additional data to a buffer until it has 16 bytes */
+ while (n && len) {
+ actx->plat.s390x.ares[n] = *aad;
+ ++aad;
+ --len;
+ n = (n + 1) & 0xf;
+ }
+ /* ctx->ares contains a complete block if offset has wrapped around */
+ if (!n) {
+ fc = S390X_gcm_fc(actx, ctx);
+ s390x_kma(actx->plat.s390x.ares, 16, NULL, 0, NULL, fc, kma);
+ actx->plat.s390x.hsflag = S390X_KMA_HS;
+ }
+ actx->plat.s390x.areslen = n;
+ }
+
+ /* If there are leftover bytes (< 128 bits) save them for next time */
+ rem = len & 0xf;
+ /* Add any remaining 16 byte blocks (128 bit each) */
+ len &= ~(size_t)0xf;
+ if (len) {
+ fc = S390X_gcm_fc(actx, ctx);
+ s390x_kma(aad, len, NULL, 0, NULL, fc, kma);
+ actx->plat.s390x.hsflag = S390X_KMA_HS;
+ aad += len;
+ }
+
+ if (rem) {
+ actx->plat.s390x.areslen = rem;
+
+ do {
+ --rem;
+ actx->plat.s390x.ares[rem] = aad[rem];
+ } while (rem);
+ }
+ return 1;
+}
+
+/*-
+ * En/de-crypt plain/cipher-text and authenticate ciphertext. Returns 1 for
+ * success. Code is big-endian.
+ */
+static int s390x_aes_gcm_cipher_update(PROV_GCM_CTX *ctx,
+ const unsigned char *in, size_t len,
+ unsigned char *out)
+{
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ S390X_KMA_PARAMS *kma = &actx->plat.s390x.param.kma;
+ const unsigned char *inptr;
+ unsigned long long mlen;
+ unsigned int fc;
+ union {
+ unsigned int w[4];
+ unsigned char b[16];
+ } buf;
+ size_t inlen;
+ int n, rem, i;
+
+ mlen = kma->tpcl + len;
+ if (mlen > ((U64(1) << 36) - 32) || (sizeof(len) == 8 && mlen < len))
+ return 0;
+ kma->tpcl = mlen;
+
+ fc = S390X_gcm_fc(actx, ctx) | S390X_KMA_LAAD;
+ n = actx->plat.s390x.mreslen;
+ if (n) {
+ inptr = in;
+ inlen = len;
+ while (n && inlen) {
+ actx->plat.s390x.mres[n] = *inptr;
+ n = (n + 1) & 0xf;
+ ++inptr;
+ --inlen;
+ }
+ /* ctx->mres contains a complete block if offset has wrapped around */
+ if (!n) {
+ s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen,
+ actx->plat.s390x.mres, 16, buf.b, fc, kma);
+ actx->plat.s390x.hsflag = S390X_KMA_HS;
+ fc |= S390X_KMA_HS;
+ actx->plat.s390x.areslen = 0;
+
+ /* previous call already encrypted/decrypted its remainder,
+ * see comment below */
+ n = actx->plat.s390x.mreslen;
+ while (n) {
+ *out = buf.b[n];
+ n = (n + 1) & 0xf;
+ ++out;
+ ++in;
+ --len;
+ }
+ actx->plat.s390x.mreslen = 0;
+ }
+ }
+
+ rem = len & 0xf;
+
+ len &= ~(size_t)0xf;
+ if (len) {
+ s390x_kma(actx->plat.s390x.ares, actx->plat.s390x.areslen, in, len, out,
+ fc, kma);
+ in += len;
+ out += len;
+ actx->plat.s390x.hsflag = S390X_KMA_HS;
+ actx->plat.s390x.areslen = 0;
+ }
+
+ /*-
+ * If there is a remainder, it has to be saved such that it can be
+ * processed by kma later. However, we also have to do the for-now
+ * unauthenticated encryption/decryption part here and now...
+ */
+ if (rem) {
+ if (!actx->plat.s390x.mreslen) {
+ buf.w[0] = kma->j0.w[0];
+ buf.w[1] = kma->j0.w[1];
+ buf.w[2] = kma->j0.w[2];
+ buf.w[3] = kma->cv.w + 1;
+ s390x_km(buf.b, 16, actx->plat.s390x.kres,
+ fc & 0x1f, &kma->k);
+ }
+
+ n = actx->plat.s390x.mreslen;
+ for (i = 0; i < rem; i++) {
+ actx->plat.s390x.mres[n + i] = in[i];
+ out[i] = in[i] ^ actx->plat.s390x.kres[n + i];
+ }
+ actx->plat.s390x.mreslen += rem;
+ }
+ return 1;
+}
+
+static const PROV_GCM_HW s390x_aes_gcm = {
+ s390x_aes_gcm_initkey,
+ s390x_aes_gcm_setiv,
+ s390x_aes_gcm_aad_update,
+ s390x_aes_gcm_cipher_update,
+ s390x_aes_gcm_cipher_final,
+ s390x_aes_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ if ((keybits == 128 && S390X_aes_128_gcm_CAPABLE)
+ || (keybits == 192 && S390X_aes_192_gcm_CAPABLE)
+ || (keybits == 256 && S390X_aes_256_gcm_CAPABLE))
+ return &s390x_aes_gcm;
+ return &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc
new file mode 100644
index 000000000000..2b3a6d1d5ea2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_t4.inc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Fujitsu SPARC64 X support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw.c
+ */
+
+static int t4_aes_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ ctr128_f ctr;
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+
+ switch (keylen) {
+ case 16:
+ ctr = (ctr128_f)aes128_t4_ctr32_encrypt;
+ break;
+ case 24:
+ ctr = (ctr128_f)aes192_t4_ctr32_encrypt;
+ break;
+ case 32:
+ ctr = (ctr128_f)aes256_t4_ctr32_encrypt;
+ break;
+ default:
+ return 0;
+ }
+
+ GCM_HW_SET_KEY_CTR_FN(ks, aes_t4_set_encrypt_key, aes_t4_encrypt, ctr);
+ return 1;
+}
+
+static const PROV_GCM_HW t4_aes_gcm = {
+ t4_aes_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ generic_aes_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+const PROV_GCM_HW *ossl_prov_aes_hw_gcm(size_t keybits)
+{
+ return SPARC_AES_CAPABLE ? &t4_aes_gcm : &aes_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc
new file mode 100644
index 000000000000..c892c0754e8d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_hw_vaes_avx512.inc
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2021-2022 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright (c) 2021, Intel Corporation. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * AVX512 VAES + VPCLMULDQD support for AES GCM.
+ * This file is included by cipher_aes_gcm_hw_aesni.inc
+ */
+
+#undef VAES_GCM_ENABLED
+#if (defined(__x86_64) || defined(__x86_64__) || \
+ defined(_M_AMD64) || defined(_M_X64))
+# define VAES_GCM_ENABLED
+
+/* Returns non-zero when AVX512F + VAES + VPCLMULDQD combination is available */
+int ossl_vaes_vpclmulqdq_capable(void);
+
+# define OSSL_AES_GCM_UPDATE(direction) \
+ void ossl_aes_gcm_ ## direction ## _avx512(const void *ks, \
+ void *gcm128ctx, \
+ unsigned int *pblocklen, \
+ const unsigned char *in, \
+ size_t len, \
+ unsigned char *out);
+
+OSSL_AES_GCM_UPDATE(encrypt)
+OSSL_AES_GCM_UPDATE(decrypt)
+
+void ossl_aes_gcm_init_avx512(const void *ks, void *gcm128ctx);
+void ossl_aes_gcm_setiv_avx512(const void *ks, void *gcm128ctx,
+ const unsigned char *iv, size_t ivlen);
+void ossl_aes_gcm_update_aad_avx512(void *gcm128ctx, const unsigned char *aad,
+ size_t aadlen);
+void ossl_aes_gcm_finalize_avx512(void *gcm128ctx, unsigned int pblocklen);
+
+void ossl_gcm_gmult_avx512(u64 Xi[2], const void *gcm128ctx);
+
+static int vaes_gcm_setkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ GCM128_CONTEXT *gcmctx = &ctx->gcm;
+ PROV_AES_GCM_CTX *actx = (PROV_AES_GCM_CTX *)ctx;
+ AES_KEY *ks = &actx->ks.ks;
+
+ aesni_set_encrypt_key(key, keylen * 8, ks);
+ memset(gcmctx, 0, sizeof(*gcmctx));
+ gcmctx->key = ks;
+ ctx->key_set = 1;
+
+ ossl_aes_gcm_init_avx512(ks, gcmctx);
+
+ return 1;
+}
+
+static int vaes_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv,
+ size_t ivlen)
+{
+ GCM128_CONTEXT *gcmctx = &ctx->gcm;
+
+ gcmctx->Yi.u[0] = 0; /* Current counter */
+ gcmctx->Yi.u[1] = 0;
+ gcmctx->Xi.u[0] = 0; /* AAD hash */
+ gcmctx->Xi.u[1] = 0;
+ gcmctx->len.u[0] = 0; /* AAD length */
+ gcmctx->len.u[1] = 0; /* Message length */
+ gcmctx->ares = 0;
+ gcmctx->mres = 0;
+
+ /* IV is limited by 2^64 bits, thus 2^61 bytes */
+ if (ivlen > (U64(1) << 61))
+ return 0;
+
+ ossl_aes_gcm_setiv_avx512(gcmctx->key, gcmctx, iv, ivlen);
+
+ return 1;
+}
+
+static int vaes_gcm_aadupdate(PROV_GCM_CTX *ctx,
+ const unsigned char *aad,
+ size_t aad_len)
+{
+ GCM128_CONTEXT *gcmctx = &ctx->gcm;
+ u64 alen = gcmctx->len.u[0];
+ unsigned int ares;
+ size_t i, lenBlks;
+
+ /* Bad sequence: call of AAD update after message processing */
+ if (gcmctx->len.u[1] > 0)
+ return 0;
+
+ alen += aad_len;
+ /* AAD is limited by 2^64 bits, thus 2^61 bytes */
+ if ((alen > (U64(1) << 61)) || (alen < aad_len))
+ return 0;
+
+ gcmctx->len.u[0] = alen;
+
+ ares = gcmctx->ares;
+ /* Partial AAD block left from previous AAD update calls */
+ if (ares > 0) {
+ /*
+ * Fill partial block buffer till full block
+ * (note, the hash is stored reflected)
+ */
+ while (ares > 0 && aad_len > 0) {
+ gcmctx->Xi.c[15 - ares] ^= *(aad++);
+ --aad_len;
+ ares = (ares + 1) % AES_BLOCK_SIZE;
+ }
+ /* Full block gathered */
+ if (ares == 0) {
+ ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx);
+ } else { /* no more AAD */
+ gcmctx->ares = ares;
+ return 1;
+ }
+ }
+
+ /* Bulk AAD processing */
+ lenBlks = aad_len & ((size_t)(-AES_BLOCK_SIZE));
+ if (lenBlks > 0) {
+ ossl_aes_gcm_update_aad_avx512(gcmctx, aad, lenBlks);
+ aad += lenBlks;
+ aad_len -= lenBlks;
+ }
+
+ /* Add remaining AAD to the hash (note, the hash is stored reflected) */
+ if (aad_len > 0) {
+ ares = aad_len;
+ for (i = 0; i < aad_len; i++)
+ gcmctx->Xi.c[15 - i] ^= aad[i];
+ }
+
+ gcmctx->ares = ares;
+
+ return 1;
+}
+
+static int vaes_gcm_cipherupdate(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out)
+{
+ GCM128_CONTEXT *gcmctx = &ctx->gcm;
+ u64 mlen = gcmctx->len.u[1];
+
+ mlen += len;
+ if (mlen > ((U64(1) << 36) - 32) || (mlen < len))
+ return 0;
+
+ gcmctx->len.u[1] = mlen;
+
+ /* Finalize GHASH(AAD) if AAD partial blocks left unprocessed */
+ if (gcmctx->ares > 0) {
+ ossl_gcm_gmult_avx512(gcmctx->Xi.u, gcmctx);
+ gcmctx->ares = 0;
+ }
+
+ if (ctx->enc)
+ ossl_aes_gcm_encrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out);
+ else
+ ossl_aes_gcm_decrypt_avx512(gcmctx->key, gcmctx, &gcmctx->mres, in, len, out);
+
+ return 1;
+}
+
+static int vaes_gcm_cipherfinal(PROV_GCM_CTX *ctx, unsigned char *tag)
+{
+ GCM128_CONTEXT *gcmctx = &ctx->gcm;
+ unsigned int *res = &gcmctx->mres;
+
+ /* Finalize AAD processing */
+ if (gcmctx->ares > 0)
+ res = &gcmctx->ares;
+
+ ossl_aes_gcm_finalize_avx512(gcmctx, *res);
+
+ if (ctx->enc) {
+ ctx->taglen = GCM_TAG_MAX_SIZE;
+ memcpy(tag, gcmctx->Xi.c,
+ ctx->taglen <= sizeof(gcmctx->Xi.c) ? ctx->taglen :
+ sizeof(gcmctx->Xi.c));
+ *res = 0;
+ } else {
+ return !CRYPTO_memcmp(gcmctx->Xi.c, tag, ctx->taglen);
+ }
+
+ return 1;
+}
+
+static const PROV_GCM_HW vaes_gcm = {
+ vaes_gcm_setkey,
+ vaes_gcm_setiv,
+ vaes_gcm_aadupdate,
+ vaes_gcm_cipherupdate,
+ vaes_gcm_cipherfinal,
+ ossl_gcm_one_shot
+};
+
+#endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c
new file mode 100644
index 000000000000..c9afeddef67a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for AES SIV mode */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/ciphercommon_aead.h"
+#include "prov/provider_ctx.h"
+#include "cipher_aes_gcm_siv.h"
+
+static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[]);
+
+static void *ossl_aes_gcm_siv_newctx(void *provctx, size_t keybits)
+{
+ PROV_AES_GCM_SIV_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ctx->key_len = keybits / 8;
+ ctx->hw = ossl_prov_cipher_hw_aes_gcm_siv(keybits);
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->provctx = provctx;
+ }
+ return ctx;
+}
+
+static void ossl_aes_gcm_siv_freectx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ if (ctx == NULL)
+ return;
+
+ OPENSSL_clear_free(ctx->aad, ctx->aad_len);
+ ctx->hw->clean_ctx(ctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *ossl_aes_gcm_siv_dupctx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *in = (PROV_AES_GCM_SIV_CTX *)vctx;
+ PROV_AES_GCM_SIV_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (in->hw == NULL)
+ return NULL;
+
+ ret = OPENSSL_memdup(in, sizeof(*in));
+ if (ret == NULL)
+ return NULL;
+ /* NULL-out these things we create later */
+ ret->aad = NULL;
+ ret->ecb_ctx = NULL;
+
+ if (in->aad != NULL) {
+ if ((ret->aad = OPENSSL_memdup(in->aad, UP16(ret->aad_len))) == NULL)
+ goto err;
+ }
+
+ if (!in->hw->dup_ctx(ret, in))
+ goto err;
+
+ return ret;
+ err:
+ if (ret != NULL) {
+ OPENSSL_clear_free(ret->aad, ret->aad_len);
+ OPENSSL_free(ret);
+ }
+ return NULL;
+}
+
+static int ossl_aes_gcm_siv_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (key != NULL) {
+ if (keylen != ctx->key_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->key_gen_key, key, ctx->key_len);
+ }
+ if (iv != NULL) {
+ if (ivlen != sizeof(ctx->nonce)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->nonce, iv, sizeof(ctx->nonce));
+ }
+
+ if (!ctx->hw->initkey(ctx))
+ return 0;
+
+ return ossl_aes_gcm_siv_set_ctx_params(ctx, params);
+}
+
+static int ossl_aes_gcm_siv_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int ossl_aes_gcm_siv_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ossl_aes_gcm_siv_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+#define ossl_aes_gcm_siv_stream_update ossl_aes_gcm_siv_cipher
+static int ossl_aes_gcm_siv_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ int error = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ error |= !ctx->hw->cipher(ctx, out, in, inl);
+
+ if (outl != NULL && !error)
+ *outl = inl;
+ return !error;
+}
+
+static int ossl_aes_gcm_siv_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ int error = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ error |= !ctx->hw->cipher(vctx, out, NULL, 0);
+
+ if (outl != NULL && !error)
+ *outl = 0;
+ return !error;
+}
+
+static int ossl_aes_gcm_siv_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) {
+ if (!ctx->enc || !ctx->generated_tag
+ || p->data_size != sizeof(ctx->tag)
+ || !OSSL_PARAM_set_octet_string(p, ctx->tag, sizeof(ctx->tag))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, sizeof(ctx->tag))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->key_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_gcm_siv_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ossl_aes_gcm_siv_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_gcm_siv_known_gettable_ctx_params;
+}
+
+static int ossl_aes_gcm_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ const OSSL_PARAM *p;
+ unsigned int speed = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || p->data_size != sizeof(ctx->user_tag)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (!ctx->enc) {
+ memcpy(ctx->user_tag, p->data, sizeof(ctx->tag));
+ ctx->have_user_tag = 1;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_SPEED);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &speed)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->speed = !!speed;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t key_len;
+
+ if (!OSSL_PARAM_get_size_t(p, &key_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* The key length can not be modified */
+ if (key_len != ctx->key_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_gcm_siv_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_SPEED, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *ossl_aes_gcm_siv_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_gcm_siv_known_settable_ctx_params;
+}
+
+#define IMPLEMENT_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_newctx_fn ossl_##alg##kbits##_##lc##_newctx; \
+static OSSL_FUNC_cipher_freectx_fn ossl_##alg##_##lc##_freectx; \
+static OSSL_FUNC_cipher_dupctx_fn ossl_##alg##_##lc##_dupctx; \
+static OSSL_FUNC_cipher_encrypt_init_fn ossl_##alg##_##lc##_einit; \
+static OSSL_FUNC_cipher_decrypt_init_fn ossl_##alg##_##lc##_dinit; \
+static OSSL_FUNC_cipher_update_fn ossl_##alg##_##lc##_stream_update; \
+static OSSL_FUNC_cipher_final_fn ossl_##alg##_##lc##_stream_final; \
+static OSSL_FUNC_cipher_cipher_fn ossl_##alg##_##lc##_cipher; \
+static OSSL_FUNC_cipher_get_params_fn ossl_##alg##_##kbits##_##lc##_get_params; \
+static OSSL_FUNC_cipher_get_ctx_params_fn ossl_##alg##_##lc##_get_ctx_params; \
+static OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_##alg##_##lc##_gettable_ctx_params; \
+static OSSL_FUNC_cipher_set_ctx_params_fn ossl_##alg##_##lc##_set_ctx_params; \
+static OSSL_FUNC_cipher_settable_ctx_params_fn ossl_##alg##_##lc##_settable_ctx_params; \
+static int ossl_##alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static void *ossl_##alg##kbits##_##lc##_newctx(void *provctx) \
+{ \
+ return ossl_##alg##_##lc##_newctx(provctx, kbits); \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))ossl_##alg##kbits##_##lc##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_##alg##_##lc##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))ossl_##alg##_##lc##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_##alg##_##lc##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_##alg##_##lc##_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_##alg##_##lc##_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_##alg##_##lc##_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))ossl_##alg##_##kbits##_##lc##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, (void (*)(void))ossl_##alg##_##lc##_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 128, 8, 96);
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 192, 8, 96);
+IMPLEMENT_cipher(aes, gcm_siv, GCM_SIV, AEAD_FLAGS, 256, 8, 96);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h
new file mode 100644
index 000000000000..37d1e3326b18
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+#define BLOCK_SIZE 16
+#define NONCE_SIZE 12
+#define TAG_SIZE 16
+
+/* AAD manipulation macros */
+#define UP16(x) (((x) + 15) & ~0x0F)
+#define DOWN16(x) ((x) & ~0x0F)
+#define REMAINDER16(x) ((x) & 0x0F)
+#define IS16(x) (((x) & 0x0F) == 0)
+
+typedef struct prov_cipher_hw_aes_gcm_siv_st {
+ int (*initkey)(void *vctx);
+ int (*cipher)(void *vctx, unsigned char *out, const unsigned char *in,
+ size_t len);
+ int (*dup_ctx)(void *vdst, void *vsrc);
+ void (*clean_ctx)(void *vctx);
+} PROV_CIPHER_HW_AES_GCM_SIV;
+
+/* Arranged for alignment purposes */
+typedef struct prov_aes_gcm_siv_ctx_st {
+ EVP_CIPHER_CTX *ecb_ctx;
+ const PROV_CIPHER_HW_AES_GCM_SIV *hw; /* maybe not used, yet? */
+ uint8_t *aad; /* Allocated, rounded up to 16 bytes, from user */
+ OSSL_LIB_CTX *libctx;
+ OSSL_PROVIDER *provctx;
+ size_t aad_len; /* actual AAD length */
+ size_t key_len;
+ uint8_t key_gen_key[32]; /* from user */
+ uint8_t msg_enc_key[32]; /* depends on key size */
+ uint8_t msg_auth_key[BLOCK_SIZE];
+ uint8_t tag[TAG_SIZE]; /* generated tag, given to user or compared to user */
+ uint8_t user_tag[TAG_SIZE]; /* from user */
+ uint8_t nonce[NONCE_SIZE]; /* from user */
+ u128 Htable[16]; /* Polyval calculations via ghash */
+ unsigned int enc : 1; /* Set to 1 if we are encrypting or 0 otherwise */
+ unsigned int have_user_tag : 1;
+ unsigned int generated_tag : 1;
+ unsigned int used_enc : 1;
+ unsigned int used_dec : 1;
+ unsigned int speed : 1;
+} PROV_AES_GCM_SIV_CTX;
+
+const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits);
+
+void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2]);
+void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len);
+
+/* Define GSWAP8/GSWAP4 - used for BOTH little and big endian architectures */
+static ossl_inline uint32_t GSWAP4(uint32_t n)
+{
+ return (((n & 0x000000FF) << 24)
+ | ((n & 0x0000FF00) << 8)
+ | ((n & 0x00FF0000) >> 8)
+ | ((n & 0xFF000000) >> 24));
+}
+static ossl_inline uint64_t GSWAP8(uint64_t n)
+{
+ uint64_t result;
+
+ result = GSWAP4(n & 0x0FFFFFFFF);
+ result <<= 32;
+ return result | GSWAP4(n >> 32);
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
new file mode 100644
index 000000000000..c60d4e048769
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_hw.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/evp.h>
+#include <internal/endian.h>
+#include <prov/implementations.h>
+#include "cipher_aes_gcm_siv.h"
+
+static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter,
+ unsigned char *out, const unsigned char *in, size_t len);
+
+static int aes_gcm_siv_initkey(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+ uint8_t output[BLOCK_SIZE];
+ uint32_t counter = 0x0;
+ size_t i;
+ union {
+ uint32_t counter;
+ uint8_t block[BLOCK_SIZE];
+ } data;
+ int out_len;
+ EVP_CIPHER *ecb = NULL;
+ DECLARE_IS_ENDIAN;
+
+ switch (ctx->key_len) {
+ case 16:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-128-ECB", NULL);
+ break;
+ case 24:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-192-ECB", NULL);
+ break;
+ case 32:
+ ecb = EVP_CIPHER_fetch(ctx->libctx, "AES-256-ECB", NULL);
+ break;
+ default:
+ goto err;
+ }
+
+ if (ctx->ecb_ctx == NULL && (ctx->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->key_gen_key, NULL, NULL))
+ goto err;
+
+ memset(&data, 0, sizeof(data));
+ memcpy(&data.block[sizeof(data.counter)], ctx->nonce, NONCE_SIZE);
+
+ /* msg_auth_key is always 16 bytes in size, regardless of AES128/AES256 */
+ /* counter is stored little-endian */
+ for (i = 0; i < BLOCK_SIZE; i += 8) {
+ if (IS_LITTLE_ENDIAN) {
+ data.counter = counter;
+ } else {
+ data.counter = GSWAP4(counter);
+ }
+ /* Block size is 16 (128 bits), but only 8 bytes are used */
+ out_len = BLOCK_SIZE;
+ if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE))
+ goto err;
+ memcpy(&ctx->msg_auth_key[i], output, 8);
+ counter++;
+ }
+
+ /* msg_enc_key length is directly tied to key length AES128/AES256 */
+ for (i = 0; i < ctx->key_len; i += 8) {
+ if (IS_LITTLE_ENDIAN) {
+ data.counter = counter;
+ } else {
+ data.counter = GSWAP4(counter);
+ }
+ /* Block size is 16 bytes (128 bits), but only 8 bytes are used */
+ out_len = BLOCK_SIZE;
+ if (!EVP_EncryptUpdate(ctx->ecb_ctx, output, &out_len, data.block, BLOCK_SIZE))
+ goto err;
+ memcpy(&ctx->msg_enc_key[i], output, 8);
+ counter++;
+ }
+
+ if (!EVP_EncryptInit_ex2(ctx->ecb_ctx, ecb, ctx->msg_enc_key, NULL, NULL))
+ goto err;
+
+ /* Freshen up the state */
+ ctx->used_enc = 0;
+ ctx->used_dec = 0;
+ EVP_CIPHER_free(ecb);
+ return 1;
+ err:
+ EVP_CIPHER_CTX_free(ctx->ecb_ctx);
+ EVP_CIPHER_free(ecb);
+ ctx->ecb_ctx = NULL;
+ return 0;
+}
+
+static int aes_gcm_siv_aad(PROV_AES_GCM_SIV_CTX *ctx,
+ const unsigned char *aad, size_t len)
+{
+ size_t to_alloc;
+ uint8_t *ptr;
+ uint64_t len64;
+
+ /* length of 0 resets the AAD */
+ if (len == 0) {
+ OPENSSL_free(ctx->aad);
+ ctx->aad = NULL;
+ ctx->aad_len = 0;
+ return 1;
+ }
+ to_alloc = UP16(ctx->aad_len + len);
+ /* need to check the size of the AAD per RFC8452 */
+ len64 = to_alloc;
+ if (len64 > ((uint64_t)1 << 36))
+ return 0;
+ ptr = OPENSSL_realloc(ctx->aad, to_alloc);
+ if (ptr == NULL)
+ return 0;
+ ctx->aad = ptr;
+ memcpy(&ctx->aad[ctx->aad_len], aad, len);
+ ctx->aad_len += len;
+ if (to_alloc > ctx->aad_len)
+ memset(&ctx->aad[ctx->aad_len], 0, to_alloc - ctx->aad_len);
+ return 1;
+}
+
+static int aes_gcm_siv_finish(PROV_AES_GCM_SIV_CTX *ctx)
+{
+ int ret = 0;
+
+ if (ctx->enc)
+ return ctx->generated_tag;
+ ret = !CRYPTO_memcmp(ctx->tag, ctx->user_tag, sizeof(ctx->tag));
+ ret &= ctx->have_user_tag;
+ return ret;
+}
+
+static int aes_gcm_siv_encrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ uint64_t len_blk[2];
+ uint8_t S_s[TAG_SIZE];
+ uint8_t counter_block[TAG_SIZE];
+ uint8_t padding[BLOCK_SIZE];
+ size_t i;
+ int64_t len64 = len;
+ int out_len;
+ int error = 0;
+ DECLARE_IS_ENDIAN;
+
+ ctx->generated_tag = 0;
+ if (!ctx->speed && ctx->used_enc)
+ return 0;
+ /* need to check the size of the input! */
+ if (len64 > ((int64_t)1 << 36))
+ return 0;
+
+ if (IS_LITTLE_ENDIAN) {
+ len_blk[0] = (uint64_t)ctx->aad_len * 8;
+ len_blk[1] = (uint64_t)len * 8;
+ } else {
+ len_blk[0] = GSWAP8((uint64_t)ctx->aad_len * 8);
+ len_blk[1] = GSWAP8((uint64_t)len * 8);
+ }
+ memset(S_s, 0, TAG_SIZE);
+ ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key);
+
+ if (ctx->aad != NULL) {
+ /* AAD is allocated with padding, but need to adjust length */
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len));
+ }
+ if (DOWN16(len) > 0)
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) in, DOWN16(len));
+ if (!IS16(len)) {
+ /* deal with padding - probably easier to memset the padding first rather than calculate */
+ memset(padding, 0, sizeof(padding));
+ memcpy(padding, &in[DOWN16(len)], REMAINDER16(len));
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, padding, sizeof(padding));
+ }
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *) len_blk, sizeof(len_blk));
+
+ for (i = 0; i < NONCE_SIZE; i++)
+ S_s[i] ^= ctx->nonce[i];
+
+ S_s[TAG_SIZE - 1] &= 0x7f;
+ out_len = sizeof(ctx->tag);
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s));
+ memcpy(counter_block, ctx->tag, TAG_SIZE);
+ counter_block[TAG_SIZE - 1] |= 0x80;
+
+ error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len);
+
+ ctx->generated_tag = !error;
+ /* Regardless of error */
+ ctx->used_enc = 1;
+ return !error;
+}
+
+static int aes_gcm_siv_decrypt(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ uint8_t counter_block[TAG_SIZE];
+ uint64_t len_blk[2];
+ uint8_t S_s[TAG_SIZE];
+ size_t i;
+ uint64_t padding[2];
+ int64_t len64 = len;
+ int out_len;
+ int error = 0;
+ DECLARE_IS_ENDIAN;
+
+ ctx->generated_tag = 0;
+ if (!ctx->speed && ctx->used_dec)
+ return 0;
+ /* need to check the size of the input! */
+ if (len64 > ((int64_t)1 << 36))
+ return 0;
+
+ memcpy(counter_block, ctx->user_tag, sizeof(counter_block));
+ counter_block[TAG_SIZE - 1] |= 0x80;
+
+ error |= !aes_gcm_siv_ctr32(ctx, counter_block, out, in, len);
+
+ if (IS_LITTLE_ENDIAN) {
+ len_blk[0] = (uint64_t)ctx->aad_len * 8;
+ len_blk[1] = (uint64_t)len * 8;
+ } else {
+ len_blk[0] = GSWAP8((uint64_t)ctx->aad_len * 8);
+ len_blk[1] = GSWAP8((uint64_t)len * 8);
+ }
+ memset(S_s, 0, TAG_SIZE);
+ ossl_polyval_ghash_init(ctx->Htable, (const uint64_t*)ctx->msg_auth_key);
+ if (ctx->aad != NULL) {
+ /* AAD allocated with padding, but need to adjust length */
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, ctx->aad, UP16(ctx->aad_len));
+ }
+ if (DOWN16(len) > 0)
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, out, DOWN16(len));
+ if (!IS16(len)) {
+ /* deal with padding - probably easier to "memset" the padding first rather than calculate */
+ padding[0] = padding[1] = 0;
+ memcpy(padding, &out[DOWN16(len)], REMAINDER16(len));
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)padding, sizeof(padding));
+ }
+ ossl_polyval_ghash_hash(ctx->Htable, S_s, (uint8_t *)len_blk, TAG_SIZE);
+
+ for (i = 0; i < NONCE_SIZE; i++)
+ S_s[i] ^= ctx->nonce[i];
+
+ S_s[TAG_SIZE - 1] &= 0x7f;
+
+ /*
+ * In the ctx, user_tag is the one received/set by the user,
+ * and tag is generated from the input
+ */
+ out_len = sizeof(ctx->tag);
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, ctx->tag, &out_len, S_s, sizeof(S_s));
+ ctx->generated_tag = !error;
+ /* Regardless of error */
+ ctx->used_dec = 1;
+ return !error;
+}
+
+static int aes_gcm_siv_cipher(void *vctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ /* EncryptFinal or DecryptFinal */
+ if (in == NULL)
+ return aes_gcm_siv_finish(ctx);
+
+ /* Deal with associated data */
+ if (out == NULL)
+ return aes_gcm_siv_aad(ctx, in, len);
+
+ if (ctx->enc)
+ return aes_gcm_siv_encrypt(ctx, in, out, len);
+
+ return aes_gcm_siv_decrypt(ctx, in, out, len);
+}
+
+static void aes_gcm_siv_clean_ctx(void *vctx)
+{
+ PROV_AES_GCM_SIV_CTX *ctx = (PROV_AES_GCM_SIV_CTX *)vctx;
+
+ EVP_CIPHER_CTX_free(ctx->ecb_ctx);
+ ctx->ecb_ctx = NULL;
+}
+
+static int aes_gcm_siv_dup_ctx(void *vdst, void *vsrc)
+{
+ PROV_AES_GCM_SIV_CTX *dst = (PROV_AES_GCM_SIV_CTX *)vdst;
+ PROV_AES_GCM_SIV_CTX *src = (PROV_AES_GCM_SIV_CTX *)vsrc;
+
+ dst->ecb_ctx = NULL;
+ if (src->ecb_ctx != NULL) {
+ if ((dst->ecb_ctx = EVP_CIPHER_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_CIPHER_CTX_copy(dst->ecb_ctx, src->ecb_ctx))
+ goto err;
+ }
+ return 1;
+
+ err:
+ EVP_CIPHER_CTX_free(dst->ecb_ctx);
+ dst->ecb_ctx = NULL;
+ return 0;
+}
+
+static const PROV_CIPHER_HW_AES_GCM_SIV aes_gcm_siv_hw = {
+ aes_gcm_siv_initkey,
+ aes_gcm_siv_cipher,
+ aes_gcm_siv_dup_ctx,
+ aes_gcm_siv_clean_ctx,
+};
+
+const PROV_CIPHER_HW_AES_GCM_SIV *ossl_prov_cipher_hw_aes_gcm_siv(size_t keybits)
+{
+ return &aes_gcm_siv_hw;
+}
+
+/* AES-GCM-SIV needs AES-CTR32, which is different than the AES-CTR implementation */
+static int aes_gcm_siv_ctr32(PROV_AES_GCM_SIV_CTX *ctx, const unsigned char *init_counter,
+ unsigned char *out, const unsigned char *in, size_t len)
+{
+ uint8_t keystream[BLOCK_SIZE];
+ int out_len;
+ size_t i;
+ size_t j;
+ size_t todo;
+ uint32_t counter;
+ int error = 0;
+ union {
+ uint32_t x32[BLOCK_SIZE / sizeof(uint32_t)];
+ uint8_t x8[BLOCK_SIZE];
+ } block;
+ DECLARE_IS_ENDIAN;
+
+ memcpy(&block, init_counter, sizeof(block));
+ if (IS_BIG_ENDIAN) {
+ counter = GSWAP4(block.x32[0]);
+ }
+
+ for (i = 0; i < len; i += sizeof(block)) {
+ out_len = BLOCK_SIZE;
+ error |= !EVP_EncryptUpdate(ctx->ecb_ctx, keystream, &out_len, (uint8_t*)&block, sizeof(block));
+ if (IS_LITTLE_ENDIAN) {
+ block.x32[0]++;
+ } else {
+ counter++;
+ block.x32[0] = GSWAP4(counter);
+ }
+ todo = len - i;
+ if (todo > sizeof(keystream))
+ todo = sizeof(keystream);
+ /* Non optimal, but avoids alignment issues */
+ for (j = 0; j < todo; j++)
+ out[i + j] = in[i + j] ^ keystream[j];
+ }
+ return !error;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c
new file mode 100644
index 000000000000..fead51dd36f7
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_gcm_siv_polyval.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/evp.h>
+#include <internal/endian.h>
+#include <prov/implementations.h>
+#include "cipher_aes_gcm_siv.h"
+
+static ossl_inline void mulx_ghash(uint64_t *a)
+{
+ uint64_t t[2], mask;
+ DECLARE_IS_ENDIAN;
+
+ if (IS_LITTLE_ENDIAN) {
+ t[0] = GSWAP8(a[0]);
+ t[1] = GSWAP8(a[1]);
+ } else {
+ t[0] = a[0];
+ t[1] = a[1];
+ }
+ mask = -(int64_t)(t[1] & 1) & 0xe1;
+ mask <<= 56;
+
+ if (IS_LITTLE_ENDIAN) {
+ a[1] = GSWAP8((t[1] >> 1) ^ (t[0] << 63));
+ a[0] = GSWAP8((t[0] >> 1) ^ mask);
+ } else {
+ a[1] = (t[1] >> 1) ^ (t[0] << 63);
+ a[0] = (t[0] >> 1) ^ mask;
+ }
+}
+
+#define aligned64(p) (((uintptr_t)p & 0x07) == 0)
+static ossl_inline void byte_reverse16(uint8_t *out, const uint8_t *in)
+{
+ if (aligned64(out) && aligned64(in)) {
+ ((uint64_t *)out)[0] = GSWAP8(((uint64_t *)in)[1]);
+ ((uint64_t *)out)[1] = GSWAP8(((uint64_t *)in)[0]);
+ } else {
+ int i;
+
+ for (i = 0; i < 16; i++)
+ out[i] = in[15 - i];
+ }
+}
+
+/* Initialization of POLYVAL via existing GHASH implementation */
+void ossl_polyval_ghash_init(u128 Htable[16], const uint64_t H[2])
+{
+ uint64_t tmp[2];
+ DECLARE_IS_ENDIAN;
+
+ byte_reverse16((uint8_t *)tmp, (const uint8_t *)H);
+ mulx_ghash(tmp);
+ if (IS_LITTLE_ENDIAN) {
+ /* "H is stored in host byte order" */
+ tmp[0] = GSWAP8(tmp[0]);
+ tmp[1] = GSWAP8(tmp[1]);
+ }
+
+ ossl_gcm_init_4bit(Htable, (u64*)tmp);
+}
+
+/* Implementation of POLYVAL via existing GHASH implementation */
+void ossl_polyval_ghash_hash(const u128 Htable[16], uint8_t *tag, const uint8_t *inp, size_t len)
+{
+ uint64_t out[2];
+ uint64_t tmp[2];
+ size_t i;
+
+ byte_reverse16((uint8_t *)out, (uint8_t *)tag);
+
+ /*
+ * This implementation doesn't deal with partials, callers do,
+ * so, len is a multiple of 16
+ */
+ for (i = 0; i < len; i += 16) {
+ byte_reverse16((uint8_t *)tmp, &inp[i]);
+ ossl_gcm_ghash_4bit((u64*)out, Htable, (uint8_t *)tmp, 16);
+ }
+ byte_reverse16(tag, (uint8_t *)out);
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c
new file mode 100644
index 000000000000..a3b72d9f728a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2001-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_aes.h"
+
+static int cipher_hw_aes_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+#ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+ ret = HWAES_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)HWAES_decrypt;
+ dat->stream.cbc = NULL;
+# ifdef HWAES_cbc_encrypt
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt;
+# endif
+# ifdef HWAES_ecb_encrypt
+ if (dat->mode == EVP_CIPH_ECB_MODE)
+ dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt;
+# endif
+ } else
+#endif
+#ifdef BSAES_CAPABLE
+ if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CBC_MODE) {
+ ret = AES_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)AES_decrypt;
+ dat->stream.cbc = (cbc128_f)ossl_bsaes_cbc_encrypt;
+ } else
+#endif
+#ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ ret = vpaes_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)vpaes_decrypt;
+ dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE)
+ ?(cbc128_f)vpaes_cbc_encrypt : NULL;
+ } else
+#endif
+ {
+ ret = AES_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)AES_decrypt;
+ dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE)
+ ? (cbc128_f)AES_cbc_encrypt : NULL;
+ }
+ } else
+#ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+ ret = HWAES_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)HWAES_encrypt;
+ dat->stream.cbc = NULL;
+# ifdef HWAES_cbc_encrypt
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f)HWAES_cbc_encrypt;
+ else
+# endif
+# ifdef HWAES_ecb_encrypt
+ if (dat->mode == EVP_CIPH_ECB_MODE)
+ dat->stream.ecb = (ecb128_f)HWAES_ecb_encrypt;
+ else
+# endif
+# ifdef HWAES_ctr32_encrypt_blocks
+ if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks;
+ else
+# endif
+ (void)0; /* terminate potentially open 'else' */
+ } else
+#endif
+#ifdef BSAES_CAPABLE
+ if (BSAES_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE) {
+ ret = AES_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)AES_encrypt;
+ dat->stream.ctr = (ctr128_f)ossl_bsaes_ctr32_encrypt_blocks;
+ } else
+#endif
+#ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ ret = vpaes_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)vpaes_encrypt;
+ dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE)
+ ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+ } else
+#endif
+ {
+ ret = AES_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f)AES_encrypt;
+ dat->stream.cbc = (dat->mode == EVP_CIPH_CBC_MODE)
+ ? (cbc128_f)AES_cbc_encrypt : NULL;
+#ifdef AES_CTR_ASM
+ if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)AES_ctr32_encrypt;
+#endif
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aes_copyctx, PROV_AES_CTX)
+
+#define PROV_CIPHER_HW_aes_mode(mode) \
+static const PROV_CIPHER_HW aes_##mode = { \
+ cipher_hw_aes_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_aes_copyctx \
+}; \
+PROV_CIPHER_HW_declare(mode) \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_##mode(size_t keybits) \
+{ \
+ PROV_CIPHER_HW_select(mode) \
+ return &aes_##mode; \
+}
+
+#if defined(AESNI_CAPABLE)
+# include "cipher_aes_hw_aesni.inc"
+#elif defined(SPARC_AES_CAPABLE)
+# include "cipher_aes_hw_t4.inc"
+#elif defined(S390X_aes_128_CAPABLE)
+# include "cipher_aes_hw_s390x.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_aes_hw_rv64i.inc"
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
+# include "cipher_aes_hw_rv32i.inc"
+#elif defined (ARMv8_HWAES_CAPABLE)
+# include "cipher_aes_hw_armv8.inc"
+#else
+/* The generic case */
+# define PROV_CIPHER_HW_declare(mode)
+# define PROV_CIPHER_HW_select(mode)
+#endif
+
+PROV_CIPHER_HW_aes_mode(cbc)
+PROV_CIPHER_HW_aes_mode(ecb)
+PROV_CIPHER_HW_aes_mode(ofb128)
+PROV_CIPHER_HW_aes_mode(cfb128)
+PROV_CIPHER_HW_aes_mode(cfb1)
+PROV_CIPHER_HW_aes_mode(cfb8)
+PROV_CIPHER_HW_aes_mode(ctr)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc
new file mode 100644
index 000000000000..33b9046054e9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_aesni.inc
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * AES-NI support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+#define cipher_hw_aesni_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_aesni_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_aesni_cfb8 ossl_cipher_hw_generic_cfb8
+#define cipher_hw_aesni_cfb1 ossl_cipher_hw_generic_cfb1
+#define cipher_hw_aesni_ctr ossl_cipher_hw_generic_ctr
+
+static int cipher_hw_aesni_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+ ret = aesni_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) aesni_decrypt;
+ dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f) aesni_cbc_encrypt : NULL;
+ } else {
+ ret = aesni_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) aesni_encrypt;
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f) aesni_cbc_encrypt;
+ else if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f) aesni_ctr32_encrypt_blocks;
+ else
+ dat->stream.cbc = NULL;
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int cipher_hw_aesni_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ const AES_KEY *ks = ctx->ks;
+
+ aesni_cbc_encrypt(in, out, len, ks, ctx->iv, ctx->enc);
+
+ return 1;
+}
+
+static int cipher_hw_aesni_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ if (len < ctx->blocksize)
+ return 1;
+
+ aesni_ecb_encrypt(in, out, len, ctx->ks, ctx->enc);
+
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW aesni_##mode = { \
+ cipher_hw_aesni_initkey, \
+ cipher_hw_aesni_##mode, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (AESNI_CAPABLE) \
+ return &aesni_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc
new file mode 100644
index 000000000000..3f73c79290a8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_armv8.inc
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Crypto extension support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+static int cipher_hw_aes_arm_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key,
+ size_t keylen)
+{
+ int ret = cipher_hw_aes_initkey(dat, key, keylen);
+ if (AES_UNROLL12_EOR3_CAPABLE && dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)HWAES_ctr32_encrypt_blocks_unroll12_eor3;
+
+ return ret;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW aes_arm_##mode = { \
+ cipher_hw_aes_arm_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (ARMv8_HWAES_CAPABLE) \
+ return &aes_arm_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc
new file mode 100644
index 000000000000..f6c652c32d9b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv32i.inc
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 32 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+#define cipher_hw_rv32i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc
+#define cipher_hw_rv32i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb
+#define cipher_hw_rv32i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_rv32i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_rv32i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8
+#define cipher_hw_rv32i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1
+#define cipher_hw_rv32i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr
+
+#define cipher_hw_rv32i_zbkb_zknd_zkne_cbc ossl_cipher_hw_generic_cbc
+#define cipher_hw_rv32i_zbkb_zknd_zkne_ecb ossl_cipher_hw_generic_ecb
+#define cipher_hw_rv32i_zbkb_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8
+#define cipher_hw_rv32i_zbkb_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1
+#define cipher_hw_rv32i_zbkb_zknd_zkne_ctr ossl_cipher_hw_generic_ctr
+
+static int cipher_hw_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+ ret = rv32i_zknd_zkne_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv32i_zknd_decrypt;
+ dat->stream.cbc = NULL;
+ } else {
+ ret = rv32i_zkne_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv32i_zkne_encrypt;
+ dat->stream.cbc = NULL;
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int cipher_hw_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+ ret = rv32i_zbkb_zknd_zkne_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv32i_zknd_decrypt;
+ dat->stream.cbc = NULL;
+ } else {
+ ret = rv32i_zbkb_zkne_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv32i_zkne_encrypt;
+ dat->stream.cbc = NULL;
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW rv32i_zknd_zkne_##mode = { \
+ cipher_hw_rv32i_zknd_zkne_initkey, \
+ cipher_hw_rv32i_zknd_zkne_##mode, \
+ cipher_hw_aes_copyctx \
+}; \
+static const PROV_CIPHER_HW rv32i_zbkb_zknd_zkne_##mode = { \
+ cipher_hw_rv32i_zbkb_zknd_zkne_initkey, \
+ cipher_hw_rv32i_zbkb_zknd_zkne_##mode, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \
+ return &rv32i_zbkb_zknd_zkne_##mode; \
+if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &rv32i_zknd_zkne_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc
new file mode 100644
index 000000000000..07d479303d90
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_rv64i.inc
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 64 ZKND ZKNE support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+#define cipher_hw_rv64i_zknd_zkne_cbc ossl_cipher_hw_generic_cbc
+#define cipher_hw_rv64i_zknd_zkne_ecb ossl_cipher_hw_generic_ecb
+#define cipher_hw_rv64i_zknd_zkne_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_rv64i_zknd_zkne_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_rv64i_zknd_zkne_cfb8 ossl_cipher_hw_generic_cfb8
+#define cipher_hw_rv64i_zknd_zkne_cfb1 ossl_cipher_hw_generic_cfb1
+#define cipher_hw_rv64i_zknd_zkne_ctr ossl_cipher_hw_generic_ctr
+
+static int cipher_hw_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+ ret = rv64i_zknd_set_decrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv64i_zknd_decrypt;
+ dat->stream.cbc = NULL;
+ } else {
+ ret = rv64i_zkne_set_encrypt_key(key, keylen * 8, ks);
+ dat->block = (block128_f) rv64i_zkne_encrypt;
+ dat->stream.cbc = NULL;
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*-
+ * RISC-V RV64 ZVKNED support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+#define cipher_hw_rv64i_zvkned_cbc ossl_cipher_hw_generic_cbc
+#define cipher_hw_rv64i_zvkned_ecb ossl_cipher_hw_generic_ecb
+#define cipher_hw_rv64i_zvkned_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_rv64i_zvkned_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_rv64i_zvkned_cfb8 ossl_cipher_hw_generic_cfb8
+#define cipher_hw_rv64i_zvkned_cfb1 ossl_cipher_hw_generic_cfb1
+#define cipher_hw_rv64i_zvkned_ctr ossl_cipher_hw_generic_ctr
+
+static int cipher_hw_rv64i_zvkned_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key,
+ size_t keylen)
+{
+ int ret;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+
+ /*
+ * Zvkned only supports 128 and 256 bit keys for key schedule generation.
+ * For AES-192 case, we could fallback to `AES_set_encrypt_key`.
+ * All Zvkned-based implementations use the same `encrypt-key` scheduling
+ * for both encryption and decryption.
+ */
+ if (keylen * 8 == 128 || keylen * 8 == 256) {
+ ret = rv64i_zvkned_set_encrypt_key(key, keylen * 8, ks);
+ } else {
+ ret = AES_set_encrypt_key(key, keylen * 8, ks);
+ }
+
+ if (dat->mode == EVP_CIPH_CBC_MODE) {
+ if (dat->enc) {
+ dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_encrypt;
+ } else {
+ dat->stream.cbc = (cbc128_f) rv64i_zvkned_cbc_decrypt;
+ }
+ } else if (dat->mode == EVP_CIPH_CTR_MODE) {
+ if (RISCV_HAS_ZVKB()) {
+ dat->stream.ctr = (ctr128_f) rv64i_zvkb_zvkned_ctr32_encrypt_blocks;
+ }
+ } else if (dat->mode == EVP_CIPH_ECB_MODE) {
+ if (dat->enc) {
+ dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_encrypt;
+ } else {
+ dat->stream.ecb = (ecb128_f) rv64i_zvkned_ecb_decrypt;
+ }
+ }
+
+ /* Zvkned supports aes-128/192/256 encryption and decryption. */
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE) &&
+ !dat->enc) {
+ dat->block = (block128_f) rv64i_zvkned_decrypt;
+ } else {
+ dat->block = (block128_f) rv64i_zvkned_encrypt;
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW rv64i_zknd_zkne_##mode = { \
+ cipher_hw_rv64i_zknd_zkne_initkey, \
+ cipher_hw_rv64i_zknd_zkne_##mode, \
+ cipher_hw_aes_copyctx \
+}; \
+static const PROV_CIPHER_HW rv64i_zvkned_##mode = { \
+ cipher_hw_rv64i_zvkned_initkey, \
+ cipher_hw_rv64i_zvkned_##mode, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \
+ return &rv64i_zvkned_##mode; \
+else if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &rv64i_zknd_zkne_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc
new file mode 100644
index 000000000000..6c4a4cc99511
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_s390x.inc
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2001-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * IBM S390X support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+#include "s390x_arch.h"
+
+#include <stdio.h>
+
+#define s390x_aes_cbc_initkey cipher_hw_aes_initkey
+#define s390x_aes_cfb1_initkey cipher_hw_aes_initkey
+#define s390x_aes_ctr_initkey cipher_hw_aes_initkey
+#define s390x_aes_cbc_cipher_hw ossl_cipher_hw_generic_cbc
+#define s390x_aes_cfb1_cipher_hw ossl_cipher_hw_generic_cfb1
+#define s390x_aes_ctr_cipher_hw ossl_cipher_hw_generic_ctr
+
+#define S390X_aes_128_ofb128_CAPABLE S390X_aes_128_ofb_CAPABLE
+#define S390X_aes_192_ofb128_CAPABLE S390X_aes_192_ofb_CAPABLE
+#define S390X_aes_256_ofb128_CAPABLE S390X_aes_256_ofb_CAPABLE
+#define S390X_aes_128_cfb128_CAPABLE S390X_aes_128_cfb_CAPABLE
+#define S390X_aes_192_cfb128_CAPABLE S390X_aes_192_cfb_CAPABLE
+#define S390X_aes_256_cfb128_CAPABLE S390X_aes_256_cfb_CAPABLE
+
+static int s390x_aes_ecb_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+
+ adat->plat.s390x.fc = S390X_AES_FC(keylen);
+ memcpy(adat->plat.s390x.param.km.k, key, keylen);
+ return 1;
+}
+
+static int s390x_aes_ecb_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
+
+ s390x_km(in, len, out, adat->plat.s390x.fc | modifier,
+ &adat->plat.s390x.param.km);
+ return 1;
+}
+
+static int s390x_aes_ofb128_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+
+ memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen);
+ adat->plat.s390x.fc = S390X_AES_FC(keylen);
+ return 1;
+}
+
+static int s390x_aes_ofb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ int n = dat->num;
+ int rem;
+
+ memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen);
+ while (n && len) {
+ *out = *in ^ adat->plat.s390x.param.kmo_kmf.cv[n];
+ n = (n + 1) & 0xf;
+ --len;
+ ++in;
+ ++out;
+ }
+
+ rem = len & 0xf;
+
+ len &= ~(size_t)0xf;
+ if (len) {
+ s390x_kmo(in, len, out, adat->plat.s390x.fc,
+ &adat->plat.s390x.param.kmo_kmf);
+
+ out += len;
+ in += len;
+ }
+
+ if (rem) {
+ s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16,
+ adat->plat.s390x.param.kmo_kmf.cv,
+ adat->plat.s390x.fc,
+ adat->plat.s390x.param.kmo_kmf.k);
+
+ while (rem--) {
+ out[n] = in[n] ^ adat->plat.s390x.param.kmo_kmf.cv[n];
+ ++n;
+ }
+ }
+
+ memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen);
+ dat->num = n;
+ return 1;
+}
+
+static int s390x_aes_cfb128_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+
+ adat->plat.s390x.fc = S390X_AES_FC(keylen);
+ adat->plat.s390x.fc |= 16 << 24; /* 16 bytes cipher feedback */
+ memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen);
+ return 1;
+}
+
+static int s390x_aes_cfb128_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
+ int n = dat->num;
+ int rem;
+ unsigned char tmp;
+
+ memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen);
+ while (n && len) {
+ tmp = *in;
+ *out = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp;
+ adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? *out : tmp;
+ n = (n + 1) & 0xf;
+ --len;
+ ++in;
+ ++out;
+ }
+
+ rem = len & 0xf;
+
+ len &= ~(size_t)0xf;
+ if (len) {
+ s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier,
+ &adat->plat.s390x.param.kmo_kmf);
+
+ out += len;
+ in += len;
+ }
+
+ if (rem) {
+ s390x_km(adat->plat.s390x.param.kmo_kmf.cv, 16,
+ adat->plat.s390x.param.kmo_kmf.cv,
+ S390X_AES_FC(dat->keylen),
+ adat->plat.s390x.param.kmo_kmf.k);
+
+ while (rem--) {
+ tmp = in[n];
+ out[n] = adat->plat.s390x.param.kmo_kmf.cv[n] ^ tmp;
+ adat->plat.s390x.param.kmo_kmf.cv[n] = dat->enc ? out[n] : tmp;
+ ++n;
+ }
+ }
+
+ memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen);
+ dat->num = n;
+ return 1;
+}
+
+static int s390x_aes_cfb8_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+
+ adat->plat.s390x.fc = S390X_AES_FC(keylen);
+ adat->plat.s390x.fc |= 1 << 24; /* 1 byte cipher feedback */
+ memcpy(adat->plat.s390x.param.kmo_kmf.k, key, keylen);
+ return 1;
+}
+
+static int s390x_aes_cfb8_cipher_hw(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ unsigned int modifier = adat->base.enc ? 0 : S390X_DECRYPT;
+
+ memcpy(adat->plat.s390x.param.kmo_kmf.cv, dat->iv, dat->ivlen);
+ s390x_kmf(in, len, out, adat->plat.s390x.fc | modifier,
+ &adat->plat.s390x.param.kmo_kmf);
+ memcpy(dat->iv, adat->plat.s390x.param.kmo_kmf.cv, dat->ivlen);
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW s390x_aes_##mode = { \
+ s390x_aes_##mode##_initkey, \
+ s390x_aes_##mode##_cipher_hw, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if ((keybits == 128 && S390X_aes_128_##mode##_CAPABLE) \
+ || (keybits == 192 && S390X_aes_192_##mode##_CAPABLE) \
+ || (keybits == 256 && S390X_aes_256_##mode##_CAPABLE)) \
+ return &s390x_aes_##mode;
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc
new file mode 100644
index 000000000000..28454fc5089b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_hw_t4.inc
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Sparc t4 support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_aes_hw.c
+ */
+
+static int cipher_hw_aes_t4_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret, bits;
+ PROV_AES_CTX *adat = (PROV_AES_CTX *)dat;
+ AES_KEY *ks = &adat->ks.ks;
+
+ dat->ks = (const void *)ks; /* used by cipher_hw_generic_XXX */
+
+ bits = keylen * 8;
+ if ((dat->mode == EVP_CIPH_ECB_MODE || dat->mode == EVP_CIPH_CBC_MODE)
+ && !dat->enc) {
+ ret = 0;
+ aes_t4_set_decrypt_key(key, bits, ks);
+ dat->block = (block128_f)aes_t4_decrypt;
+ switch (bits) {
+ case 128:
+ dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f)aes128_t4_cbc_decrypt : NULL;
+ break;
+ case 192:
+ dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f)aes192_t4_cbc_decrypt : NULL;
+ break;
+ case 256:
+ dat->stream.cbc = dat->mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f)aes256_t4_cbc_decrypt : NULL;
+ break;
+ default:
+ ret = -1;
+ }
+ } else {
+ ret = 0;
+ aes_t4_set_encrypt_key(key, bits, ks);
+ dat->block = (block128_f)aes_t4_encrypt;
+ switch (bits) {
+ case 128:
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f)aes128_t4_cbc_encrypt;
+ else if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)aes128_t4_ctr32_encrypt;
+ else
+ dat->stream.cbc = NULL;
+ break;
+ case 192:
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f)aes192_t4_cbc_encrypt;
+ else if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)aes192_t4_ctr32_encrypt;
+ else
+ dat->stream.cbc = NULL;
+ break;
+ case 256:
+ if (dat->mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f)aes256_t4_cbc_encrypt;
+ else if (dat->mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f)aes256_t4_ctr32_encrypt;
+ else
+ dat->stream.cbc = NULL;
+ break;
+ default:
+ ret = -1;
+ }
+ }
+
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW aes_t4_##mode = { \
+ cipher_hw_aes_t4_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_aes_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+ if (SPARC_AES_CAPABLE) \
+ return &aes_t4_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c
new file mode 100644
index 000000000000..876ff294246c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_aes_ocb.h"
+#include "prov/providercommon.h"
+#include "prov/ciphercommon_aead.h"
+#include "prov/implementations.h"
+
+#define AES_OCB_FLAGS AEAD_FLAGS
+
+#define OCB_DEFAULT_TAG_LEN 16
+#define OCB_DEFAULT_IV_LEN 12
+#define OCB_MIN_IV_LEN 1
+#define OCB_MAX_IV_LEN 15
+
+PROV_CIPHER_FUNC(int, ocb_cipher, (PROV_AES_OCB_CTX *ctx,
+ const unsigned char *in, unsigned char *out,
+ size_t nextblock));
+/* forward declarations */
+static OSSL_FUNC_cipher_encrypt_init_fn aes_ocb_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn aes_ocb_dinit;
+static OSSL_FUNC_cipher_update_fn aes_ocb_block_update;
+static OSSL_FUNC_cipher_final_fn aes_ocb_block_final;
+static OSSL_FUNC_cipher_cipher_fn aes_ocb_cipher;
+static OSSL_FUNC_cipher_freectx_fn aes_ocb_freectx;
+static OSSL_FUNC_cipher_dupctx_fn aes_ocb_dupctx;
+static OSSL_FUNC_cipher_get_ctx_params_fn aes_ocb_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_ocb_set_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn cipher_ocb_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn cipher_ocb_settable_ctx_params;
+
+/*
+ * The following methods could be moved into PROV_AES_OCB_HW if
+ * multiple hardware implementations are ever needed.
+ */
+static ossl_inline int aes_generic_ocb_setiv(PROV_AES_OCB_CTX *ctx,
+ const unsigned char *iv,
+ size_t ivlen, size_t taglen)
+{
+ return (CRYPTO_ocb128_setiv(&ctx->ocb, iv, ivlen, taglen) == 1);
+}
+
+static ossl_inline int aes_generic_ocb_setaad(PROV_AES_OCB_CTX *ctx,
+ const unsigned char *aad,
+ size_t alen)
+{
+ return CRYPTO_ocb128_aad(&ctx->ocb, aad, alen) == 1;
+}
+
+static ossl_inline int aes_generic_ocb_gettag(PROV_AES_OCB_CTX *ctx,
+ unsigned char *tag, size_t tlen)
+{
+ return CRYPTO_ocb128_tag(&ctx->ocb, tag, tlen) > 0;
+}
+
+static ossl_inline int aes_generic_ocb_final(PROV_AES_OCB_CTX *ctx)
+{
+ return (CRYPTO_ocb128_finish(&ctx->ocb, ctx->tag, ctx->taglen) == 0);
+}
+
+static ossl_inline void aes_generic_ocb_cleanup(PROV_AES_OCB_CTX *ctx)
+{
+ CRYPTO_ocb128_cleanup(&ctx->ocb);
+}
+
+static ossl_inline int aes_generic_ocb_cipher(PROV_AES_OCB_CTX *ctx,
+ const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ if (ctx->base.enc) {
+ if (!CRYPTO_ocb128_encrypt(&ctx->ocb, in, out, len))
+ return 0;
+ } else {
+ if (!CRYPTO_ocb128_decrypt(&ctx->ocb, in, out, len))
+ return 0;
+ }
+ return 1;
+}
+
+static ossl_inline int aes_generic_ocb_copy_ctx(PROV_AES_OCB_CTX *dst,
+ PROV_AES_OCB_CTX *src)
+{
+ return CRYPTO_ocb128_copy_ctx(&dst->ocb, &src->ocb,
+ &dst->ksenc.ks, &dst->ksdec.ks);
+}
+
+/*-
+ * Provider dispatch functions
+ */
+static int aes_ocb_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->aad_buf_len = 0;
+ ctx->data_buf_len = 0;
+ ctx->base.enc = enc;
+
+ if (iv != NULL) {
+ if (ivlen != ctx->base.ivlen) {
+ /* IV len must be 1 to 15 */
+ if (ivlen < OCB_MIN_IV_LEN || ivlen > OCB_MAX_IV_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ ctx->base.ivlen = ivlen;
+ }
+ if (!ossl_cipher_generic_initiv(&ctx->base, iv, ivlen))
+ return 0;
+ ctx->iv_state = IV_STATE_BUFFERED;
+ }
+ if (key != NULL) {
+ if (keylen != ctx->base.keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->base.hw->init(&ctx->base, key, keylen))
+ return 0;
+ }
+ return aes_ocb_set_ctx_params(ctx, params);
+}
+
+static int aes_ocb_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return aes_ocb_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int aes_ocb_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return aes_ocb_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+/*
+ * Because of the way OCB works, both the AAD and data are buffered in the
+ * same way. Only the last block can be a partial block.
+ */
+static int aes_ocb_block_update_internal(PROV_AES_OCB_CTX *ctx,
+ unsigned char *buf, size_t *bufsz,
+ unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl, OSSL_ocb_cipher_fn ciph)
+{
+ size_t nextblocks;
+ size_t outlint = 0;
+
+ if (*bufsz != 0)
+ nextblocks = ossl_cipher_fillblock(buf, bufsz, AES_BLOCK_SIZE, &in, &inl);
+ else
+ nextblocks = inl & ~(AES_BLOCK_SIZE-1);
+
+ if (*bufsz == AES_BLOCK_SIZE) {
+ if (outsize < AES_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!ciph(ctx, buf, out, AES_BLOCK_SIZE)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ *bufsz = 0;
+ outlint = AES_BLOCK_SIZE;
+ if (out != NULL)
+ out += AES_BLOCK_SIZE;
+ }
+ if (nextblocks > 0) {
+ outlint += nextblocks;
+ if (outsize < outlint) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!ciph(ctx, in, out, nextblocks)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ in += nextblocks;
+ inl -= nextblocks;
+ }
+ if (inl != 0
+ && !ossl_cipher_trailingdata(buf, bufsz, AES_BLOCK_SIZE, &in, &inl)) {
+ /* PROVerr already called */
+ return 0;
+ }
+
+ *outl = outlint;
+ return inl == 0;
+}
+
+/* A wrapper function that has the same signature as cipher */
+static int cipher_updateaad(PROV_AES_OCB_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ return aes_generic_ocb_setaad(ctx, in, len);
+}
+
+static int update_iv(PROV_AES_OCB_CTX *ctx)
+{
+ if (ctx->iv_state == IV_STATE_FINISHED
+ || ctx->iv_state == IV_STATE_UNINITIALISED)
+ return 0;
+ if (ctx->iv_state == IV_STATE_BUFFERED) {
+ if (!aes_generic_ocb_setiv(ctx, ctx->base.iv, ctx->base.ivlen,
+ ctx->taglen))
+ return 0;
+ ctx->iv_state = IV_STATE_COPIED;
+ }
+ return 1;
+}
+
+static int aes_ocb_block_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+ unsigned char *buf;
+ size_t *buflen;
+ OSSL_ocb_cipher_fn fn;
+
+ if (!ctx->key_set || !update_iv(ctx))
+ return 0;
+
+ if (inl == 0) {
+ *outl = 0;
+ return 1;
+ }
+
+ /* Are we dealing with AAD or normal data here? */
+ if (out == NULL) {
+ buf = ctx->aad_buf;
+ buflen = &ctx->aad_buf_len;
+ fn = cipher_updateaad;
+ } else {
+ buf = ctx->data_buf;
+ buflen = &ctx->data_buf_len;
+ fn = aes_generic_ocb_cipher;
+ }
+ return aes_ocb_block_update_internal(ctx, buf, buflen, out, outl, outsize,
+ in, inl, fn);
+}
+
+static int aes_ocb_block_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* If no block_update has run then the iv still needs to be set */
+ if (!ctx->key_set || !update_iv(ctx))
+ return 0;
+
+ /*
+ * Empty the buffer of any partial block that we might have been provided,
+ * both for data and AAD
+ */
+ *outl = 0;
+ if (ctx->data_buf_len > 0) {
+ if (!aes_generic_ocb_cipher(ctx, ctx->data_buf, out, ctx->data_buf_len))
+ return 0;
+ *outl = ctx->data_buf_len;
+ ctx->data_buf_len = 0;
+ }
+ if (ctx->aad_buf_len > 0) {
+ if (!aes_generic_ocb_setaad(ctx, ctx->aad_buf, ctx->aad_buf_len))
+ return 0;
+ ctx->aad_buf_len = 0;
+ }
+ if (ctx->base.enc) {
+ /* If encrypting then just get the tag */
+ if (!aes_generic_ocb_gettag(ctx, ctx->tag, ctx->taglen))
+ return 0;
+ } else {
+ /* If decrypting then verify */
+ if (ctx->taglen == 0)
+ return 0;
+ if (!aes_generic_ocb_final(ctx))
+ return 0;
+ }
+ /* Don't reuse the IV */
+ ctx->iv_state = IV_STATE_FINISHED;
+ return 1;
+}
+
+static void *aes_ocb_newctx(void *provctx, size_t kbits, size_t blkbits,
+ size_t ivbits, unsigned int mode, uint64_t flags)
+{
+ PROV_AES_OCB_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags,
+ ossl_prov_cipher_hw_aes_ocb(kbits), NULL);
+ ctx->taglen = OCB_DEFAULT_TAG_LEN;
+ }
+ return ctx;
+}
+
+static void aes_ocb_freectx(void *vctx)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ if (ctx != NULL) {
+ aes_generic_ocb_cleanup(ctx);
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+static void *aes_ocb_dupctx(void *vctx)
+{
+ PROV_AES_OCB_CTX *in = (PROV_AES_OCB_CTX *)vctx;
+ PROV_AES_OCB_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+ if (!aes_generic_ocb_copy_ctx(ret, in)) {
+ OPENSSL_free(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+static int aes_ocb_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t sz;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (p->data == NULL) {
+ /* Tag len must be 0 to 16 */
+ if (p->data_size > OCB_MAX_TAG_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ ctx->taglen = p->data_size;
+ } else {
+ if (ctx->base.enc) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ if (p->data_size != ctx->taglen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->tag, p->data, p->data_size);
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* IV len must be 1 to 15 */
+ if (sz < OCB_MIN_IV_LEN || sz > OCB_MAX_IV_LEN)
+ return 0;
+ if (ctx->base.ivlen != sz) {
+ ctx->base.ivlen = sz;
+ ctx->iv_state = IV_STATE_UNINITIALISED;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t keylen;
+
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->base.keylen != keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int aes_ocb_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, ctx->taglen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+ if (p != NULL) {
+ if (ctx->base.ivlen > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->base.oiv, ctx->base.ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.oiv, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
+ if (p != NULL) {
+ if (ctx->base.ivlen > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->base.iv, ctx->base.ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->base.iv, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (!ctx->base.enc || p->data_size != ctx->taglen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ memcpy(p->data, ctx->tag, ctx->taglen);
+ }
+ return 1;
+}
+
+static const OSSL_PARAM cipher_ocb_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *cipher_ocb_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *p_ctx)
+{
+ return cipher_ocb_known_gettable_ctx_params;
+}
+
+static const OSSL_PARAM cipher_ocb_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *cipher_ocb_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *p_ctx)
+{
+ return cipher_ocb_known_settable_ctx_params;
+}
+
+static int aes_ocb_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!aes_generic_ocb_cipher(ctx, in, out, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ *outl = inl;
+ return 1;
+}
+
+#define IMPLEMENT_cipher(mode, UCMODE, flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##mode##_get_params; \
+static int aes_##kbits##_##mode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn aes_##kbits##_##mode##_newctx; \
+static void *aes_##kbits##_##mode##_newctx(void *provctx) \
+{ \
+ return aes_##mode##_newctx(provctx, kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags); \
+} \
+const OSSL_DISPATCH ossl_##aes##kbits##mode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void))aes_##kbits##_##mode##_newctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_##mode##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_##mode##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_##mode##_block_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_##mode##_block_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))aes_ocb_cipher }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_##mode##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_##mode##_dupctx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))aes_##kbits##_##mode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))aes_##mode##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))aes_##mode##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))cipher_ocb_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))cipher_ocb_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 256, 128, OCB_DEFAULT_IV_LEN * 8);
+IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 192, 128, OCB_DEFAULT_IV_LEN * 8);
+IMPLEMENT_cipher(ocb, OCB, AES_OCB_FLAGS, 128, 128, OCB_DEFAULT_IV_LEN * 8);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h
new file mode 100644
index 000000000000..370717b43614
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+#define OCB_MAX_TAG_LEN AES_BLOCK_SIZE
+#define OCB_MAX_DATA_LEN AES_BLOCK_SIZE
+#define OCB_MAX_AAD_LEN AES_BLOCK_SIZE
+
+typedef struct prov_aes_ocb_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ksenc; /* AES key schedule to use for encryption/aad */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ksdec; /* AES key schedule to use for decryption */
+ OCB128_CONTEXT ocb;
+ unsigned int iv_state; /* set to one of IV_STATE_XXX */
+ unsigned int key_set : 1;
+ size_t taglen;
+ size_t data_buf_len;
+ size_t aad_buf_len;
+ unsigned char tag[OCB_MAX_TAG_LEN];
+ unsigned char data_buf[OCB_MAX_DATA_LEN]; /* Store partial data blocks */
+ unsigned char aad_buf[OCB_MAX_AAD_LEN]; /* Store partial AAD blocks */
+} PROV_AES_OCB_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ocb(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c
new file mode 100644
index 000000000000..00920408b918
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_ocb_hw.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_ocb.h"
+
+#define OCB_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \
+ fn_block_enc, fn_block_dec, \
+ fn_stream_enc, fn_stream_dec) \
+CRYPTO_ocb128_cleanup(&ctx->ocb); \
+fn_set_enc_key(key, keylen * 8, &ctx->ksenc.ks); \
+fn_set_dec_key(key, keylen * 8, &ctx->ksdec.ks); \
+if (!CRYPTO_ocb128_init(&ctx->ocb, &ctx->ksenc.ks, &ctx->ksdec.ks, \
+ (block128_f)fn_block_enc, (block128_f)fn_block_dec, \
+ ctx->base.enc ? (ocb128_f)fn_stream_enc : \
+ (ocb128_f)fn_stream_dec)) \
+ return 0; \
+ctx->key_set = 1
+
+
+static int cipher_hw_aes_ocb_generic_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+/*
+ * We set both the encrypt and decrypt key here because decrypt
+ * needs both. (i.e- AAD uses encrypt).
+ */
+# ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+ OCB_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_set_decrypt_key,
+ HWAES_encrypt, HWAES_decrypt,
+ HWAES_ocb_encrypt, HWAES_ocb_decrypt);
+ } else
+# endif
+# ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ OCB_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_set_decrypt_key,
+ vpaes_encrypt, vpaes_decrypt, NULL, NULL);
+ } else
+# endif
+ {
+ OCB_SET_KEY_FN(AES_set_encrypt_key, AES_set_decrypt_key,
+ AES_encrypt, AES_decrypt, NULL, NULL);
+ }
+ return 1;
+}
+
+# if defined(AESNI_CAPABLE)
+
+static int cipher_hw_aes_ocb_aesni_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ OCB_SET_KEY_FN(aesni_set_encrypt_key, aesni_set_decrypt_key,
+ aesni_encrypt, aesni_decrypt,
+ aesni_ocb_encrypt, aesni_ocb_decrypt);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare() \
+static const PROV_CIPHER_HW aesni_ocb = { \
+ cipher_hw_aes_ocb_aesni_initkey, \
+ NULL \
+};
+# define PROV_CIPHER_HW_select() \
+ if (AESNI_CAPABLE) \
+ return &aesni_ocb;
+
+#elif defined(SPARC_AES_CAPABLE)
+
+static int cipher_hw_aes_ocb_t4_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ OCB_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_set_decrypt_key,
+ aes_t4_encrypt, aes_t4_decrypt, NULL, NULL);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare() \
+static const PROV_CIPHER_HW aes_t4_ocb = { \
+ cipher_hw_aes_ocb_t4_initkey, \
+ NULL \
+};
+# define PROV_CIPHER_HW_select() \
+ if (SPARC_AES_CAPABLE) \
+ return &aes_t4_ocb;
+
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+
+static int cipher_hw_aes_ocb_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ OCB_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key,
+ rv64i_zkne_encrypt, rv64i_zknd_decrypt, NULL, NULL);
+ return 1;
+}
+
+static int cipher_hw_aes_ocb_rv64i_zvkned_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ /* Zvkned only supports 128 and 256 bit keys. */
+ if (keylen * 8 == 128 || keylen * 8 == 256) {
+ OCB_SET_KEY_FN(rv64i_zvkned_set_encrypt_key,
+ rv64i_zvkned_set_decrypt_key,
+ rv64i_zvkned_encrypt, rv64i_zvkned_decrypt,
+ NULL, NULL);
+ } else {
+ OCB_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key,
+ rv64i_zvkned_encrypt, rv64i_zvkned_decrypt,
+ NULL, NULL);
+ }
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare() \
+static const PROV_CIPHER_HW aes_rv64i_zknd_zkne_ocb = { \
+ cipher_hw_aes_ocb_rv64i_zknd_zkne_initkey, \
+ NULL \
+}; \
+static const PROV_CIPHER_HW aes_rv64i_zvkned_ocb = { \
+ cipher_hw_aes_ocb_rv64i_zvkned_initkey, \
+ NULL \
+};
+# define PROV_CIPHER_HW_select() \
+ if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \
+ return &aes_rv64i_zvkned_ocb; \
+ else if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &aes_rv64i_zknd_zkne_ocb;
+
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
+
+static int cipher_hw_aes_ocb_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ OCB_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key,
+ rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL);
+ return 1;
+}
+
+static int cipher_hw_aes_ocb_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *vctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
+
+ OCB_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key,
+ rv32i_zkne_encrypt, rv32i_zknd_decrypt, NULL, NULL);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare() \
+static const PROV_CIPHER_HW aes_rv32i_zknd_zkne_ocb = { \
+ cipher_hw_aes_ocb_rv32i_zknd_zkne_initkey, \
+ NULL \
+}; \
+static const PROV_CIPHER_HW aes_rv32i_zbkb_zknd_zkne_ocb = { \
+ cipher_hw_aes_ocb_rv32i_zbkb_zknd_zkne_initkey, \
+ NULL \
+};
+# define PROV_CIPHER_HW_select() \
+ if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \
+ return &aes_rv32i_zbkb_zknd_zkne_ocb; \
+ if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &aes_rv32i_zknd_zkne_ocb;
+#else
+# define PROV_CIPHER_HW_declare()
+# define PROV_CIPHER_HW_select()
+# endif
+
+static const PROV_CIPHER_HW aes_generic_ocb = {
+ cipher_hw_aes_ocb_generic_initkey,
+ NULL
+};
+PROV_CIPHER_HW_declare()
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_ocb(size_t keybits)
+{
+ PROV_CIPHER_HW_select()
+ return &aes_generic_ocb;
+}
+
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c
new file mode 100644
index 000000000000..ccac2bbe9663
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for AES SIV mode */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_aes_siv.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/ciphercommon_aead.h"
+#include "prov/provider_ctx.h"
+
+#define siv_stream_update siv_cipher
+#define SIV_FLAGS AEAD_FLAGS
+
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_siv_set_ctx_params;
+
+static void *aes_siv_newctx(void *provctx, size_t keybits, unsigned int mode,
+ uint64_t flags)
+{
+ PROV_AES_SIV_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ctx->taglen = SIV_LEN;
+ ctx->mode = mode;
+ ctx->keylen = keybits / 8;
+ ctx->hw = ossl_prov_cipher_hw_aes_siv(keybits);
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ }
+ return ctx;
+}
+
+static void aes_siv_freectx(void *vctx)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+
+ if (ctx != NULL) {
+ ctx->hw->cleanup(ctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+static void *siv_dupctx(void *vctx)
+{
+ PROV_AES_SIV_CTX *in = (PROV_AES_SIV_CTX *)vctx;
+ PROV_AES_SIV_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ if (!in->hw->dupctx(in, ret)) {
+ OPENSSL_free(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+static int siv_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->initkey(ctx, key, ctx->keylen))
+ return 0;
+ }
+ return aes_siv_set_ctx_params(ctx, params);
+}
+
+static int siv_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return siv_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int siv_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return siv_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static int siv_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (out != NULL && outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (ctx->hw->cipher(ctx, out, in, inl) <= 0)
+ return 0;
+
+ if (outl != NULL)
+ *outl = inl;
+ return 1;
+}
+
+static int siv_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ctx->hw->cipher(vctx, out, NULL, 0))
+ return 0;
+
+ if (outl != NULL)
+ *outl = 0;
+ return 1;
+}
+
+static int aes_siv_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING) {
+ if (!ctx->enc
+ || p->data_size != ctx->taglen
+ || !OSSL_PARAM_set_octet_string(p, &sctx->tag.byte, ctx->taglen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->taglen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_siv_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *aes_siv_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_siv_known_gettable_ctx_params;
+}
+
+static int aes_siv_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ const OSSL_PARAM *p;
+ unsigned int speed = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (ctx->enc)
+ return 1;
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !ctx->hw->settag(ctx, p->data, p->data_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_SPEED);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &speed)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->hw->setspeed(ctx, (int)speed);
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t keylen;
+
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* The key length can not be modified */
+ if (keylen != ctx->keylen)
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM aes_siv_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_SPEED, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *aes_siv_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_siv_known_settable_ctx_params;
+}
+
+#define IMPLEMENT_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_newctx_fn alg##kbits##lc##_newctx; \
+static OSSL_FUNC_cipher_freectx_fn alg##_##lc##_freectx; \
+static OSSL_FUNC_cipher_dupctx_fn lc##_dupctx; \
+static OSSL_FUNC_cipher_encrypt_init_fn lc##_einit; \
+static OSSL_FUNC_cipher_decrypt_init_fn lc##_dinit; \
+static OSSL_FUNC_cipher_update_fn lc##_stream_update; \
+static OSSL_FUNC_cipher_final_fn lc##_stream_final; \
+static OSSL_FUNC_cipher_cipher_fn lc##_cipher; \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lc##_get_params; \
+static OSSL_FUNC_cipher_get_ctx_params_fn alg##_##lc##_get_ctx_params; \
+static OSSL_FUNC_cipher_gettable_ctx_params_fn \
+ alg##_##lc##_gettable_ctx_params; \
+static OSSL_FUNC_cipher_set_ctx_params_fn alg##_##lc##_set_ctx_params; \
+static OSSL_FUNC_cipher_settable_ctx_params_fn \
+ alg##_##lc##_settable_ctx_params; \
+static int alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, 2*kbits, blkbits, ivbits); \
+} \
+static void *alg##kbits##lc##_newctx(void *provctx) \
+{ \
+ return alg##_##lc##_newctx(provctx, 2*kbits, EVP_CIPH_##UCMODE##_MODE, \
+ flags); \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))alg##kbits##lc##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))alg##_##lc##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) lc##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) lc##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) lc##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void)) lc##_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void)) lc##_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void)) lc##_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lc##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void)) alg##_##lc##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void)) alg##_##lc##_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void)) alg##_##lc##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void)) alg##_##lc##_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 128, 8, 0)
+IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 192, 8, 0)
+IMPLEMENT_cipher(aes, siv, SIV, SIV_FLAGS, 256, 8, 0)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h
new file mode 100644
index 000000000000..4a682b77c440
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+#include "crypto/siv.h"
+
+typedef struct prov_cipher_hw_aes_siv_st {
+ int (*initkey)(void *ctx, const uint8_t *key, size_t keylen);
+ int (*cipher)(void *ctx, unsigned char *out, const unsigned char *in,
+ size_t len);
+ void (*setspeed)(void *ctx, int speed);
+ int (*settag)(void *ctx, const unsigned char *tag, size_t tagl);
+ void (*cleanup)(void *ctx);
+ int (*dupctx)(void *src, void *dst);
+} PROV_CIPHER_HW_AES_SIV;
+
+typedef struct prov_siv_ctx_st {
+ unsigned int mode; /* The mode that we are using */
+ unsigned int enc : 1; /* Set to 1 if we are encrypting or 0 otherwise */
+ size_t keylen; /* The input keylength (twice the alg key length) */
+ size_t taglen; /* the taglen is the same as the sivlen */
+ SIV128_CONTEXT siv;
+ EVP_CIPHER *ctr; /* These are fetched - so we need to free them */
+ EVP_CIPHER *cbc;
+ const PROV_CIPHER_HW_AES_SIV *hw;
+ OSSL_LIB_CTX *libctx;
+} PROV_AES_SIV_CTX;
+
+const PROV_CIPHER_HW_AES_SIV *ossl_prov_cipher_hw_aes_siv(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c
new file mode 100644
index 000000000000..75fd54829c4b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_siv_hw.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_siv.h"
+
+static void aes_siv_cleanup(void *vctx);
+
+static int aes_siv_initkey(void *vctx, const unsigned char *key, size_t keylen)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+ size_t klen = keylen / 2;
+ OSSL_LIB_CTX *libctx = ctx->libctx;
+ const char *propq = NULL;
+
+ EVP_CIPHER_free(ctx->cbc);
+ EVP_CIPHER_free(ctx->ctr);
+ ctx->cbc = NULL;
+ ctx->ctr = NULL;
+
+ switch (klen) {
+ case 16:
+ ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-128-CBC", propq);
+ ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-128-CTR", propq);
+ break;
+ case 24:
+ ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-192-CBC", propq);
+ ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-192-CTR", propq);
+ break;
+ case 32:
+ ctx->cbc = EVP_CIPHER_fetch(libctx, "AES-256-CBC", propq);
+ ctx->ctr = EVP_CIPHER_fetch(libctx, "AES-256-CTR", propq);
+ break;
+ default:
+ break;
+ }
+ if (ctx->cbc == NULL || ctx->ctr == NULL)
+ return 0;
+ /*
+ * klen is the length of the underlying cipher, not the input key,
+ * which should be twice as long
+ */
+ return ossl_siv128_init(sctx, key, klen, ctx->cbc, ctx->ctr, libctx,
+ propq);
+}
+
+static int aes_siv_dupctx(void *in_vctx, void *out_vctx)
+{
+ PROV_AES_SIV_CTX *in = (PROV_AES_SIV_CTX *)in_vctx;
+ PROV_AES_SIV_CTX *out = (PROV_AES_SIV_CTX *)out_vctx;
+
+ if (in->cbc != NULL && !EVP_CIPHER_up_ref(in->cbc))
+ return 0;
+ if (in->ctr != NULL && !EVP_CIPHER_up_ref(in->ctr)) {
+ EVP_CIPHER_free(in->cbc);
+ return 0;
+ }
+
+ *out = *in;
+ out->siv.cipher_ctx = NULL;
+ out->siv.mac_ctx_init = NULL;
+ out->siv.mac = NULL;
+ if (!ossl_siv128_copy_ctx(&out->siv, &in->siv))
+ return 0;
+
+ return 1;
+}
+
+static int aes_siv_settag(void *vctx, const unsigned char *tag, size_t tagl)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+
+ return ossl_siv128_set_tag(sctx, tag, tagl);
+}
+
+static void aes_siv_setspeed(void *vctx, int speed)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+
+ ossl_siv128_speed(sctx, (int)speed);
+}
+
+static void aes_siv_cleanup(void *vctx)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+
+ ossl_siv128_cleanup(sctx);
+ EVP_CIPHER_free(ctx->cbc);
+ EVP_CIPHER_free(ctx->ctr);
+}
+
+static int aes_siv_cipher(void *vctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_AES_SIV_CTX *ctx = (PROV_AES_SIV_CTX *)vctx;
+ SIV128_CONTEXT *sctx = &ctx->siv;
+
+ /* EncryptFinal or DecryptFinal */
+ if (in == NULL)
+ return ossl_siv128_finish(sctx) == 0;
+
+ /* Deal with associated data */
+ if (out == NULL)
+ return (ossl_siv128_aad(sctx, in, len) == 1);
+
+ if (ctx->enc)
+ return ossl_siv128_encrypt(sctx, in, out, len) > 0;
+
+ return ossl_siv128_decrypt(sctx, in, out, len) > 0;
+}
+
+static const PROV_CIPHER_HW_AES_SIV aes_siv_hw = {
+ aes_siv_initkey,
+ aes_siv_cipher,
+ aes_siv_setspeed,
+ aes_siv_settag,
+ aes_siv_cleanup,
+ aes_siv_dupctx,
+};
+
+const PROV_CIPHER_HW_AES_SIV *ossl_prov_cipher_hw_aes_siv(size_t keybits)
+{
+ return &aes_siv_hw;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c
new file mode 100644
index 000000000000..a3bac9b23d0a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_wrp.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_aes.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+
+/* AES wrap with padding has IV length of 4, without padding 8 */
+#define AES_WRAP_PAD_IVLEN 4
+#define AES_WRAP_NOPAD_IVLEN 8
+
+#define WRAP_FLAGS (PROV_CIPHER_FLAG_CUSTOM_IV)
+#define WRAP_FLAGS_INV (WRAP_FLAGS | PROV_CIPHER_FLAG_INVERSE_CIPHER)
+
+typedef size_t (*aeswrap_fn)(void *key, const unsigned char *iv,
+ unsigned char *out, const unsigned char *in,
+ size_t inlen, block128_f block);
+
+static OSSL_FUNC_cipher_encrypt_init_fn aes_wrap_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn aes_wrap_dinit;
+static OSSL_FUNC_cipher_update_fn aes_wrap_cipher;
+static OSSL_FUNC_cipher_final_fn aes_wrap_final;
+static OSSL_FUNC_cipher_freectx_fn aes_wrap_freectx;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_wrap_set_ctx_params;
+
+typedef struct prov_aes_wrap_ctx_st {
+ PROV_CIPHER_CTX base;
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ks;
+ aeswrap_fn wrapfn;
+
+} PROV_AES_WRAP_CTX;
+
+
+static void *aes_wrap_newctx(size_t kbits, size_t blkbits,
+ size_t ivbits, unsigned int mode, uint64_t flags)
+{
+ PROV_AES_WRAP_CTX *wctx;
+ PROV_CIPHER_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ wctx = OPENSSL_zalloc(sizeof(*wctx));
+ ctx = (PROV_CIPHER_CTX *)wctx;
+ if (ctx != NULL) {
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags,
+ NULL, NULL);
+ ctx->pad = (ctx->ivlen == AES_WRAP_PAD_IVLEN);
+ }
+ return wctx;
+}
+
+static void *aes_wrap_dupctx(void *wctx)
+{
+ PROV_AES_WRAP_CTX *ctx = wctx;
+ PROV_AES_WRAP_CTX *dctx = wctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (ctx == NULL)
+ return NULL;
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+
+ if (dctx != NULL && dctx->base.tlsmac != NULL && dctx->base.alloced) {
+ dctx->base.tlsmac = OPENSSL_memdup(dctx->base.tlsmac,
+ dctx->base.tlsmacsize);
+ if (dctx->base.tlsmac == NULL) {
+ OPENSSL_free(dctx);
+ dctx = NULL;
+ }
+ }
+ return dctx;
+}
+
+static void aes_wrap_freectx(void *vctx)
+{
+ PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(wctx, sizeof(*wctx));
+}
+
+static int aes_wrap_init(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[], int enc)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+ if (ctx->pad)
+ wctx->wrapfn = enc ? CRYPTO_128_wrap_pad : CRYPTO_128_unwrap_pad;
+ else
+ wctx->wrapfn = enc ? CRYPTO_128_wrap : CRYPTO_128_unwrap;
+
+ if (iv != NULL) {
+ if (!ossl_cipher_generic_initiv(ctx, iv, ivlen))
+ return 0;
+ }
+ if (key != NULL) {
+ int use_forward_transform;
+
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ /*
+ * See SP800-38F : Section 5.1
+ * The forward and inverse transformations for the AES block
+ * cipher—called “cipher” and “inverse cipher” are informally known as
+ * the AES encryption and AES decryption functions, respectively.
+ * If the designated cipher function for a key-wrap algorithm is chosen
+ * to be the AES decryption function, then CIPH-1K will be the AES
+ * encryption function.
+ */
+ if (ctx->inverse_cipher == 0)
+ use_forward_transform = ctx->enc;
+ else
+ use_forward_transform = !ctx->enc;
+ if (use_forward_transform) {
+ AES_set_encrypt_key(key, keylen * 8, &wctx->ks.ks);
+ ctx->block = (block128_f)AES_encrypt;
+ } else {
+ AES_set_decrypt_key(key, keylen * 8, &wctx->ks.ks);
+ ctx->block = (block128_f)AES_decrypt;
+ }
+ }
+ return aes_wrap_set_ctx_params(ctx, params);
+}
+
+static int aes_wrap_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return aes_wrap_init(ctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int aes_wrap_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return aes_wrap_init(ctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static int aes_wrap_cipher_internal(void *vctx, unsigned char *out,
+ const unsigned char *in, size_t inlen)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_AES_WRAP_CTX *wctx = (PROV_AES_WRAP_CTX *)vctx;
+ size_t rv;
+ int pad = ctx->pad;
+
+ /* No final operation so always return zero length */
+ if (in == NULL)
+ return 0;
+
+ /* Input length must always be non-zero */
+ if (inlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH);
+ return -1;
+ }
+
+ /* If decrypting need at least 16 bytes and multiple of 8 */
+ if (!ctx->enc && (inlen < 16 || inlen & 0x7)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH);
+ return -1;
+ }
+
+ /* If not padding input must be multiple of 8 */
+ if (!pad && inlen & 0x7) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH);
+ return -1;
+ }
+
+ if (out == NULL) {
+ if (ctx->enc) {
+ /* If padding round up to multiple of 8 */
+ if (pad)
+ inlen = (inlen + 7) / 8 * 8;
+ /* 8 byte prefix */
+ return inlen + 8;
+ } else {
+ /*
+ * If not padding output will be exactly 8 bytes smaller than
+ * input. If padding it will be at least 8 bytes smaller but we
+ * don't know how much.
+ */
+ return inlen - 8;
+ }
+ }
+
+ rv = wctx->wrapfn(&wctx->ks.ks, ctx->iv_set ? ctx->iv : NULL, out, in,
+ inlen, ctx->block);
+ if (!rv) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return -1;
+ }
+ if (rv > INT_MAX) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
+ return -1;
+ }
+ return (int)rv;
+}
+
+static int aes_wrap_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+static int aes_wrap_cipher(void *vctx,
+ unsigned char *out, size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_AES_WRAP_CTX *ctx = (PROV_AES_WRAP_CTX *)vctx;
+ size_t len;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (inl == 0) {
+ *outl = 0;
+ return 1;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ len = aes_wrap_cipher_internal(ctx, out, in, inl);
+ if (len <= 0)
+ return 0;
+
+ *outl = len;
+ return 1;
+}
+
+static int aes_wrap_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t keylen = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->keylen != keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define IMPLEMENT_cipher(mode, fname, UCMODE, flags, kbits, blkbits, ivbits) \
+ static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##fname##_get_params; \
+ static int aes_##kbits##_##fname##_get_params(OSSL_PARAM params[]) \
+ { \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE,\
+ flags, kbits, blkbits, ivbits); \
+ } \
+ static OSSL_FUNC_cipher_newctx_fn aes_##kbits##fname##_newctx; \
+ static void *aes_##kbits##fname##_newctx(void *provctx) \
+ { \
+ return aes_##mode##_newctx(kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags); \
+ } \
+ const OSSL_DISPATCH ossl_##aes##kbits##fname##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void))aes_##kbits##fname##_newctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_##mode##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_##mode##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_##mode##_cipher }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_##mode##_final }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_##mode##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_##mode##_dupctx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))aes_##kbits##_##fname##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))aes_wrap_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 256, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 192, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrap, WRAP, WRAP_FLAGS, 128, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 256, 64, AES_WRAP_PAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 192, 64, AES_WRAP_PAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappad, WRAP, WRAP_FLAGS, 128, 64, AES_WRAP_PAD_IVLEN * 8);
+
+IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 256, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 192, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrapinv, WRAP, WRAP_FLAGS_INV, 128, 64, AES_WRAP_NOPAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 256, 64, AES_WRAP_PAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 192, 64, AES_WRAP_PAD_IVLEN * 8);
+IMPLEMENT_cipher(wrap, wrappadinv, WRAP, WRAP_FLAGS_INV, 128, 64, AES_WRAP_PAD_IVLEN * 8);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c
new file mode 100644
index 000000000000..1e0081da94ec
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.c
@@ -0,0 +1,318 @@
+
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_aes_xts.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define AES_XTS_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV
+#define AES_XTS_IV_BITS 128
+#define AES_XTS_BLOCK_BITS 8
+
+/* forward declarations */
+static OSSL_FUNC_cipher_encrypt_init_fn aes_xts_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn aes_xts_dinit;
+static OSSL_FUNC_cipher_update_fn aes_xts_stream_update;
+static OSSL_FUNC_cipher_final_fn aes_xts_stream_final;
+static OSSL_FUNC_cipher_cipher_fn aes_xts_cipher;
+static OSSL_FUNC_cipher_freectx_fn aes_xts_freectx;
+static OSSL_FUNC_cipher_dupctx_fn aes_xts_dupctx;
+static OSSL_FUNC_cipher_set_ctx_params_fn aes_xts_set_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn aes_xts_settable_ctx_params;
+
+/*
+ * Verify that the two keys are different.
+ *
+ * This addresses the vulnerability described in Rogaway's
+ * September 2004 paper:
+ *
+ * "Efficient Instantiations of Tweakable Blockciphers and
+ * Refinements to Modes OCB and PMAC".
+ * (http://web.cs.ucdavis.edu/~rogaway/papers/offsets.pdf)
+ *
+ * FIPS 140-2 IG A.9 XTS-AES Key Generation Requirements states
+ * that:
+ * "The check for Key_1 != Key_2 shall be done at any place
+ * BEFORE using the keys in the XTS-AES algorithm to process
+ * data with them."
+ */
+static int aes_xts_check_keys_differ(const unsigned char *key, size_t bytes,
+ int enc)
+{
+ if ((!ossl_aes_xts_allow_insecure_decrypt || enc)
+ && CRYPTO_memcmp(key, key + bytes, bytes) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DUPLICATED_KEYS);
+ return 0;
+ }
+ return 1;
+}
+
+#ifdef AES_XTS_S390X
+# include "cipher_aes_xts_s390x.inc"
+#endif
+
+/*-
+ * Provider dispatch functions
+ */
+static int aes_xts_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
+ PROV_CIPHER_CTX *ctx = &xctx->base;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (!ossl_cipher_generic_initiv(vctx, iv, ivlen))
+ return 0;
+ }
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!aes_xts_check_keys_differ(key, keylen / 2, enc))
+ return 0;
+ if (!ctx->hw->init(ctx, key, keylen))
+ return 0;
+ }
+ return aes_xts_set_ctx_params(ctx, params);
+}
+
+static int aes_xts_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+#ifdef AES_XTS_S390X
+ if (s390x_aes_xts_einit(vctx, key, keylen, iv, ivlen, params) == 1)
+ return 1;
+#endif
+ return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int aes_xts_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+#ifdef AES_XTS_S390X
+ if (s390x_aes_xts_dinit(vctx, key, keylen, iv, ivlen, params) == 1)
+ return 1;
+#endif
+ return aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static void *aes_xts_newctx(void *provctx, unsigned int mode, uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits)
+{
+ PROV_AES_XTS_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits, mode,
+ flags, ossl_prov_cipher_hw_aes_xts(kbits),
+ NULL);
+ }
+ return ctx;
+}
+
+static void aes_xts_freectx(void *vctx)
+{
+ PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *aes_xts_dupctx(void *vctx)
+{
+ PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx;
+ PROV_AES_XTS_CTX *ret = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+#ifdef AES_XTS_S390X
+ if (in->plat.s390x.fc)
+ return s390x_aes_xts_dupctx(vctx);
+#endif
+
+ if (in->xts.key1 != NULL) {
+ if (in->xts.key1 != &in->ks1)
+ return NULL;
+ }
+ if (in->xts.key2 != NULL) {
+ if (in->xts.key2 != &in->ks2)
+ return NULL;
+ }
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+ return ret;
+}
+
+static int aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx;
+
+#ifdef AES_XTS_S390X
+ if (ctx->plat.s390x.fc)
+ return s390x_aes_xts_cipher(vctx, out, outl, outsize, in, inl);
+#endif
+
+ if (!ossl_prov_is_running()
+ || ctx->xts.key1 == NULL
+ || ctx->xts.key2 == NULL
+ || !ctx->base.iv_set
+ || out == NULL
+ || in == NULL
+ || inl < AES_BLOCK_SIZE)
+ return 0;
+
+ /*
+ * Impose a limit of 2^20 blocks per data unit as specified by
+ * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007
+ * indicated that this was a SHOULD NOT rather than a MUST NOT.
+ * NIST SP 800-38E mandates the same limit.
+ */
+ if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE);
+ return 0;
+ }
+
+ if (ctx->stream != NULL)
+ (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2, ctx->base.iv);
+ else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl,
+ ctx->base.enc))
+ return 0;
+
+ *outl = inl;
+ return 1;
+}
+
+static int aes_xts_stream_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_AES_XTS_CTX *ctx = (PROV_AES_XTS_CTX *)vctx;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!aes_xts_cipher(ctx, out, outl, outsize, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int aes_xts_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ *outl = 0;
+ return 1;
+}
+
+static const OSSL_PARAM aes_xts_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *aes_xts_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return aes_xts_known_settable_ctx_params;
+}
+
+static int aes_xts_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t keylen;
+
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* The key length can not be modified for xts mode */
+ if (keylen != ctx->keylen)
+ return 0;
+ }
+
+ return 1;
+}
+
+#define IMPLEMENT_cipher(lcmode, UCMODE, kbits, flags) \
+static OSSL_FUNC_cipher_get_params_fn aes_##kbits##_##lcmode##_get_params; \
+static int aes_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, 2 * kbits, AES_XTS_BLOCK_BITS, \
+ AES_XTS_IV_BITS); \
+} \
+static OSSL_FUNC_cipher_newctx_fn aes_##kbits##_xts_newctx; \
+static void *aes_##kbits##_xts_newctx(void *provctx) \
+{ \
+ return aes_xts_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, flags, 2 * kbits, \
+ AES_XTS_BLOCK_BITS, AES_XTS_IV_BITS); \
+} \
+const OSSL_DISPATCH ossl_aes##kbits##xts_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))aes_##kbits##_xts_newctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))aes_xts_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))aes_xts_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))aes_xts_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))aes_xts_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))aes_xts_cipher }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))aes_xts_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))aes_xts_dupctx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))aes_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))aes_xts_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))aes_xts_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+IMPLEMENT_cipher(xts, XTS, 256, AES_XTS_FLAGS);
+IMPLEMENT_cipher(xts, XTS, 128, AES_XTS_FLAGS);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h
new file mode 100644
index 000000000000..56891ca98c42
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/aes.h>
+#include "prov/ciphercommon.h"
+#include "crypto/aes_platform.h"
+
+/*
+ * Available in cipher_fips.c, and compiled with different values depending
+ * on we're in the FIPS module or not.
+ */
+extern const int ossl_aes_xts_allow_insecure_decrypt;
+
+PROV_CIPHER_FUNC(void, xts_stream,
+ (const unsigned char *in, unsigned char *out, size_t len,
+ const AES_KEY *key1, const AES_KEY *key2,
+ const unsigned char iv[16]));
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+typedef struct S390X_km_xts_params_st {
+ unsigned char key[64];
+ unsigned char tweak[16];
+ unsigned char nap[16];
+} S390X_KM_XTS_PARAMS;
+#endif
+
+typedef struct prov_aes_xts_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ AES_KEY ks;
+ } ks1, ks2; /* AES key schedules to use */
+ XTS128_CONTEXT xts;
+ OSSL_xts_stream_fn stream;
+
+ /* Platform specific data */
+ union {
+ int dummy;
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+ struct {
+ union {
+ OSSL_UNION_ALIGN;
+ S390X_KM_XTS_PARAMS km;
+ } param;
+ size_t offset;
+ unsigned int fc;
+ unsigned int iv_set : 1;
+ unsigned int key_set : 1;
+ } s390x;
+#endif
+ } plat;
+} PROV_AES_XTS_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c
new file mode 100644
index 000000000000..777d928bf96f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_fips.c
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * AES low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_xts.h"
+
+#ifdef FIPS_MODULE
+const int ossl_aes_xts_allow_insecure_decrypt = 0;
+#else
+const int ossl_aes_xts_allow_insecure_decrypt = 1;
+#endif /* FIPS_MODULE */
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c
new file mode 100644
index 000000000000..b6353f18456b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_hw.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file uses the low level AES functions (which are deprecated for
+ * non-internal use) in order to implement provider AES ciphers.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_aes_xts.h"
+
+#define XTS_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \
+ fn_block_enc, fn_block_dec, \
+ fn_stream_enc, fn_stream_dec) { \
+ size_t bytes = keylen / 2; \
+ size_t bits = bytes * 8; \
+ \
+ if (ctx->enc) { \
+ fn_set_enc_key(key, bits, &xctx->ks1.ks); \
+ xctx->xts.block1 = (block128_f)fn_block_enc; \
+ } else { \
+ fn_set_dec_key(key, bits, &xctx->ks1.ks); \
+ xctx->xts.block1 = (block128_f)fn_block_dec; \
+ } \
+ fn_set_enc_key(key + bytes, bits, &xctx->ks2.ks); \
+ xctx->xts.block2 = (block128_f)fn_block_enc; \
+ xctx->xts.key1 = &xctx->ks1; \
+ xctx->xts.key2 = &xctx->ks2; \
+ xctx->stream = ctx->enc ? fn_stream_enc : fn_stream_dec; \
+}
+
+static int cipher_hw_aes_xts_generic_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_enc = NULL;
+ OSSL_xts_stream_fn stream_dec = NULL;
+
+#ifdef AES_XTS_ASM
+ stream_enc = AES_xts_encrypt;
+ stream_dec = AES_xts_decrypt;
+#endif /* AES_XTS_ASM */
+
+#ifdef HWAES_CAPABLE
+ if (HWAES_CAPABLE) {
+# ifdef HWAES_xts_encrypt
+ stream_enc = HWAES_xts_encrypt;
+# endif /* HWAES_xts_encrypt */
+# ifdef HWAES_xts_decrypt
+ stream_dec = HWAES_xts_decrypt;
+# endif /* HWAES_xts_decrypt */
+ XTS_SET_KEY_FN(HWAES_set_encrypt_key, HWAES_set_decrypt_key,
+ HWAES_encrypt, HWAES_decrypt,
+ stream_enc, stream_dec);
+ return 1;
+ } else
+#endif /* HWAES_CAPABLE */
+
+#ifdef BSAES_CAPABLE
+ if (BSAES_CAPABLE) {
+ stream_enc = ossl_bsaes_xts_encrypt;
+ stream_dec = ossl_bsaes_xts_decrypt;
+ } else
+#endif /* BSAES_CAPABLE */
+#ifdef VPAES_CAPABLE
+ if (VPAES_CAPABLE) {
+ XTS_SET_KEY_FN(vpaes_set_encrypt_key, vpaes_set_decrypt_key,
+ vpaes_encrypt, vpaes_decrypt, stream_enc, stream_dec);
+ return 1;
+ } else
+#endif /* VPAES_CAPABLE */
+ {
+ (void)0;
+ }
+ {
+ XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_decrypt_key,
+ AES_encrypt, AES_decrypt, stream_enc, stream_dec);
+ }
+ return 1;
+}
+
+static void cipher_hw_aes_xts_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src)
+{
+ PROV_AES_XTS_CTX *sctx = (PROV_AES_XTS_CTX *)src;
+ PROV_AES_XTS_CTX *dctx = (PROV_AES_XTS_CTX *)dst;
+
+ *dctx = *sctx;
+ dctx->xts.key1 = &dctx->ks1.ks;
+ dctx->xts.key2 = &dctx->ks2.ks;
+}
+
+#if defined(AESNI_CAPABLE)
+
+static int cipher_hw_aesni_xts_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+
+ void (*aesni_xts_enc)(const unsigned char *in,
+ unsigned char *out,
+ size_t length,
+ const AES_KEY *key1, const AES_KEY *key2,
+ const unsigned char iv[16]);
+ void (*aesni_xts_dec)(const unsigned char *in,
+ unsigned char *out,
+ size_t length,
+ const AES_KEY *key1, const AES_KEY *key2,
+ const unsigned char iv[16]);
+
+ aesni_xts_enc = aesni_xts_encrypt;
+ aesni_xts_dec = aesni_xts_decrypt;
+
+# if (defined(__x86_64) || defined(__x86_64__) || \
+ defined(_M_AMD64) || defined(_M_X64))
+ if (aesni_xts_avx512_eligible()) {
+ if (keylen == 64) {
+ aesni_xts_enc = aesni_xts_256_encrypt_avx512;
+ aesni_xts_dec = aesni_xts_256_decrypt_avx512;
+ } else if (keylen == 32) {
+ aesni_xts_enc = aesni_xts_128_encrypt_avx512;
+ aesni_xts_dec = aesni_xts_128_decrypt_avx512;
+ }
+ }
+# endif
+
+ XTS_SET_KEY_FN(aesni_set_encrypt_key, aesni_set_decrypt_key,
+ aesni_encrypt, aesni_decrypt,
+ aesni_xts_enc, aesni_xts_dec);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare_xts() \
+static const PROV_CIPHER_HW aesni_xts = { \
+ cipher_hw_aesni_xts_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+};
+# define PROV_CIPHER_HW_select_xts() \
+if (AESNI_CAPABLE) \
+ return &aesni_xts;
+
+# elif defined(SPARC_AES_CAPABLE)
+
+static int cipher_hw_aes_xts_t4_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_enc = NULL;
+ OSSL_xts_stream_fn stream_dec = NULL;
+
+ /* Note: keylen is the size of 2 keys */
+ switch (keylen) {
+ case 32:
+ stream_enc = aes128_t4_xts_encrypt;
+ stream_dec = aes128_t4_xts_decrypt;
+ break;
+ case 64:
+ stream_enc = aes256_t4_xts_encrypt;
+ stream_dec = aes256_t4_xts_decrypt;
+ break;
+ default:
+ return 0;
+ }
+
+ XTS_SET_KEY_FN(aes_t4_set_encrypt_key, aes_t4_set_decrypt_key,
+ aes_t4_encrypt, aes_t4_decrypt,
+ stream_enc, stream_dec);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare_xts() \
+static const PROV_CIPHER_HW aes_xts_t4 = { \
+ cipher_hw_aes_xts_t4_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+};
+# define PROV_CIPHER_HW_select_xts() \
+if (SPARC_AES_CAPABLE) \
+ return &aes_xts_t4;
+
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+
+static int cipher_hw_aes_xts_rv64i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_enc = NULL;
+ OSSL_xts_stream_fn stream_dec = NULL;
+
+ XTS_SET_KEY_FN(rv64i_zkne_set_encrypt_key, rv64i_zknd_set_decrypt_key,
+ rv64i_zkne_encrypt, rv64i_zknd_decrypt,
+ stream_enc, stream_dec);
+ return 1;
+}
+
+static int cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey(
+ PROV_CIPHER_CTX *ctx, const unsigned char *key, size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_enc = NULL;
+ OSSL_xts_stream_fn stream_dec = NULL;
+
+ /* Zvkned only supports 128 and 256 bit keys. */
+ if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) {
+ XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key,
+ rv64i_zvkned_set_decrypt_key, rv64i_zvkned_encrypt,
+ rv64i_zvkned_decrypt,
+ rv64i_zvbb_zvkg_zvkned_aes_xts_encrypt,
+ rv64i_zvbb_zvkg_zvkned_aes_xts_decrypt);
+ } else {
+ XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key,
+ rv64i_zvkned_encrypt, rv64i_zvkned_decrypt,
+ stream_enc, stream_dec);
+ }
+ return 1;
+}
+
+static int cipher_hw_aes_xts_rv64i_zvkned_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_enc = NULL;
+ OSSL_xts_stream_fn stream_dec = NULL;
+
+ /* Zvkned only supports 128 and 256 bit keys. */
+ if (keylen * 8 == 128 * 2 || keylen * 8 == 256 * 2) {
+ XTS_SET_KEY_FN(rv64i_zvkned_set_encrypt_key,
+ rv64i_zvkned_set_decrypt_key,
+ rv64i_zvkned_encrypt, rv64i_zvkned_decrypt,
+ stream_enc, stream_dec);
+ } else {
+ XTS_SET_KEY_FN(AES_set_encrypt_key, AES_set_encrypt_key,
+ rv64i_zvkned_encrypt, rv64i_zvkned_decrypt,
+ stream_enc, stream_dec);
+ }
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare_xts() \
+static const PROV_CIPHER_HW aes_xts_rv64i_zknd_zkne = { \
+ cipher_hw_aes_xts_rv64i_zknd_zkne_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+}; \
+static const PROV_CIPHER_HW aes_xts_rv64i_zvkned = { \
+ cipher_hw_aes_xts_rv64i_zvkned_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+}; \
+static const PROV_CIPHER_HW aes_xts_rv64i_zvbb_zvkg_zvkned = { \
+ cipher_hw_aes_xts_rv64i_zvbb_zvkg_zvkned_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+};
+
+# define PROV_CIPHER_HW_select_xts() \
+if (RISCV_HAS_ZVBB() && RISCV_HAS_ZVKG() && RISCV_HAS_ZVKNED() && \
+ riscv_vlen() >= 128) \
+ return &aes_xts_rv64i_zvbb_zvkg_zvkned; \
+if (RISCV_HAS_ZVKNED() && riscv_vlen() >= 128) \
+ return &aes_xts_rv64i_zvkned; \
+else if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &aes_xts_rv64i_zknd_zkne;
+
+#elif defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 32
+
+static int cipher_hw_aes_xts_rv32i_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+
+ XTS_SET_KEY_FN(rv32i_zkne_set_encrypt_key, rv32i_zknd_zkne_set_decrypt_key,
+ rv32i_zkne_encrypt, rv32i_zknd_decrypt,
+ NULL, NULL);
+ return 1;
+}
+
+static int cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)ctx;
+
+ XTS_SET_KEY_FN(rv32i_zbkb_zkne_set_encrypt_key, rv32i_zbkb_zknd_zkne_set_decrypt_key,
+ rv32i_zkne_encrypt, rv32i_zknd_decrypt,
+ NULL, NULL);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_declare_xts() \
+static const PROV_CIPHER_HW aes_xts_rv32i_zknd_zkne = { \
+ cipher_hw_aes_xts_rv32i_zknd_zkne_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+}; \
+static const PROV_CIPHER_HW aes_xts_rv32i_zbkb_zknd_zkne = { \
+ cipher_hw_aes_xts_rv32i_zbkb_zknd_zkne_initkey, \
+ NULL, \
+ cipher_hw_aes_xts_copyctx \
+};
+# define PROV_CIPHER_HW_select_xts() \
+if (RISCV_HAS_ZBKB_AND_ZKND_AND_ZKNE()) \
+ return &aes_xts_rv32i_zbkb_zknd_zkne; \
+if (RISCV_HAS_ZKND_AND_ZKNE()) \
+ return &aes_xts_rv32i_zknd_zkne;
+# else
+/* The generic case */
+# define PROV_CIPHER_HW_declare_xts()
+# define PROV_CIPHER_HW_select_xts()
+#endif
+
+static const PROV_CIPHER_HW aes_generic_xts = {
+ cipher_hw_aes_xts_generic_initkey,
+ NULL,
+ cipher_hw_aes_xts_copyctx
+};
+PROV_CIPHER_HW_declare_xts()
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aes_xts(size_t keybits)
+{
+ PROV_CIPHER_HW_select_xts()
+ return &aes_generic_xts;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc
new file mode 100644
index 000000000000..77341b3bbd4d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aes_xts_s390x.inc
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/s390x_arch.h"
+
+static OSSL_FUNC_cipher_encrypt_init_fn s390x_aes_xts_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn s390x_aes_xts_dinit;
+static OSSL_FUNC_cipher_cipher_fn s390x_aes_xts_cipher;
+static OSSL_FUNC_cipher_dupctx_fn s390x_aes_xts_dupctx;
+
+static int s390x_aes_xts_init(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[],
+ unsigned int dec)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
+ S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
+ unsigned int fc, offs;
+
+ switch (xctx->base.keylen) {
+ case 128 / 8 * 2:
+ fc = S390X_XTS_AES_128_MSA10;
+ offs = 32;
+ break;
+ case 256 / 8 * 2:
+ fc = S390X_XTS_AES_256_MSA10;
+ offs = 0;
+ break;
+ default:
+ goto not_supported;
+ }
+
+ if (!(OPENSSL_s390xcap_P.km[1] && S390X_CAPBIT(fc)))
+ goto not_supported;
+
+ if (iv != NULL) {
+ if (ivlen != xctx->base.ivlen
+ || ivlen > sizeof(km->tweak)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ memcpy(km->tweak, iv, ivlen);
+ xctx->plat.s390x.iv_set = 1;
+ }
+
+ if (key != NULL) {
+ if (keylen != xctx->base.keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!aes_xts_check_keys_differ(key, keylen / 2, !dec))
+ return 0;
+
+ memcpy(km->key + offs, key, keylen);
+ xctx->plat.s390x.key_set = 1;
+ }
+
+ xctx->plat.s390x.fc = fc | dec;
+ xctx->plat.s390x.offset = offs;
+
+ memset(km->nap, 0, sizeof(km->nap));
+ km->nap[0] = 0x1;
+
+ return aes_xts_set_ctx_params(xctx, params);
+
+not_supported:
+ xctx->plat.s390x.fc = 0;
+ xctx->plat.s390x.offset = 0;
+ return 0;
+}
+
+static int s390x_aes_xts_einit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static int s390x_aes_xts_dinit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ return s390x_aes_xts_init(vctx, key, keylen, iv, ivlen, params,
+ S390X_DECRYPT);
+}
+
+static void *s390x_aes_xts_dupctx(void *vctx)
+{
+ PROV_AES_XTS_CTX *in = (PROV_AES_XTS_CTX *)vctx;
+ PROV_AES_XTS_CTX *ret = OPENSSL_zalloc(sizeof(*in));
+
+ if (ret != NULL)
+ *ret = *in;
+
+ return ret;
+}
+
+static int s390x_aes_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_AES_XTS_CTX *xctx = (PROV_AES_XTS_CTX *)vctx;
+ S390X_KM_XTS_PARAMS *km = &xctx->plat.s390x.param.km;
+ unsigned char *param = (unsigned char *)km + xctx->plat.s390x.offset;
+ unsigned int fc = xctx->plat.s390x.fc;
+ unsigned char tmp[2][AES_BLOCK_SIZE];
+ unsigned char nap_n1[AES_BLOCK_SIZE];
+ unsigned char drop[AES_BLOCK_SIZE];
+ size_t len_incomplete, len_complete;
+
+ if (!ossl_prov_is_running()
+ || inl < AES_BLOCK_SIZE
+ || in == NULL
+ || out == NULL
+ || !xctx->plat.s390x.iv_set
+ || !xctx->plat.s390x.key_set)
+ return 0;
+
+ /*
+ * Impose a limit of 2^20 blocks per data unit as specified by
+ * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007
+ * indicated that this was a SHOULD NOT rather than a MUST NOT.
+ * NIST SP 800-38E mandates the same limit.
+ */
+ if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * AES_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE);
+ return 0;
+ }
+
+ len_incomplete = inl % AES_BLOCK_SIZE;
+ len_complete = (len_incomplete == 0) ? inl :
+ (inl / AES_BLOCK_SIZE - 1) * AES_BLOCK_SIZE;
+
+ if (len_complete > 0)
+ s390x_km(in, len_complete, out, fc, param);
+ if (len_incomplete == 0)
+ goto out;
+
+ memcpy(tmp, in + len_complete, AES_BLOCK_SIZE + len_incomplete);
+ /* swap NAP for decrypt */
+ if (fc & S390X_DECRYPT) {
+ memcpy(nap_n1, km->nap, AES_BLOCK_SIZE);
+ s390x_km(tmp[0], AES_BLOCK_SIZE, drop, fc, param);
+ }
+ s390x_km(tmp[0], AES_BLOCK_SIZE, tmp[0], fc, param);
+ if (fc & S390X_DECRYPT)
+ memcpy(km->nap, nap_n1, AES_BLOCK_SIZE);
+
+ memcpy(tmp[1] + len_incomplete, tmp[0] + len_incomplete,
+ AES_BLOCK_SIZE - len_incomplete);
+ s390x_km(tmp[1], AES_BLOCK_SIZE, out + len_complete, fc, param);
+ memcpy(out + len_complete + AES_BLOCK_SIZE, tmp[0], len_incomplete);
+
+ /* do not expose temporary data */
+ OPENSSL_cleanse(tmp, sizeof(tmp));
+out:
+ memcpy(xctx->base.iv, km->tweak, AES_BLOCK_SIZE);
+ *outl = inl;
+
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria.c
new file mode 100644
index 000000000000..ce4938d44a9a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for ARIA cipher modes ecb, cbc, ofb, cfb, ctr */
+
+#include "cipher_aria.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn aria_freectx;
+static OSSL_FUNC_cipher_dupctx_fn aria_dupctx;
+
+static void aria_freectx(void *vctx)
+{
+ PROV_ARIA_CTX *ctx = (PROV_ARIA_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *aria_dupctx(void *ctx)
+{
+ PROV_ARIA_CTX *in = (PROV_ARIA_CTX *)ctx;
+ PROV_ARIA_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+/* ossl_aria256ecb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 256, 128, 0, block)
+/* ossl_aria192ecb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 192, 128, 0, block)
+/* ossl_aria128ecb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ecb, ECB, 0, 128, 128, 0, block)
+/* ossl_aria256cbc_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 256, 128, 128, block)
+/* ossl_aria192cbc_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 192, 128, 128, block)
+/* ossl_aria128cbc_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cbc, CBC, 0, 128, 128, 128, block)
+/* ossl_aria256ofb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 256, 8, 128, stream)
+/* ossl_aria192ofb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 192, 8, 128, stream)
+/* ossl_aria128ofb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ofb, OFB, 0, 128, 8, 128, stream)
+/* ossl_aria256cfb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 256, 8, 128, stream)
+/* ossl_aria192cfb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 192, 8, 128, stream)
+/* ossl_aria128cfb_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb, CFB, 0, 128, 8, 128, stream)
+/* ossl_aria256cfb1_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 256, 8, 128, stream)
+/* ossl_aria192cfb1_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 192, 8, 128, stream)
+/* ossl_aria128cfb1_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb1, CFB, 0, 128, 8, 128, stream)
+/* ossl_aria256cfb8_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 256, 8, 128, stream)
+/* ossl_aria192cfb8_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 192, 8, 128, stream)
+/* ossl_aria128cfb8_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, cfb8, CFB, 0, 128, 8, 128, stream)
+/* ossl_aria256ctr_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 256, 8, 128, stream)
+/* ossl_aria192ctr_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 192, 8, 128, stream)
+/* ossl_aria128ctr_functions */
+IMPLEMENT_generic_cipher(aria, ARIA, ctr, CTR, 0, 128, 8, 128, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria.h
new file mode 100644
index 000000000000..39f84d3b4321
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/aria.h"
+#include "prov/ciphercommon.h"
+
+typedef struct prov_aria_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ ARIA_KEY ks;
+ } ks;
+} PROV_ARIA_CTX;
+
+
+#define ossl_prov_cipher_hw_aria_ofb ossl_prov_cipher_hw_aria_ofb128
+#define ossl_prov_cipher_hw_aria_cfb ossl_prov_cipher_hw_aria_cfb128
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ofb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb1(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_cfb8(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_ctr(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c
new file mode 100644
index 000000000000..0a0f52cdcc1b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for ARIA CCM mode */
+
+#include "cipher_aria_ccm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn aria_ccm_freectx;
+
+static void *aria_ccm_newctx(void *provctx, size_t keybits)
+{
+ PROV_ARIA_CCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_aria_hw_ccm(keybits));
+ return ctx;
+}
+
+static void *aria_ccm_dupctx(void *provctx)
+{
+ PROV_ARIA_CCM_CTX *ctx = provctx;
+ PROV_ARIA_CCM_CTX *dctx = NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.ccm_ctx.key != NULL)
+ dctx->base.ccm_ctx.key = &dctx->ks.ks;
+
+ return dctx;
+}
+
+static void aria_ccm_freectx(void *vctx)
+{
+ PROV_ARIA_CCM_CTX *ctx = (PROV_ARIA_CCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* aria128ccm functions */
+IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 128, 8, 96);
+/* aria192ccm functions */
+IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 192, 8, 96);
+/* aria256ccm functions */
+IMPLEMENT_aead_cipher(aria, ccm, CCM, AEAD_FLAGS, 256, 8, 96);
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h
new file mode 100644
index 000000000000..558da4973fa2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/aria.h"
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_ccm.h"
+
+typedef struct prov_aria_ccm_ctx_st {
+ PROV_CCM_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ ARIA_KEY ks;
+ } ks; /* ARIA key schedule to use */
+} PROV_ARIA_CCM_CTX;
+
+const PROV_CCM_HW *ossl_prov_aria_hw_ccm(size_t keylen);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c
new file mode 100644
index 000000000000..e56ec8fb0865
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_ccm_hw.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Generic support for ARIA CCM.
+ */
+
+#include "cipher_aria_ccm.h"
+
+static int ccm_aria_initkey(PROV_CCM_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_ARIA_CCM_CTX *actx = (PROV_ARIA_CCM_CTX *)ctx;
+
+ ossl_aria_set_encrypt_key(key, keylen * 8, &actx->ks.ks);
+ CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ks.ks,
+ (block128_f)ossl_aria_encrypt);
+ ctx->str = NULL;
+ ctx->key_set = 1;
+ return 1;
+}
+
+static const PROV_CCM_HW ccm_aria = {
+ ccm_aria_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+const PROV_CCM_HW *ossl_prov_aria_hw_ccm(size_t keybits)
+{
+ return &ccm_aria;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c
new file mode 100644
index 000000000000..e794a80a00a4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for ARIA GCM mode */
+
+#include "cipher_aria_gcm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static void *aria_gcm_newctx(void *provctx, size_t keybits)
+{
+ PROV_ARIA_GCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_gcm_initctx(provctx, &ctx->base, keybits,
+ ossl_prov_aria_hw_gcm(keybits));
+ return ctx;
+}
+
+static void *aria_gcm_dupctx(void *provctx)
+{
+ PROV_ARIA_GCM_CTX *ctx = provctx;
+ PROV_ARIA_GCM_CTX *dctx = NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.gcm.key != NULL)
+ dctx->base.gcm.key = &dctx->ks.ks;
+
+ return dctx;
+}
+
+static OSSL_FUNC_cipher_freectx_fn aria_gcm_freectx;
+static void aria_gcm_freectx(void *vctx)
+{
+ PROV_ARIA_GCM_CTX *ctx = (PROV_ARIA_GCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* ossl_aria128gcm_functions */
+IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 128, 8, 96);
+/* ossl_aria192gcm_functions */
+IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 192, 8, 96);
+/* ossl_aria256gcm_functions */
+IMPLEMENT_aead_cipher(aria, gcm, GCM, AEAD_FLAGS, 256, 8, 96);
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h
new file mode 100644
index 000000000000..6251e8322f36
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/aria.h"
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_gcm.h"
+
+typedef struct prov_aria_gcm_ctx_st {
+ PROV_GCM_CTX base; /* must be first entry in struct */
+ union {
+ OSSL_UNION_ALIGN;
+ ARIA_KEY ks;
+ } ks;
+} PROV_ARIA_GCM_CTX;
+
+const PROV_GCM_HW *ossl_prov_aria_hw_gcm(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c
new file mode 100644
index 000000000000..927327c29c77
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_gcm_hw.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Generic support for ARIA GCM.
+ */
+
+#include "cipher_aria_gcm.h"
+
+static int aria_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_ARIA_GCM_CTX *actx = (PROV_ARIA_GCM_CTX *)ctx;
+ ARIA_KEY *ks = &actx->ks.ks;
+
+ GCM_HW_SET_KEY_CTR_FN(ks, ossl_aria_set_encrypt_key, ossl_aria_encrypt, NULL);
+ return 1;
+}
+
+static const PROV_GCM_HW aria_gcm = {
+ aria_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ ossl_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+const PROV_GCM_HW *ossl_prov_aria_hw_gcm(size_t keybits)
+{
+ return &aria_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c
new file mode 100644
index 000000000000..425d87a65eca
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_aria_hw.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/proverr.h>
+#include "cipher_aria.h"
+
+static int cipher_hw_aria_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret, mode = dat->mode;
+ PROV_ARIA_CTX *adat = (PROV_ARIA_CTX *)dat;
+ ARIA_KEY *ks = &adat->ks.ks;
+
+ if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE))
+ ret = ossl_aria_set_encrypt_key(key, keylen * 8, ks);
+ else
+ ret = ossl_aria_set_decrypt_key(key, keylen * 8, ks);
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+ dat->ks = ks;
+ dat->block = (block128_f)ossl_aria_encrypt;
+ return 1;
+}
+
+IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_aria_copyctx, PROV_ARIA_CTX)
+
+# define PROV_CIPHER_HW_aria_mode(mode) \
+static const PROV_CIPHER_HW aria_##mode = { \
+ cipher_hw_aria_initkey, \
+ ossl_cipher_hw_chunked_##mode, \
+ cipher_hw_aria_copyctx \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_aria_##mode(size_t keybits) \
+{ \
+ return &aria_##mode; \
+}
+
+PROV_CIPHER_HW_aria_mode(cbc)
+PROV_CIPHER_HW_aria_mode(ecb)
+PROV_CIPHER_HW_aria_mode(ofb128)
+PROV_CIPHER_HW_aria_mode(cfb128)
+PROV_CIPHER_HW_aria_mode(cfb1)
+PROV_CIPHER_HW_aria_mode(cfb8)
+PROV_CIPHER_HW_aria_mode(ctr)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c
new file mode 100644
index 000000000000..9f17f1200dfd
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for Blowfish cipher modes ecb, cbc, ofb, cfb */
+
+/*
+ * BF low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_blowfish.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define BF_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH
+
+static OSSL_FUNC_cipher_freectx_fn blowfish_freectx;
+static OSSL_FUNC_cipher_dupctx_fn blowfish_dupctx;
+
+static void blowfish_freectx(void *vctx)
+{
+ PROV_BLOWFISH_CTX *ctx = (PROV_BLOWFISH_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *blowfish_dupctx(void *ctx)
+{
+ PROV_BLOWFISH_CTX *in = (PROV_BLOWFISH_CTX *)ctx;
+ PROV_BLOWFISH_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+/* bf_ecb_functions */
+IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, ecb, ECB, BF_FLAGS, 128, 64, 0, block)
+/* bf_cbc_functions */
+IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, cbc, CBC, BF_FLAGS, 128, 64, 64, block)
+/* bf_ofb_functions */
+IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, ofb64, OFB, BF_FLAGS, 128, 8, 64, stream)
+/* bf_cfb_functions */
+IMPLEMENT_var_keylen_cipher(blowfish, BLOWFISH, cfb64, CFB, BF_FLAGS, 128, 8, 64, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h
new file mode 100644
index 000000000000..bbdc9da3789d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/blowfish.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_blowfish_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ BF_KEY ks;
+ } ks;
+} PROV_BLOWFISH_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_ofb64(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_cfb64(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c
new file mode 100644
index 000000000000..4855a71f6871
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_blowfish_hw.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * BF low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_blowfish.h"
+
+static int cipher_hw_blowfish_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_BLOWFISH_CTX *bctx = (PROV_BLOWFISH_CTX *)ctx;
+
+ BF_set_key(&bctx->ks.ks, keylen, key);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_blowfish_mode(mode, UCMODE) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, blowfish, PROV_BLOWFISH_CTX, BF_KEY, \
+ BF_##mode) \
+static const PROV_CIPHER_HW bf_##mode = { \
+ cipher_hw_blowfish_initkey, \
+ cipher_hw_blowfish_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_blowfish_##mode(size_t keybits) \
+{ \
+ return &bf_##mode; \
+}
+
+PROV_CIPHER_HW_blowfish_mode(cbc, CBC)
+PROV_CIPHER_HW_blowfish_mode(ecb, ECB)
+PROV_CIPHER_HW_blowfish_mode(ofb64, OFB)
+PROV_CIPHER_HW_blowfish_mode(cfb64, CFB)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c
new file mode 100644
index 000000000000..c550af3f8330
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Camellia low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for CAMELLIA cipher modes ecb, cbc, ofb, cfb, ctr */
+
+#include "cipher_camellia.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn camellia_freectx;
+static OSSL_FUNC_cipher_dupctx_fn camellia_dupctx;
+
+static void camellia_freectx(void *vctx)
+{
+ PROV_CAMELLIA_CTX *ctx = (PROV_CAMELLIA_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *camellia_dupctx(void *ctx)
+{
+ PROV_CAMELLIA_CTX *in = (PROV_CAMELLIA_CTX *)ctx;
+ PROV_CAMELLIA_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+/* ossl_camellia256ecb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 256, 128, 0, block)
+/* ossl_camellia192ecb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 192, 128, 0, block)
+/* ossl_camellia128ecb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ecb, ECB, 0, 128, 128, 0, block)
+/* ossl_camellia256cbc_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 256, 128, 128, block)
+/* ossl_camellia192cbc_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 192, 128, 128, block)
+/* ossl_camellia128cbc_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cbc, CBC, 0, 128, 128, 128, block)
+/* ossl_camellia256ofb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 256, 8, 128, stream)
+/* ossl_camellia192ofb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 192, 8, 128, stream)
+/* ossl_camellia128ofb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ofb, OFB, 0, 128, 8, 128, stream)
+/* ossl_camellia256cfb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 256, 8, 128, stream)
+/* ossl_camellia192cfb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 192, 8, 128, stream)
+/* ossl_camellia128cfb_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb, CFB, 0, 128, 8, 128, stream)
+/* ossl_camellia256cfb1_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 256, 8, 128, stream)
+/* ossl_camellia192cfb1_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 192, 8, 128, stream)
+/* ossl_camellia128cfb1_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb1, CFB, 0, 128, 8, 128, stream)
+/* ossl_camellia256cfb8_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 256, 8, 128, stream)
+/* ossl_camellia192cfb8_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 192, 8, 128, stream)
+/* ossl_camellia128cfb8_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, cfb8, CFB, 0, 128, 8, 128, stream)
+/* ossl_camellia256ctr_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 256, 8, 128, stream)
+/* ossl_camellia192ctr_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 192, 8, 128, stream)
+/* ossl_camellia128ctr_functions */
+IMPLEMENT_generic_cipher(camellia, CAMELLIA, ctr, CTR, 0, 128, 8, 128, stream)
+
+#include "cipher_camellia_cts.inc"
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h
new file mode 100644
index 000000000000..953ea74c0b5d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/camellia.h>
+#include "prov/ciphercommon.h"
+#include "crypto/cmll_platform.h"
+
+typedef struct prov_camellia_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ CAMELLIA_KEY ks;
+ } ks;
+} PROV_CAMELLIA_CTX;
+
+#define ossl_prov_cipher_hw_camellia_ofb ossl_prov_cipher_hw_camellia_ofb128
+#define ossl_prov_cipher_hw_camellia_cfb ossl_prov_cipher_hw_camellia_cfb128
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ofb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb1(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_cfb8(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_ctr(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc
new file mode 100644
index 000000000000..84ea992b8da9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_cts.inc
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for CAMELLIA CBC CTS ciphers */
+
+#include <openssl/proverr.h>
+#include "cipher_cts.h"
+
+#define CTS_FLAGS PROV_CIPHER_FLAG_CTS
+
+static OSSL_FUNC_cipher_encrypt_init_fn camellia_cbc_cts_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn camellia_cbc_cts_dinit;
+static OSSL_FUNC_cipher_get_ctx_params_fn camellia_cbc_cts_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn camellia_cbc_cts_set_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn camellia_cbc_cts_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn camellia_cbc_cts_settable_ctx_params;
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(camellia_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(camellia_cbc_cts)
+
+static int camellia_cbc_cts_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return camellia_cbc_cts_set_ctx_params(ctx, params);
+}
+
+static int camellia_cbc_cts_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return camellia_cbc_cts_set_ctx_params(ctx, params);
+}
+
+static int camellia_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS_MODE);
+ if (p != NULL) {
+ const char *name = ossl_cipher_cbc_cts_mode_id2name(ctx->cts_mode);
+
+ if (name == NULL || !OSSL_PARAM_set_utf8_string(p, name)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ return ossl_cipher_generic_get_ctx_params(vctx, params);
+}
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(camellia_cbc_cts)
+OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_CTS_MODE, NULL, 0),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(camellia_cbc_cts)
+
+static int camellia_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+ int id;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_CTS_MODE);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ goto err;
+ id = ossl_cipher_cbc_cts_mode_name2id(p->data);
+ if (id < 0)
+ goto err;
+
+ ctx->cts_mode = (unsigned int)id;
+ }
+ return ossl_cipher_generic_set_ctx_params(vctx, params);
+err:
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+}
+
+/* ossl_camellia256cbc_cts_functions */
+IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 256, 128, 128, block)
+/* ossl_camellia192cbc_cts_functions */
+IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 192, 128, 128, block)
+/* ossl_camellia128cbc_cts_functions */
+IMPLEMENT_cts_cipher(camellia, CAMELLIA, cbc, CBC, CTS_FLAGS, 128, 128, 128, block)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c
new file mode 100644
index 000000000000..3ebf5b8d4612
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Camellia low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/camellia.h>
+#include <openssl/proverr.h>
+#include "cipher_camellia.h"
+
+static int cipher_hw_camellia_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key, size_t keylen)
+{
+ int ret, mode = dat->mode;
+ PROV_CAMELLIA_CTX *adat = (PROV_CAMELLIA_CTX *)dat;
+ CAMELLIA_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+ ret = Camellia_set_key(key, keylen * 8, ks);
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+ if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE)) {
+ dat->block = (block128_f) Camellia_encrypt;
+ dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f) Camellia_cbc_encrypt : NULL;
+ } else {
+ dat->block = (block128_f) Camellia_decrypt;
+ dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f) Camellia_cbc_encrypt : NULL;
+ }
+ return 1;
+}
+
+IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_camellia_copyctx, PROV_CAMELLIA_CTX)
+
+# if defined(SPARC_CMLL_CAPABLE)
+# include "cipher_camellia_hw_t4.inc"
+# else
+/* The generic case */
+# define PROV_CIPHER_HW_declare(mode)
+# define PROV_CIPHER_HW_select(mode)
+# endif /* SPARC_CMLL_CAPABLE */
+
+#define PROV_CIPHER_HW_camellia_mode(mode) \
+static const PROV_CIPHER_HW camellia_##mode = { \
+ cipher_hw_camellia_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_camellia_copyctx \
+}; \
+PROV_CIPHER_HW_declare(mode) \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_camellia_##mode(size_t keybits) \
+{ \
+ PROV_CIPHER_HW_select(mode) \
+ return &camellia_##mode; \
+}
+
+PROV_CIPHER_HW_camellia_mode(cbc)
+PROV_CIPHER_HW_camellia_mode(ecb)
+PROV_CIPHER_HW_camellia_mode(ofb128)
+PROV_CIPHER_HW_camellia_mode(cfb128)
+PROV_CIPHER_HW_camellia_mode(cfb1)
+PROV_CIPHER_HW_camellia_mode(cfb8)
+PROV_CIPHER_HW_camellia_mode(ctr)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc
new file mode 100644
index 000000000000..2dcf3fa18eb5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_camellia_hw_t4.inc
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Fujitsu SPARC64 X support for camellia modes.
+ * This file is included by cipher_camellia_hw.c
+ */
+
+static int cipher_hw_camellia_t4_initkey(PROV_CIPHER_CTX *dat,
+ const unsigned char *key,
+ size_t keylen)
+{
+ int ret = 0, bits, mode = dat->mode;
+ PROV_CAMELLIA_CTX *adat = (PROV_CAMELLIA_CTX *)dat;
+ CAMELLIA_KEY *ks = &adat->ks.ks;
+
+ dat->ks = ks;
+ bits = keylen * 8;
+
+ cmll_t4_set_key(key, bits, ks);
+
+ if (dat->enc || (mode != EVP_CIPH_ECB_MODE && mode != EVP_CIPH_CBC_MODE)) {
+ dat->block = (block128_f) cmll_t4_encrypt;
+ switch (bits) {
+ case 128:
+ if (mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f) cmll128_t4_cbc_encrypt;
+ else if (mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f) cmll128_t4_ctr32_encrypt;
+ else
+ dat->stream.cbc = NULL;
+ break;
+ case 192:
+ case 256:
+ if (mode == EVP_CIPH_CBC_MODE)
+ dat->stream.cbc = (cbc128_f) cmll256_t4_cbc_encrypt;
+ else if (mode == EVP_CIPH_CTR_MODE)
+ dat->stream.ctr = (ctr128_f) cmll256_t4_ctr32_encrypt;
+ else
+ dat->stream.cbc = NULL;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ } else {
+ dat->block = (block128_f) cmll_t4_decrypt;
+ switch (bits) {
+ case 128:
+ dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f) cmll128_t4_cbc_decrypt : NULL;
+ break;
+ case 192:
+ case 256:
+ dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ?
+ (cbc128_f) cmll256_t4_cbc_decrypt : NULL;
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ }
+ if (ret < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SETUP_FAILED);
+ return 0;
+ }
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW t4_camellia_##mode = { \
+ cipher_hw_camellia_t4_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_camellia_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (SPARC_CMLL_CAPABLE) \
+ return &t4_camellia_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast.h b/crypto/openssl/providers/implementations/ciphers/cipher_cast.h
new file mode 100644
index 000000000000..84b58621c1bc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/cast.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_cast_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ CAST_KEY ks;
+ } ks;
+} PROV_CAST_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_ofb64(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_cfb64(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c b/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c
new file mode 100644
index 000000000000..84c88793b05e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast5.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * CAST low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for cast cipher modes ecb, cbc, ofb, cfb */
+
+#include <openssl/proverr.h>
+#include "cipher_cast.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define CAST5_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH
+
+static OSSL_FUNC_cipher_freectx_fn cast5_freectx;
+static OSSL_FUNC_cipher_dupctx_fn cast5_dupctx;
+
+static void cast5_freectx(void *vctx)
+{
+ PROV_CAST_CTX *ctx = (PROV_CAST_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *cast5_dupctx(void *ctx)
+{
+ PROV_CAST_CTX *in = (PROV_CAST_CTX *)ctx;
+ PROV_CAST_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+/* ossl_cast5128ecb_functions */
+IMPLEMENT_var_keylen_cipher(cast5, CAST, ecb, ECB, CAST5_FLAGS, 128, 64, 0, block)
+/* ossl_cast5128cbc_functions */
+IMPLEMENT_var_keylen_cipher(cast5, CAST, cbc, CBC, CAST5_FLAGS, 128, 64, 64, block)
+/* ossl_cast5128ofb64_functions */
+IMPLEMENT_var_keylen_cipher(cast5, CAST, ofb64, OFB, CAST5_FLAGS, 128, 8, 64, stream)
+/* ossl_cast5128cfb64_functions */
+IMPLEMENT_var_keylen_cipher(cast5, CAST, cfb64, CFB, CAST5_FLAGS, 128, 8, 64, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c
new file mode 100644
index 000000000000..73f0628e578b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_cast5_hw.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * CAST low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_cast.h"
+
+static int cipher_hw_cast5_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_CAST_CTX *bctx = (PROV_CAST_CTX *)ctx;
+
+ CAST_set_key(&(bctx->ks.ks), keylen, key);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_cast_mode(mode, UCMODE) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, cast5, PROV_CAST_CTX, CAST_KEY, \
+ CAST_##mode) \
+static const PROV_CIPHER_HW cast5_##mode = { \
+ cipher_hw_cast5_initkey, \
+ cipher_hw_cast5_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_cast5_##mode(size_t keybits) \
+{ \
+ return &cast5_##mode; \
+}
+
+PROV_CIPHER_HW_cast_mode(cbc, CBC)
+PROV_CIPHER_HW_cast_mode(ecb, ECB)
+PROV_CIPHER_HW_cast_mode(ofb64, OFB)
+PROV_CIPHER_HW_cast_mode(cfb64, CFB)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c
new file mode 100644
index 000000000000..3d03e6b09266
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for chacha20 cipher */
+
+#include <openssl/proverr.h>
+#include "cipher_chacha20.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define CHACHA20_KEYLEN (CHACHA_KEY_SIZE)
+#define CHACHA20_BLKLEN (1)
+#define CHACHA20_IVLEN (CHACHA_CTR_SIZE)
+#define CHACHA20_FLAGS (PROV_CIPHER_FLAG_CUSTOM_IV)
+
+static OSSL_FUNC_cipher_newctx_fn chacha20_newctx;
+static OSSL_FUNC_cipher_freectx_fn chacha20_freectx;
+static OSSL_FUNC_cipher_dupctx_fn chacha20_dupctx;
+static OSSL_FUNC_cipher_get_params_fn chacha20_get_params;
+static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_set_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn chacha20_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn chacha20_settable_ctx_params;
+#define chacha20_cipher ossl_cipher_generic_cipher
+#define chacha20_update ossl_cipher_generic_stream_update
+#define chacha20_final ossl_cipher_generic_stream_final
+#define chacha20_gettable_params ossl_cipher_generic_gettable_params
+
+void ossl_chacha20_initctx(PROV_CHACHA20_CTX *ctx)
+{
+ ossl_cipher_generic_initkey(ctx, CHACHA20_KEYLEN * 8,
+ CHACHA20_BLKLEN * 8,
+ CHACHA20_IVLEN * 8,
+ 0, CHACHA20_FLAGS,
+ ossl_prov_cipher_hw_chacha20(CHACHA20_KEYLEN * 8),
+ NULL);
+}
+
+static void *chacha20_newctx(void *provctx)
+{
+ PROV_CHACHA20_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_chacha20_initctx(ctx);
+ return ctx;
+}
+
+static void chacha20_freectx(void *vctx)
+{
+ PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx;
+
+ if (ctx != NULL) {
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+static void *chacha20_dupctx(void *vctx)
+{
+ PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)vctx;
+ PROV_CHACHA20_CTX *dupctx = NULL;
+
+ if (ctx != NULL) {
+ dupctx = OPENSSL_memdup(ctx, sizeof(*dupctx));
+ if (dupctx != NULL && dupctx->base.tlsmac != NULL && dupctx->base.alloced) {
+ dupctx->base.tlsmac = OPENSSL_memdup(dupctx->base.tlsmac,
+ dupctx->base.tlsmacsize);
+ if (dupctx->base.tlsmac == NULL) {
+ OPENSSL_free(dupctx);
+ dupctx = NULL;
+ }
+ }
+ }
+ return dupctx;
+}
+
+static int chacha20_get_params(OSSL_PARAM params[])
+{
+ return ossl_cipher_generic_get_params(params, 0, CHACHA20_FLAGS,
+ CHACHA20_KEYLEN * 8,
+ CHACHA20_BLKLEN * 8,
+ CHACHA20_IVLEN * 8);
+}
+
+static int chacha20_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_IVLEN)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_KEYLEN)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM chacha20_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *chacha20_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return chacha20_known_gettable_ctx_params;
+}
+
+static int chacha20_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ size_t len;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (len != CHACHA20_KEYLEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (len != CHACHA20_IVLEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const OSSL_PARAM chacha20_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *chacha20_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return chacha20_known_settable_ctx_params;
+}
+
+int ossl_chacha20_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ int ret;
+
+ /* The generic function checks for ossl_prov_is_running() */
+ ret = ossl_cipher_generic_einit(vctx, key, keylen, iv, ivlen, NULL);
+ if (ret && iv != NULL) {
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw;
+
+ hw->initiv(ctx);
+ }
+ if (ret && !chacha20_set_ctx_params(vctx, params))
+ ret = 0;
+ return ret;
+}
+
+int ossl_chacha20_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ int ret;
+
+ /* The generic function checks for ossl_prov_is_running() */
+ ret = ossl_cipher_generic_dinit(vctx, key, keylen, iv, ivlen, NULL);
+ if (ret && iv != NULL) {
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20 *hw = (PROV_CIPHER_HW_CHACHA20 *)ctx->hw;
+
+ hw->initiv(ctx);
+ }
+ if (ret && !chacha20_set_ctx_params(vctx, params))
+ ret = 0;
+ return ret;
+}
+
+/* ossl_chacha20_functions */
+const OSSL_DISPATCH ossl_chacha20_functions[] = {
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_newctx },
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_freectx },
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))chacha20_dupctx },
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_chacha20_einit },
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_chacha20_dinit },
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_update },
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_final },
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_cipher},
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))chacha20_get_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, (void (*)(void))chacha20_gettable_params },
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))chacha20_get_ctx_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))chacha20_gettable_ctx_params },
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))chacha20_set_ctx_params },
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))chacha20_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h
new file mode 100644
index 000000000000..9db8ed9cb424
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "include/crypto/chacha.h"
+#include "prov/ciphercommon.h"
+
+typedef struct {
+ PROV_CIPHER_CTX base; /* must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ unsigned int d[CHACHA_KEY_SIZE / 4];
+ } key;
+ unsigned int counter[CHACHA_CTR_SIZE / 4];
+ unsigned char buf[CHACHA_BLK_SIZE];
+ unsigned int partial_len;
+} PROV_CHACHA20_CTX;
+
+typedef struct prov_cipher_hw_chacha20_st {
+ PROV_CIPHER_HW base; /* must be first */
+ int (*initiv)(PROV_CIPHER_CTX *ctx);
+
+} PROV_CIPHER_HW_CHACHA20;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20(size_t keybits);
+
+OSSL_FUNC_cipher_encrypt_init_fn ossl_chacha20_einit;
+OSSL_FUNC_cipher_decrypt_init_fn ossl_chacha20_dinit;
+void ossl_chacha20_initctx(PROV_CHACHA20_CTX *ctx);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c
new file mode 100644
index 000000000000..3b03bc81ed5a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_hw.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* chacha20 cipher implementation */
+
+#include "cipher_chacha20.h"
+
+static int chacha20_initkey(PROV_CIPHER_CTX *bctx, const uint8_t *key,
+ size_t keylen)
+{
+ PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
+ unsigned int i;
+
+ if (key != NULL) {
+ for (i = 0; i < CHACHA_KEY_SIZE; i += 4)
+ ctx->key.d[i / 4] = CHACHA_U8TOU32(key + i);
+ }
+ ctx->partial_len = 0;
+ return 1;
+}
+
+static int chacha20_initiv(PROV_CIPHER_CTX *bctx)
+{
+ PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
+ unsigned int i;
+
+ if (bctx->iv_set) {
+ for (i = 0; i < CHACHA_CTR_SIZE; i += 4)
+ ctx->counter[i / 4] = CHACHA_U8TOU32(bctx->oiv + i);
+ }
+ ctx->partial_len = 0;
+ return 1;
+}
+
+static int chacha20_cipher(PROV_CIPHER_CTX *bctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CHACHA20_CTX *ctx = (PROV_CHACHA20_CTX *)bctx;
+ unsigned int n, rem, ctr32;
+
+ n = ctx->partial_len;
+ if (n > 0) {
+ while (inl > 0 && n < CHACHA_BLK_SIZE) {
+ *out++ = *in++ ^ ctx->buf[n++];
+ inl--;
+ }
+ ctx->partial_len = n;
+
+ if (inl == 0)
+ return 1;
+
+ if (n == CHACHA_BLK_SIZE) {
+ ctx->partial_len = 0;
+ ctx->counter[0]++;
+ if (ctx->counter[0] == 0)
+ ctx->counter[1]++;
+ }
+ }
+
+ rem = (unsigned int)(inl % CHACHA_BLK_SIZE);
+ inl -= rem;
+ ctr32 = ctx->counter[0];
+ while (inl >= CHACHA_BLK_SIZE) {
+ size_t blocks = inl / CHACHA_BLK_SIZE;
+
+ /*
+ * 1<<28 is just a not-so-small yet not-so-large number...
+ * Below condition is practically never met, but it has to
+ * be checked for code correctness.
+ */
+ if (sizeof(size_t) > sizeof(unsigned int) && blocks > (1U << 28))
+ blocks = (1U << 28);
+
+ /*
+ * As ChaCha20_ctr32 operates on 32-bit counter, caller
+ * has to handle overflow. 'if' below detects the
+ * overflow, which is then handled by limiting the
+ * amount of blocks to the exact overflow point...
+ */
+ ctr32 += (unsigned int)blocks;
+ if (ctr32 < blocks) {
+ blocks -= ctr32;
+ ctr32 = 0;
+ }
+ blocks *= CHACHA_BLK_SIZE;
+ ChaCha20_ctr32(out, in, blocks, ctx->key.d, ctx->counter);
+ inl -= blocks;
+ in += blocks;
+ out += blocks;
+
+ ctx->counter[0] = ctr32;
+ if (ctr32 == 0) ctx->counter[1]++;
+ }
+
+ if (rem > 0) {
+ memset(ctx->buf, 0, sizeof(ctx->buf));
+ ChaCha20_ctr32(ctx->buf, ctx->buf, CHACHA_BLK_SIZE,
+ ctx->key.d, ctx->counter);
+ for (n = 0; n < rem; n++)
+ out[n] = in[n] ^ ctx->buf[n];
+ ctx->partial_len = rem;
+ }
+
+ return 1;
+}
+
+static const PROV_CIPHER_HW_CHACHA20 chacha20_hw = {
+ { chacha20_initkey, chacha20_cipher },
+ chacha20_initiv
+};
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20(size_t keybits)
+{
+ return (PROV_CIPHER_HW *)&chacha20_hw;
+}
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c
new file mode 100644
index 000000000000..3724da23bcd3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for chacha20_poly1305 cipher */
+
+#include <openssl/proverr.h>
+#include "cipher_chacha20_poly1305.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define CHACHA20_POLY1305_KEYLEN CHACHA_KEY_SIZE
+#define CHACHA20_POLY1305_BLKLEN 1
+#define CHACHA20_POLY1305_MAX_IVLEN 12
+#define CHACHA20_POLY1305_MODE 0
+#define CHACHA20_POLY1305_FLAGS (PROV_CIPHER_FLAG_AEAD \
+ | PROV_CIPHER_FLAG_CUSTOM_IV)
+
+static OSSL_FUNC_cipher_newctx_fn chacha20_poly1305_newctx;
+static OSSL_FUNC_cipher_freectx_fn chacha20_poly1305_freectx;
+static OSSL_FUNC_cipher_dupctx_fn chacha20_poly1305_dupctx;
+static OSSL_FUNC_cipher_encrypt_init_fn chacha20_poly1305_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn chacha20_poly1305_dinit;
+static OSSL_FUNC_cipher_get_params_fn chacha20_poly1305_get_params;
+static OSSL_FUNC_cipher_get_ctx_params_fn chacha20_poly1305_get_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn chacha20_poly1305_set_ctx_params;
+static OSSL_FUNC_cipher_cipher_fn chacha20_poly1305_cipher;
+static OSSL_FUNC_cipher_final_fn chacha20_poly1305_final;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn chacha20_poly1305_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn chacha20_poly1305_settable_ctx_params;
+#define chacha20_poly1305_gettable_params ossl_cipher_generic_gettable_params
+#define chacha20_poly1305_update chacha20_poly1305_cipher
+
+static void *chacha20_poly1305_newctx(void *provctx)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ ossl_cipher_generic_initkey(&ctx->base, CHACHA20_POLY1305_KEYLEN * 8,
+ CHACHA20_POLY1305_BLKLEN * 8,
+ CHACHA20_POLY1305_IVLEN * 8,
+ CHACHA20_POLY1305_MODE,
+ CHACHA20_POLY1305_FLAGS,
+ ossl_prov_cipher_hw_chacha20_poly1305(
+ CHACHA20_POLY1305_KEYLEN * 8),
+ NULL);
+ ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+ ossl_chacha20_initctx(&ctx->chacha);
+ }
+ return ctx;
+}
+
+static void *chacha20_poly1305_dupctx(void *provctx)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = provctx;
+ PROV_CHACHA20_POLY1305_CTX *dctx = NULL;
+
+ if (ctx == NULL)
+ return NULL;
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.tlsmac != NULL && dctx->base.alloced) {
+ dctx->base.tlsmac = OPENSSL_memdup(dctx->base.tlsmac,
+ dctx->base.tlsmacsize);
+ if (dctx->base.tlsmac == NULL) {
+ OPENSSL_free(dctx);
+ dctx = NULL;
+ }
+ }
+ return dctx;
+}
+
+static void chacha20_poly1305_freectx(void *vctx)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
+
+ if (ctx != NULL) {
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+ }
+}
+
+static int chacha20_poly1305_get_params(OSSL_PARAM params[])
+{
+ return ossl_cipher_generic_get_params(params, 0, CHACHA20_POLY1305_FLAGS,
+ CHACHA20_POLY1305_KEYLEN * 8,
+ CHACHA20_POLY1305_BLKLEN * 8,
+ CHACHA20_POLY1305_IVLEN * 8);
+}
+
+static int chacha20_poly1305_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, CHACHA20_POLY1305_IVLEN)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, CHACHA20_POLY1305_KEYLEN)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tag_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ if (!ctx->base.enc) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET);
+ return 0;
+ }
+ if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ memcpy(p->data, ctx->tag, p->data_size);
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM chacha20_poly1305_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *chacha20_poly1305_gettable_ctx_params
+ (ossl_unused void *cctx, ossl_unused void *provctx)
+{
+ return chacha20_poly1305_known_gettable_ctx_params;
+}
+
+static const OSSL_PARAM chacha20_poly1305_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *chacha20_poly1305_settable_ctx_params(
+ ossl_unused void *cctx, ossl_unused void *provctx
+ )
+{
+ return chacha20_poly1305_known_settable_ctx_params;
+}
+
+static int chacha20_poly1305_set_ctx_params(void *vctx,
+ const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ size_t len;
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20_POLY1305 *hw =
+ (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->base.hw;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (len != CHACHA20_POLY1305_KEYLEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (len != CHACHA20_POLY1305_MAX_IVLEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (p->data_size == 0 || p->data_size > POLY1305_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+ if (p->data != NULL) {
+ if (ctx->base.enc) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED);
+ return 0;
+ }
+ memcpy(ctx->tag, p->data, p->data_size);
+ }
+ ctx->tag_len = p->data_size;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ len = hw->tls_init(&ctx->base, p->data, p->data_size);
+ if (len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
+ return 0;
+ }
+ ctx->tls_aad_pad_sz = len;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (hw->tls_iv_set_fixed(&ctx->base, p->data, p->data_size) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int chacha20_poly1305_einit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ int ret;
+
+ /* The generic function checks for ossl_prov_is_running() */
+ ret = ossl_cipher_generic_einit(vctx, key, keylen, iv, ivlen, NULL);
+ if (ret && iv != NULL) {
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20_POLY1305 *hw =
+ (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;
+
+ hw->initiv(ctx);
+ }
+ if (ret && !chacha20_poly1305_set_ctx_params(vctx, params))
+ ret = 0;
+ return ret;
+}
+
+static int chacha20_poly1305_dinit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ int ret;
+
+ /* The generic function checks for ossl_prov_is_running() */
+ ret = ossl_cipher_generic_dinit(vctx, key, keylen, iv, ivlen, NULL);
+ if (ret && iv != NULL) {
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20_POLY1305 *hw =
+ (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;
+
+ hw->initiv(ctx);
+ }
+ if (ret && !chacha20_poly1305_set_ctx_params(vctx, params))
+ ret = 0;
+ return ret;
+}
+
+static int chacha20_poly1305_cipher(void *vctx, unsigned char *out,
+ size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20_POLY1305 *hw =
+ (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (inl == 0) {
+ *outl = 0;
+ return 1;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!hw->aead_cipher(ctx, out, outl, in, inl))
+ return 0;
+
+ return 1;
+}
+
+static int chacha20_poly1305_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ PROV_CIPHER_HW_CHACHA20_POLY1305 *hw =
+ (PROV_CIPHER_HW_CHACHA20_POLY1305 *)ctx->hw;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (hw->aead_cipher(ctx, out, outl, NULL, 0) <= 0)
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+/* ossl_chacha20_ossl_poly1305_functions */
+const OSSL_DISPATCH ossl_chacha20_ossl_poly1305_functions[] = {
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))chacha20_poly1305_newctx },
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))chacha20_poly1305_freectx },
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))chacha20_poly1305_dupctx },
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))chacha20_poly1305_einit },
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))chacha20_poly1305_dinit },
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))chacha20_poly1305_update },
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))chacha20_poly1305_final },
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))chacha20_poly1305_cipher },
+ { OSSL_FUNC_CIPHER_GET_PARAMS,
+ (void (*)(void))chacha20_poly1305_get_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,
+ (void (*)(void))chacha20_poly1305_gettable_params },
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS,
+ (void (*)(void))chacha20_poly1305_get_ctx_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))chacha20_poly1305_gettable_ctx_params },
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS,
+ (void (*)(void))chacha20_poly1305_set_ctx_params },
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))chacha20_poly1305_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h
new file mode 100644
index 000000000000..f2ea26a77f3a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for chacha20_poly1305 cipher */
+
+#include "include/crypto/poly1305.h"
+#include "cipher_chacha20.h"
+
+#define NO_TLS_PAYLOAD_LENGTH ((size_t)-1)
+#define CHACHA20_POLY1305_IVLEN 12
+
+typedef struct {
+ PROV_CIPHER_CTX base; /* must be first */
+ PROV_CHACHA20_CTX chacha;
+ POLY1305 poly1305;
+ unsigned int nonce[12 / 4];
+ unsigned char tag[POLY1305_BLOCK_SIZE];
+ unsigned char tls_aad[POLY1305_BLOCK_SIZE];
+ struct { uint64_t aad, text; } len;
+ unsigned int aad : 1;
+ unsigned int mac_inited : 1;
+ size_t tag_len;
+ size_t tls_payload_length;
+ size_t tls_aad_pad_sz;
+} PROV_CHACHA20_POLY1305_CTX;
+
+typedef struct prov_cipher_hw_chacha_aead_st {
+ PROV_CIPHER_HW base; /* must be first */
+ int (*aead_cipher)(PROV_CIPHER_CTX *dat, unsigned char *out, size_t *outl,
+ const unsigned char *in, size_t len);
+ int (*initiv)(PROV_CIPHER_CTX *ctx);
+ int (*tls_init)(PROV_CIPHER_CTX *ctx, unsigned char *aad, size_t alen);
+ int (*tls_iv_set_fixed)(PROV_CIPHER_CTX *ctx, unsigned char *fixed,
+ size_t flen);
+} PROV_CIPHER_HW_CHACHA20_POLY1305;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20_poly1305(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c
new file mode 100644
index 000000000000..29533efe0eb5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_chacha20_poly1305_hw.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* chacha20_poly1305 cipher implementation */
+
+#include "internal/endian.h"
+#include "cipher_chacha20_poly1305.h"
+
+static int chacha_poly1305_tls_init(PROV_CIPHER_CTX *bctx,
+ unsigned char *aad, size_t alen)
+{
+ unsigned int len;
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+
+ if (alen != EVP_AEAD_TLS1_AAD_LEN)
+ return 0;
+
+ memcpy(ctx->tls_aad, aad, EVP_AEAD_TLS1_AAD_LEN);
+ len = aad[EVP_AEAD_TLS1_AAD_LEN - 2] << 8 | aad[EVP_AEAD_TLS1_AAD_LEN - 1];
+ aad = ctx->tls_aad;
+ if (!bctx->enc) {
+ if (len < POLY1305_BLOCK_SIZE)
+ return 0;
+ len -= POLY1305_BLOCK_SIZE; /* discount attached tag */
+ aad[EVP_AEAD_TLS1_AAD_LEN - 2] = (unsigned char)(len >> 8);
+ aad[EVP_AEAD_TLS1_AAD_LEN - 1] = (unsigned char)len;
+ }
+ ctx->tls_payload_length = len;
+
+ /* merge record sequence number as per RFC7905 */
+ ctx->chacha.counter[1] = ctx->nonce[0];
+ ctx->chacha.counter[2] = ctx->nonce[1] ^ CHACHA_U8TOU32(aad);
+ ctx->chacha.counter[3] = ctx->nonce[2] ^ CHACHA_U8TOU32(aad+4);
+ ctx->mac_inited = 0;
+
+ return POLY1305_BLOCK_SIZE; /* tag length */
+}
+
+static int chacha_poly1305_tls_iv_set_fixed(PROV_CIPHER_CTX *bctx,
+ unsigned char *fixed, size_t flen)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+
+ if (flen != CHACHA20_POLY1305_IVLEN)
+ return 0;
+ ctx->nonce[0] = ctx->chacha.counter[1] = CHACHA_U8TOU32(fixed);
+ ctx->nonce[1] = ctx->chacha.counter[2] = CHACHA_U8TOU32(fixed + 4);
+ ctx->nonce[2] = ctx->chacha.counter[3] = CHACHA_U8TOU32(fixed + 8);
+ return 1;
+}
+
+static int chacha20_poly1305_initkey(PROV_CIPHER_CTX *bctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+
+ ctx->len.aad = 0;
+ ctx->len.text = 0;
+ ctx->aad = 0;
+ ctx->mac_inited = 0;
+ ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+
+ if (bctx->enc)
+ return ossl_chacha20_einit(&ctx->chacha, key, keylen, NULL, 0, NULL);
+ else
+ return ossl_chacha20_dinit(&ctx->chacha, key, keylen, NULL, 0, NULL);
+}
+
+static int chacha20_poly1305_initiv(PROV_CIPHER_CTX *bctx)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+ unsigned char tempiv[CHACHA_CTR_SIZE] = { 0 };
+ int ret = 1;
+ size_t noncelen = CHACHA20_POLY1305_IVLEN;
+
+ ctx->len.aad = 0;
+ ctx->len.text = 0;
+ ctx->aad = 0;
+ ctx->mac_inited = 0;
+ ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+
+ /* pad on the left */
+ memcpy(tempiv + CHACHA_CTR_SIZE - noncelen, bctx->oiv,
+ noncelen);
+
+ if (bctx->enc)
+ ret = ossl_chacha20_einit(&ctx->chacha, NULL, 0,
+ tempiv, sizeof(tempiv), NULL);
+ else
+ ret = ossl_chacha20_dinit(&ctx->chacha, NULL, 0,
+ tempiv, sizeof(tempiv), NULL);
+ ctx->nonce[0] = ctx->chacha.counter[1];
+ ctx->nonce[1] = ctx->chacha.counter[2];
+ ctx->nonce[2] = ctx->chacha.counter[3];
+ bctx->iv_set = 1;
+ return ret;
+}
+
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+
+# if defined(POLY1305_ASM) && (defined(__x86_64) || defined(__x86_64__) \
+ || defined(_M_AMD64) || defined(_M_X64))
+# define XOR128_HELPERS
+void *xor128_encrypt_n_pad(void *out, const void *inp, void *otp, size_t len);
+void *xor128_decrypt_n_pad(void *out, const void *inp, void *otp, size_t len);
+static const unsigned char zero[4 * CHACHA_BLK_SIZE] = { 0 };
+# else
+static const unsigned char zero[2 * CHACHA_BLK_SIZE] = { 0 };
+# endif
+
+static int chacha20_poly1305_tls_cipher(PROV_CIPHER_CTX *bctx,
+ unsigned char *out,
+ size_t *out_padlen,
+ const unsigned char *in, size_t len)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+ POLY1305 *poly = &ctx->poly1305;
+ size_t tail, tohash_len, buf_len, plen = ctx->tls_payload_length;
+ unsigned char *buf, *tohash, *ctr, storage[sizeof(zero) + 32];
+
+ DECLARE_IS_ENDIAN;
+
+ buf = storage + ((0 - (size_t)storage) & 15); /* align */
+ ctr = buf + CHACHA_BLK_SIZE;
+ tohash = buf + CHACHA_BLK_SIZE - POLY1305_BLOCK_SIZE;
+
+# ifdef XOR128_HELPERS
+ if (plen <= 3 * CHACHA_BLK_SIZE) {
+ ctx->chacha.counter[0] = 0;
+ buf_len = (plen + 2 * CHACHA_BLK_SIZE - 1) & (0 - CHACHA_BLK_SIZE);
+ ChaCha20_ctr32(buf, zero, buf_len, ctx->chacha.key.d, ctx->chacha.counter);
+ Poly1305_Init(poly, buf);
+ ctx->chacha.partial_len = 0;
+ memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE);
+ tohash_len = POLY1305_BLOCK_SIZE;
+ ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN;
+ ctx->len.text = plen;
+
+ if (plen) {
+ if (bctx->enc)
+ ctr = xor128_encrypt_n_pad(out, in, ctr, plen);
+ else
+ ctr = xor128_decrypt_n_pad(out, in, ctr, plen);
+
+ in += plen;
+ out += plen;
+ tohash_len = (size_t)(ctr - tohash);
+ }
+ }
+# else
+ if (plen <= CHACHA_BLK_SIZE) {
+ size_t i;
+
+ ctx->chacha.counter[0] = 0;
+ ChaCha20_ctr32(buf, zero, (buf_len = 2 * CHACHA_BLK_SIZE),
+ ctx->chacha.key.d, ctx->chacha.counter);
+ Poly1305_Init(poly, buf);
+ ctx->chacha.partial_len = 0;
+ memcpy(tohash, ctx->tls_aad, POLY1305_BLOCK_SIZE);
+ tohash_len = POLY1305_BLOCK_SIZE;
+ ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN;
+ ctx->len.text = plen;
+
+ if (bctx->enc) {
+ for (i = 0; i < plen; i++)
+ out[i] = ctr[i] ^= in[i];
+ } else {
+ for (i = 0; i < plen; i++) {
+ unsigned char c = in[i];
+
+ out[i] = ctr[i] ^ c;
+ ctr[i] = c;
+ }
+ }
+
+ in += i;
+ out += i;
+
+ tail = (0 - i) & (POLY1305_BLOCK_SIZE - 1);
+ memset(ctr + i, 0, tail);
+ ctr += i + tail;
+ tohash_len += i + tail;
+ }
+# endif
+ else {
+ ctx->chacha.counter[0] = 0;
+ ChaCha20_ctr32(buf, zero, (buf_len = CHACHA_BLK_SIZE),
+ ctx->chacha.key.d, ctx->chacha.counter);
+ Poly1305_Init(poly, buf);
+ ctx->chacha.counter[0] = 1;
+ ctx->chacha.partial_len = 0;
+ Poly1305_Update(poly, ctx->tls_aad, POLY1305_BLOCK_SIZE);
+ tohash = ctr;
+ tohash_len = 0;
+ ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN;
+ ctx->len.text = plen;
+
+ if (bctx->enc) {
+ ChaCha20_ctr32(out, in, plen, ctx->chacha.key.d, ctx->chacha.counter);
+ Poly1305_Update(poly, out, plen);
+ } else {
+ Poly1305_Update(poly, in, plen);
+ ChaCha20_ctr32(out, in, plen, ctx->chacha.key.d, ctx->chacha.counter);
+ }
+
+ in += plen;
+ out += plen;
+ tail = (0 - plen) & (POLY1305_BLOCK_SIZE - 1);
+ Poly1305_Update(poly, zero, tail);
+ }
+
+ if (IS_LITTLE_ENDIAN) {
+ memcpy(ctr, (unsigned char *)&ctx->len, POLY1305_BLOCK_SIZE);
+ } else {
+ ctr[0] = (unsigned char)(ctx->len.aad);
+ ctr[1] = (unsigned char)(ctx->len.aad>>8);
+ ctr[2] = (unsigned char)(ctx->len.aad>>16);
+ ctr[3] = (unsigned char)(ctx->len.aad>>24);
+ ctr[4] = (unsigned char)(ctx->len.aad>>32);
+ ctr[5] = (unsigned char)(ctx->len.aad>>40);
+ ctr[6] = (unsigned char)(ctx->len.aad>>48);
+ ctr[7] = (unsigned char)(ctx->len.aad>>56);
+
+ ctr[8] = (unsigned char)(ctx->len.text);
+ ctr[9] = (unsigned char)(ctx->len.text>>8);
+ ctr[10] = (unsigned char)(ctx->len.text>>16);
+ ctr[11] = (unsigned char)(ctx->len.text>>24);
+ ctr[12] = (unsigned char)(ctx->len.text>>32);
+ ctr[13] = (unsigned char)(ctx->len.text>>40);
+ ctr[14] = (unsigned char)(ctx->len.text>>48);
+ ctr[15] = (unsigned char)(ctx->len.text>>56);
+ }
+ tohash_len += POLY1305_BLOCK_SIZE;
+
+ Poly1305_Update(poly, tohash, tohash_len);
+ OPENSSL_cleanse(buf, buf_len);
+ Poly1305_Final(poly, bctx->enc ? ctx->tag : tohash);
+
+ ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+
+ if (bctx->enc) {
+ memcpy(out, ctx->tag, POLY1305_BLOCK_SIZE);
+ } else {
+ if (CRYPTO_memcmp(tohash, in, POLY1305_BLOCK_SIZE)) {
+ if (len > POLY1305_BLOCK_SIZE)
+ memset(out - (len - POLY1305_BLOCK_SIZE), 0,
+ len - POLY1305_BLOCK_SIZE);
+ return 0;
+ }
+ /* Strip the tag */
+ len -= POLY1305_BLOCK_SIZE;
+ }
+
+ *out_padlen = len;
+ return 1;
+}
+#else
+static const unsigned char zero[CHACHA_BLK_SIZE] = { 0 };
+#endif /* OPENSSL_SMALL_FOOTPRINT */
+
+static int chacha20_poly1305_aead_cipher(PROV_CIPHER_CTX *bctx,
+ unsigned char *out, size_t *outl,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CHACHA20_POLY1305_CTX *ctx = (PROV_CHACHA20_POLY1305_CTX *)bctx;
+ POLY1305 *poly = &ctx->poly1305;
+ size_t rem, plen = ctx->tls_payload_length;
+ size_t olen = 0;
+ int rv = 0;
+
+ DECLARE_IS_ENDIAN;
+
+ if (!ctx->mac_inited) {
+ if (plen != NO_TLS_PAYLOAD_LENGTH && out != NULL) {
+ if (inl != plen + POLY1305_BLOCK_SIZE)
+ return 0;
+#if !defined(OPENSSL_SMALL_FOOTPRINT)
+ return chacha20_poly1305_tls_cipher(bctx, out, outl, in, inl);
+#endif
+ }
+
+ ctx->chacha.counter[0] = 0;
+ ChaCha20_ctr32(ctx->chacha.buf, zero, CHACHA_BLK_SIZE,
+ ctx->chacha.key.d, ctx->chacha.counter);
+ Poly1305_Init(poly, ctx->chacha.buf);
+ ctx->chacha.counter[0] = 1;
+ ctx->chacha.partial_len = 0;
+ ctx->len.aad = ctx->len.text = 0;
+ ctx->mac_inited = 1;
+ if (plen != NO_TLS_PAYLOAD_LENGTH) {
+ Poly1305_Update(poly, ctx->tls_aad, EVP_AEAD_TLS1_AAD_LEN);
+ ctx->len.aad = EVP_AEAD_TLS1_AAD_LEN;
+ ctx->aad = 1;
+ }
+ }
+
+ if (in != NULL) { /* aad or text */
+ if (out == NULL) { /* aad */
+ Poly1305_Update(poly, in, inl);
+ ctx->len.aad += inl;
+ ctx->aad = 1;
+ goto finish;
+ } else { /* plain- or ciphertext */
+ if (ctx->aad) { /* wrap up aad */
+ if ((rem = (size_t)ctx->len.aad % POLY1305_BLOCK_SIZE))
+ Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem);
+ ctx->aad = 0;
+ }
+
+ ctx->tls_payload_length = NO_TLS_PAYLOAD_LENGTH;
+ if (plen == NO_TLS_PAYLOAD_LENGTH)
+ plen = inl;
+ else if (inl != plen + POLY1305_BLOCK_SIZE)
+ goto err;
+
+ if (bctx->enc) { /* plaintext */
+ ctx->chacha.base.hw->cipher(&ctx->chacha.base, out, in, plen);
+ Poly1305_Update(poly, out, plen);
+ in += plen;
+ out += plen;
+ ctx->len.text += plen;
+ } else { /* ciphertext */
+ Poly1305_Update(poly, in, plen);
+ ctx->chacha.base.hw->cipher(&ctx->chacha.base, out, in, plen);
+ in += plen;
+ out += plen;
+ ctx->len.text += plen;
+ }
+ }
+ }
+ /* explicit final, or tls mode */
+ if (in == NULL || inl != plen) {
+
+ unsigned char temp[POLY1305_BLOCK_SIZE];
+
+ if (ctx->aad) { /* wrap up aad */
+ if ((rem = (size_t)ctx->len.aad % POLY1305_BLOCK_SIZE))
+ Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem);
+ ctx->aad = 0;
+ }
+
+ if ((rem = (size_t)ctx->len.text % POLY1305_BLOCK_SIZE))
+ Poly1305_Update(poly, zero, POLY1305_BLOCK_SIZE - rem);
+
+ if (IS_LITTLE_ENDIAN) {
+ Poly1305_Update(poly, (unsigned char *)&ctx->len,
+ POLY1305_BLOCK_SIZE);
+ } else {
+ temp[0] = (unsigned char)(ctx->len.aad);
+ temp[1] = (unsigned char)(ctx->len.aad>>8);
+ temp[2] = (unsigned char)(ctx->len.aad>>16);
+ temp[3] = (unsigned char)(ctx->len.aad>>24);
+ temp[4] = (unsigned char)(ctx->len.aad>>32);
+ temp[5] = (unsigned char)(ctx->len.aad>>40);
+ temp[6] = (unsigned char)(ctx->len.aad>>48);
+ temp[7] = (unsigned char)(ctx->len.aad>>56);
+ temp[8] = (unsigned char)(ctx->len.text);
+ temp[9] = (unsigned char)(ctx->len.text>>8);
+ temp[10] = (unsigned char)(ctx->len.text>>16);
+ temp[11] = (unsigned char)(ctx->len.text>>24);
+ temp[12] = (unsigned char)(ctx->len.text>>32);
+ temp[13] = (unsigned char)(ctx->len.text>>40);
+ temp[14] = (unsigned char)(ctx->len.text>>48);
+ temp[15] = (unsigned char)(ctx->len.text>>56);
+ Poly1305_Update(poly, temp, POLY1305_BLOCK_SIZE);
+ }
+ Poly1305_Final(poly, bctx->enc ? ctx->tag : temp);
+ ctx->mac_inited = 0;
+
+ if (in != NULL && inl != plen) {
+ if (bctx->enc) {
+ memcpy(out, ctx->tag, POLY1305_BLOCK_SIZE);
+ } else {
+ if (CRYPTO_memcmp(temp, in, POLY1305_BLOCK_SIZE)) {
+ memset(out - plen, 0, plen);
+ goto err;
+ }
+ /* Strip the tag */
+ inl -= POLY1305_BLOCK_SIZE;
+ }
+ }
+ else if (!bctx->enc) {
+ if (CRYPTO_memcmp(temp, ctx->tag, ctx->tag_len))
+ goto err;
+ }
+ }
+finish:
+ olen = inl;
+ rv = 1;
+err:
+ *outl = olen;
+ return rv;
+}
+
+static const PROV_CIPHER_HW_CHACHA20_POLY1305 chacha20poly1305_hw = {
+ { chacha20_poly1305_initkey, NULL },
+ chacha20_poly1305_aead_cipher,
+ chacha20_poly1305_initiv,
+ chacha_poly1305_tls_init,
+ chacha_poly1305_tls_iv_set_fixed
+};
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_chacha20_poly1305(size_t keybits)
+{
+ return (PROV_CIPHER_HW *)&chacha20poly1305_hw;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cts.c b/crypto/openssl/providers/implementations/ciphers/cipher_cts.c
new file mode 100644
index 000000000000..9f58b06c4c96
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_cts.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Helper functions for 128 bit CBC CTS ciphers (Currently AES and Camellia).
+ *
+ * The function dispatch tables are embedded into cipher_aes.c
+ * and cipher_camellia.c using cipher_aes_cts.inc and cipher_camellia_cts.inc
+ */
+
+/*
+ * Refer to SP800-38A-Addendum
+ *
+ * Ciphertext stealing encrypts plaintext using a block cipher, without padding
+ * the message to a multiple of the block size, so the ciphertext is the same
+ * size as the plaintext.
+ * It does this by altering processing of the last two blocks of the message.
+ * The processing of all but the last two blocks is unchanged, but a portion of
+ * the second-last block's ciphertext is "stolen" to pad the last plaintext
+ * block. The padded final block is then encrypted as usual.
+ * The final ciphertext for the last two blocks, consists of the partial block
+ * (with the "stolen" portion omitted) plus the full final block,
+ * which are the same size as the original plaintext.
+ * Decryption requires decrypting the final block first, then restoring the
+ * stolen ciphertext to the partial block, which can then be decrypted as usual.
+
+ * AES_CBC_CTS has 3 variants:
+ * (1) CS1 The NIST variant.
+ * If the length is a multiple of the blocksize it is the same as CBC mode.
+ * otherwise it produces C1||C2||(C(n-1))*||Cn.
+ * Where C(n-1)* is a partial block.
+ * (2) CS2
+ * If the length is a multiple of the blocksize it is the same as CBC mode.
+ * otherwise it produces C1||C2||Cn||(C(n-1))*.
+ * Where C(n-1)* is a partial block.
+ * (3) CS3 The Kerberos5 variant.
+ * Produces C1||C2||Cn||(C(n-1))* regardless of the length.
+ * If the length is a multiple of the blocksize it looks similar to CBC mode
+ * with the last 2 blocks swapped.
+ * Otherwise it is the same as CS2.
+ */
+
+#include <openssl/core_names.h>
+#include "prov/ciphercommon.h"
+#include "internal/nelem.h"
+#include "cipher_cts.h"
+
+/* The value assigned to 0 is the default */
+#define CTS_CS1 0
+#define CTS_CS2 1
+#define CTS_CS3 2
+
+#define CTS_BLOCK_SIZE 16
+
+typedef union {
+ size_t align;
+ unsigned char c[CTS_BLOCK_SIZE];
+} aligned_16bytes;
+
+typedef struct cts_mode_name2id_st {
+ unsigned int id;
+ const char *name;
+} CTS_MODE_NAME2ID;
+
+static CTS_MODE_NAME2ID cts_modes[] = {
+ { CTS_CS1, OSSL_CIPHER_CTS_MODE_CS1 },
+ { CTS_CS2, OSSL_CIPHER_CTS_MODE_CS2 },
+ { CTS_CS3, OSSL_CIPHER_CTS_MODE_CS3 },
+};
+
+const char *ossl_cipher_cbc_cts_mode_id2name(unsigned int id)
+{
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(cts_modes); ++i) {
+ if (cts_modes[i].id == id)
+ return cts_modes[i].name;
+ }
+ return NULL;
+}
+
+int ossl_cipher_cbc_cts_mode_name2id(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(cts_modes); ++i) {
+ if (OPENSSL_strcasecmp(name, cts_modes[i].name) == 0)
+ return (int)cts_modes[i].id;
+ }
+ return -1;
+}
+
+static size_t cts128_cs1_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ aligned_16bytes tmp_in;
+ size_t residue;
+
+ residue = len % CTS_BLOCK_SIZE;
+ len -= residue;
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+
+ if (residue == 0)
+ return len;
+
+ in += len;
+ out += len;
+
+ memset(tmp_in.c, 0, sizeof(tmp_in));
+ memcpy(tmp_in.c, in, residue);
+ if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE + residue, tmp_in.c,
+ CTS_BLOCK_SIZE))
+ return 0;
+ return len + residue;
+}
+
+static void do_xor(const unsigned char *in1, const unsigned char *in2,
+ size_t len, unsigned char *out)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i)
+ out[i] = in1[i] ^ in2[i];
+}
+
+static size_t cts128_cs1_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ aligned_16bytes mid_iv, ct_mid, cn, pt_last;
+ size_t residue;
+
+ residue = len % CTS_BLOCK_SIZE;
+ if (residue == 0) {
+ /* If there are no partial blocks then it is the same as CBC mode */
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+ return len;
+ }
+ /* Process blocks at the start - but leave the last 2 blocks */
+ len -= CTS_BLOCK_SIZE + residue;
+ if (len > 0) {
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+ in += len;
+ out += len;
+ }
+ /* Save the iv that will be used by the second last block */
+ memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE);
+ /* Save the C(n) block */
+ memcpy(cn.c, in + residue, CTS_BLOCK_SIZE);
+
+ /* Decrypt the last block first using an iv of zero */
+ memset(ctx->iv, 0, CTS_BLOCK_SIZE);
+ if (!ctx->hw->cipher(ctx, pt_last.c, in + residue, CTS_BLOCK_SIZE))
+ return 0;
+
+ /*
+ * Rebuild the ciphertext of the second last block as a combination of
+ * the decrypted last block + replace the start with the ciphertext bytes
+ * of the partial second last block.
+ */
+ memcpy(ct_mid.c, in, residue);
+ memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue);
+ /*
+ * Restore the last partial ciphertext block.
+ * Now that we have the cipher text of the second last block, apply
+ * that to the partial plaintext end block. We have already decrypted the
+ * block using an IV of zero. For decryption the IV is just XORed after
+ * doing an Cipher CBC block - so just XOR in the cipher text.
+ */
+ do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE);
+
+ /* Restore the iv needed by the second last block */
+ memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE);
+
+ /*
+ * Decrypt the second last plaintext block now that we have rebuilt the
+ * ciphertext.
+ */
+ if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE))
+ return 0;
+
+ /* The returned iv is the C(n) block */
+ memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE);
+ return len + CTS_BLOCK_SIZE + residue;
+}
+
+static size_t cts128_cs3_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ aligned_16bytes tmp_in;
+ size_t residue;
+
+ if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */
+ return 0;
+
+ /* If we only have one block then just process the aligned block */
+ if (len == CTS_BLOCK_SIZE)
+ return ctx->hw->cipher(ctx, out, in, len) ? len : 0;
+
+ residue = len % CTS_BLOCK_SIZE;
+ if (residue == 0)
+ residue = CTS_BLOCK_SIZE;
+ len -= residue;
+
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+
+ in += len;
+ out += len;
+
+ memset(tmp_in.c, 0, sizeof(tmp_in));
+ memcpy(tmp_in.c, in, residue);
+ memcpy(out, out - CTS_BLOCK_SIZE, residue);
+ if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE, tmp_in.c, CTS_BLOCK_SIZE))
+ return 0;
+ return len + residue;
+}
+
+/*
+ * Note:
+ * The cipher text (in) is of the form C(0), C(1), ., C(n), C(n-1)* where
+ * C(n) is a full block and C(n-1)* can be a partial block
+ * (but could be a full block).
+ * This means that the output plaintext (out) needs to swap the plaintext of
+ * the last two decoded ciphertext blocks.
+ */
+static size_t cts128_cs3_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ aligned_16bytes mid_iv, ct_mid, cn, pt_last;
+ size_t residue;
+
+ if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */
+ return 0;
+
+ /* If we only have one block then just process the aligned block */
+ if (len == CTS_BLOCK_SIZE)
+ return ctx->hw->cipher(ctx, out, in, len) ? len : 0;
+
+ /* Process blocks at the start - but leave the last 2 blocks */
+ residue = len % CTS_BLOCK_SIZE;
+ if (residue == 0)
+ residue = CTS_BLOCK_SIZE;
+ len -= CTS_BLOCK_SIZE + residue;
+
+ if (len > 0) {
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+ in += len;
+ out += len;
+ }
+ /* Save the iv that will be used by the second last block */
+ memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE);
+ /* Save the C(n) block : For CS3 it is C(1)||...||C(n-2)||C(n)||C(n-1)* */
+ memcpy(cn.c, in, CTS_BLOCK_SIZE);
+
+ /* Decrypt the C(n) block first using an iv of zero */
+ memset(ctx->iv, 0, CTS_BLOCK_SIZE);
+ if (!ctx->hw->cipher(ctx, pt_last.c, in, CTS_BLOCK_SIZE))
+ return 0;
+
+ /*
+ * Rebuild the ciphertext of C(n-1) as a combination of
+ * the decrypted C(n) block + replace the start with the ciphertext bytes
+ * of the partial last block.
+ */
+ memcpy(ct_mid.c, in + CTS_BLOCK_SIZE, residue);
+ if (residue != CTS_BLOCK_SIZE)
+ memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue);
+ /*
+ * Restore the last partial ciphertext block.
+ * Now that we have the cipher text of the second last block, apply
+ * that to the partial plaintext end block. We have already decrypted the
+ * block using an IV of zero. For decryption the IV is just XORed after
+ * doing an AES block - so just XOR in the ciphertext.
+ */
+ do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE);
+
+ /* Restore the iv needed by the second last block */
+ memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE);
+ /*
+ * Decrypt the second last plaintext block now that we have rebuilt the
+ * ciphertext.
+ */
+ if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE))
+ return 0;
+
+ /* The returned iv is the C(n) block */
+ memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE);
+ return len + CTS_BLOCK_SIZE + residue;
+}
+
+static size_t cts128_cs2_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ if (len % CTS_BLOCK_SIZE == 0) {
+ /* If there are no partial blocks then it is the same as CBC mode */
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+ return len;
+ }
+ /* For partial blocks CS2 is equivalent to CS3 */
+ return cts128_cs3_encrypt(ctx, in, out, len);
+}
+
+static size_t cts128_cs2_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len)
+{
+ if (len % CTS_BLOCK_SIZE == 0) {
+ /* If there are no partial blocks then it is the same as CBC mode */
+ if (!ctx->hw->cipher(ctx, out, in, len))
+ return 0;
+ return len;
+ }
+ /* For partial blocks CS2 is equivalent to CS3 */
+ return cts128_cs3_decrypt(ctx, in, out, len);
+}
+
+int ossl_cipher_cbc_cts_block_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ size_t sz = 0;
+
+ if (inl < CTS_BLOCK_SIZE) /* There must be at least one block for CTS mode */
+ return 0;
+ if (outsize < inl)
+ return 0;
+ if (out == NULL) {
+ *outl = inl;
+ return 1;
+ }
+
+ /*
+ * Return an error if the update is called multiple times, only one shot
+ * is supported.
+ */
+ if (ctx->updated == 1)
+ return 0;
+
+ if (ctx->enc) {
+ if (ctx->cts_mode == CTS_CS1)
+ sz = cts128_cs1_encrypt(ctx, in, out, inl);
+ else if (ctx->cts_mode == CTS_CS2)
+ sz = cts128_cs2_encrypt(ctx, in, out, inl);
+ else if (ctx->cts_mode == CTS_CS3)
+ sz = cts128_cs3_encrypt(ctx, in, out, inl);
+ } else {
+ if (ctx->cts_mode == CTS_CS1)
+ sz = cts128_cs1_decrypt(ctx, in, out, inl);
+ else if (ctx->cts_mode == CTS_CS2)
+ sz = cts128_cs2_decrypt(ctx, in, out, inl);
+ else if (ctx->cts_mode == CTS_CS3)
+ sz = cts128_cs3_decrypt(ctx, in, out, inl);
+ }
+ if (sz == 0)
+ return 0;
+ ctx->updated = 1; /* Stop multiple updates being allowed */
+ *outl = sz;
+ return 1;
+}
+
+int ossl_cipher_cbc_cts_block_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ *outl = 0;
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_cts.h b/crypto/openssl/providers/implementations/ciphers/cipher_cts.h
new file mode 100644
index 000000000000..a26e5a9e0719
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_cts.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/evp.h"
+
+/* NOTE: The underlying block cipher is CBC so we reuse most of the code */
+#define IMPLEMENT_cts_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \
+static int alg##_cts_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_cts_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) alg##_cbc_cts_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) alg##_cbc_cts_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, \
+ (void (*)(void)) ossl_cipher_cbc_cts_block_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, \
+ (void (*)(void)) ossl_cipher_cbc_cts_block_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_cts_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void)) alg##_cbc_cts_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void)) alg##_cbc_cts_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void)) alg##_cbc_cts_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void)) alg##_cbc_cts_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+OSSL_FUNC_cipher_update_fn ossl_cipher_cbc_cts_block_update;
+OSSL_FUNC_cipher_final_fn ossl_cipher_cbc_cts_block_final;
+
+const char *ossl_cipher_cbc_cts_mode_id2name(unsigned int id);
+int ossl_cipher_cbc_cts_mode_name2id(const char *name);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des.c b/crypto/openssl/providers/implementations/ciphers/cipher_des.c
new file mode 100644
index 000000000000..e2c890979ea4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_des.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/ciphercommon.h"
+#include "cipher_des.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define DES_FLAGS PROV_CIPHER_FLAG_RAND_KEY
+
+static OSSL_FUNC_cipher_freectx_fn des_freectx;
+static OSSL_FUNC_cipher_encrypt_init_fn des_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn des_dinit;
+static OSSL_FUNC_cipher_get_ctx_params_fn des_get_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn des_gettable_ctx_params;
+
+static void *des_newctx(void *provctx, size_t kbits, size_t blkbits,
+ size_t ivbits, unsigned int mode, uint64_t flags,
+ const PROV_CIPHER_HW *hw)
+{
+ PROV_DES_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, mode, flags,
+ hw, provctx);
+ return ctx;
+}
+
+static void *des_dupctx(void *ctx)
+{
+ PROV_DES_CTX *in = (PROV_DES_CTX *)ctx;
+ PROV_DES_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+static void des_freectx(void *vctx)
+{
+ PROV_DES_CTX *ctx = (PROV_DES_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static int des_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->num = 0;
+ ctx->bufsz = 0;
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (!ossl_cipher_generic_initiv(ctx, iv, ivlen))
+ return 0;
+ } else if (ctx->iv_set) {
+ /* reset IV to keep compatibility with 1.1.1 */
+ memcpy(ctx->iv, ctx->oiv, ctx->ivlen);
+ }
+
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->init(ctx, key, keylen))
+ return 0;
+ ctx->key_set = 1;
+ }
+ return ossl_cipher_generic_set_ctx_params(ctx, params);
+}
+
+static int des_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return des_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int des_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return des_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static int des_generatekey(PROV_CIPHER_CTX *ctx, void *ptr)
+{
+
+ DES_cblock *deskey = ptr;
+ size_t kl = ctx->keylen;
+
+ if (kl == 0 || RAND_priv_bytes_ex(ctx->libctx, ptr, kl, 0) <= 0)
+ return 0;
+ DES_set_odd_parity(deskey);
+ return 1;
+}
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(des)
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_RANDOM_KEY, NULL, 0),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(des)
+
+static int des_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (!ossl_cipher_generic_get_ctx_params(vctx, params))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RANDOM_KEY);
+ if (p != NULL && !des_generatekey(ctx, p->data)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ return 0;
+ }
+ return 1;
+}
+
+#define IMPLEMENT_des_cipher(type, lcmode, UCMODE, flags, \
+ kbits, blkbits, ivbits, block) \
+static OSSL_FUNC_cipher_newctx_fn type##_##lcmode##_newctx; \
+static void *des_##lcmode##_newctx(void *provctx) \
+{ \
+ return des_newctx(provctx, kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags, \
+ ossl_prov_cipher_hw_des_##lcmode()); \
+} \
+static OSSL_FUNC_cipher_get_params_fn des_##lcmode##_get_params; \
+static int des_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+const OSSL_DISPATCH ossl_##des_##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))des_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))des_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, \
+ (void (*)(void))ossl_cipher_generic_##block##_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##block##_final },\
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void))des_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))des_dupctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))des_freectx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))des_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))des_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))des_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+/* ossl_des_ecb_functions */
+IMPLEMENT_des_cipher(des, ecb, ECB, DES_FLAGS, 64, 64, 0, block);
+/* ossl_des_cbc_functions */
+IMPLEMENT_des_cipher(des, cbc, CBC, DES_FLAGS, 64, 64, 64, block);
+/* ossl_des_ofb64_functions */
+IMPLEMENT_des_cipher(des, ofb64, OFB, DES_FLAGS, 64, 8, 64, stream);
+/* ossl_des_cfb64_functions */
+IMPLEMENT_des_cipher(des, cfb64, CFB, DES_FLAGS, 64, 8, 64, stream);
+/* ossl_des_cfb1_functions */
+IMPLEMENT_des_cipher(des, cfb1, CFB, DES_FLAGS, 64, 8, 64, stream);
+/* ossl_des_cfb8_functions */
+IMPLEMENT_des_cipher(des, cfb8, CFB, DES_FLAGS, 64, 8, 64, stream);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des.h b/crypto/openssl/providers/implementations/ciphers/cipher_des.h
new file mode 100644
index 000000000000..ad10f63d8b16
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_des.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/des.h>
+#include "crypto/des_platform.h"
+
+#define TDES_FLAGS 0
+
+typedef struct prov_des_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ DES_key_schedule ks;
+ } dks;
+ union {
+ void (*cbc) (const void *, void *, size_t,
+ const DES_key_schedule *, unsigned char *);
+ } dstream;
+
+} PROV_DES_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cbc(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_ecb(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_ofb64(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb64(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb1(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_cfb8(void);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c
new file mode 100644
index 000000000000..a2d54b46ba11
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_des_hw.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "prov/ciphercommon.h"
+#include "cipher_des.h"
+
+static int cipher_hw_des_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_DES_CTX *dctx = (PROV_DES_CTX *)ctx;
+ DES_cblock *deskey = (DES_cblock *)key;
+ DES_key_schedule *ks = &dctx->dks.ks;
+
+ dctx->dstream.cbc = NULL;
+#if defined(SPARC_DES_CAPABLE)
+ if (SPARC_DES_CAPABLE) {
+ if (ctx->mode == EVP_CIPH_CBC_MODE) {
+ des_t4_key_expand(&deskey[0], ks);
+ dctx->dstream.cbc = ctx->enc ? des_t4_cbc_encrypt :
+ des_t4_cbc_decrypt;
+ return 1;
+ }
+ }
+#endif
+ DES_set_key_unchecked(deskey, ks);
+ return 1;
+}
+
+static void cipher_hw_des_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src)
+{
+ PROV_DES_CTX *sctx = (PROV_DES_CTX *)src;
+ PROV_DES_CTX *dctx = (PROV_DES_CTX *)dst;
+
+ *dctx = *sctx;
+ dst->ks = &dctx->dks.ks;
+}
+
+static int cipher_hw_des_ecb_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ size_t i, bl = ctx->blocksize;
+ DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks);
+
+ if (len < bl)
+ return 1;
+ for (i = 0, len -= bl; i <= len; i += bl)
+ DES_ecb_encrypt((const_DES_cblock *)(in + i),
+ (const_DES_cblock *)(out + i), key, ctx->enc);
+ return 1;
+}
+
+static int cipher_hw_des_cbc_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_DES_CTX *dctx = (PROV_DES_CTX *)ctx;
+ DES_key_schedule *key = &(dctx->dks.ks);
+
+ if (dctx->dstream.cbc != NULL) {
+ (*dctx->dstream.cbc) (in, out, len, key, ctx->iv);
+ return 1;
+ }
+
+ while (len >= MAXCHUNK) {
+ DES_ncbc_encrypt(in, out, MAXCHUNK, key, (DES_cblock *)ctx->iv,
+ ctx->enc);
+ len -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (len > 0)
+ DES_ncbc_encrypt(in, out, (long)len, key, (DES_cblock *)ctx->iv,
+ ctx->enc);
+ return 1;
+}
+
+static int cipher_hw_des_ofb64_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ int num = ctx->num;
+ DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks);
+
+ while (len >= MAXCHUNK) {
+ DES_ofb64_encrypt(in, out, MAXCHUNK, key, (DES_cblock *)ctx->iv, &num);
+ len -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (len > 0) {
+ DES_ofb64_encrypt(in, out, (long)len, key, (DES_cblock *)ctx->iv, &num);
+ }
+ ctx->num = num;
+ return 1;
+}
+
+static int cipher_hw_des_cfb64_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ size_t chunk = MAXCHUNK;
+ DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks);
+ int num = ctx->num;
+
+ if (len < chunk)
+ chunk = len;
+ while (len > 0 && len >= chunk) {
+ DES_cfb64_encrypt(in, out, (long)chunk, key, (DES_cblock *)ctx->iv,
+ &num, ctx->enc);
+ len -= chunk;
+ in += chunk;
+ out += chunk;
+ if (len < chunk)
+ chunk = len;
+ }
+ ctx->num = num;
+ return 1;
+}
+
+/*
+ * Although we have a CFB-r implementation for DES, it doesn't pack the right
+ * way, so wrap it here
+ */
+static int cipher_hw_des_cfb1_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ size_t n, chunk = MAXCHUNK / 8;
+ DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks);
+ unsigned char c[1];
+ unsigned char d[1] = { 0 };
+
+ if (inl < chunk)
+ chunk = inl;
+
+ while (inl && inl >= chunk) {
+ for (n = 0; n < chunk * 8; ++n) {
+ c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+ DES_cfb_encrypt(c, d, 1, 1, key, (DES_cblock *)ctx->iv, ctx->enc);
+ out[n / 8] =
+ (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8))) |
+ ((d[0] & 0x80) >> (unsigned int)(n % 8));
+ }
+ inl -= chunk;
+ in += chunk;
+ out += chunk;
+ if (inl < chunk)
+ chunk = inl;
+ }
+
+ return 1;
+}
+
+static int cipher_hw_des_cfb8_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ DES_key_schedule *key = &(((PROV_DES_CTX *)ctx)->dks.ks);
+
+ while (inl >= MAXCHUNK) {
+ DES_cfb_encrypt(in, out, 8, (long)MAXCHUNK, key,
+ (DES_cblock *)ctx->iv, ctx->enc);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ DES_cfb_encrypt(in, out, 8, (long)inl, key,
+ (DES_cblock *)ctx->iv, ctx->enc);
+ return 1;
+}
+
+#define PROV_CIPHER_HW_des_mode(mode) \
+static const PROV_CIPHER_HW des_##mode = { \
+ cipher_hw_des_initkey, \
+ cipher_hw_des_##mode##_cipher, \
+ cipher_hw_des_copyctx \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_des_##mode(void) \
+{ \
+ return &des_##mode; \
+}
+
+PROV_CIPHER_HW_des_mode(ecb)
+PROV_CIPHER_HW_des_mode(cbc)
+PROV_CIPHER_HW_des_mode(ofb64)
+PROV_CIPHER_HW_des_mode(cfb64)
+PROV_CIPHER_HW_des_mode(cfb1)
+PROV_CIPHER_HW_des_mode(cfb8)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_desx.c b/crypto/openssl/providers/implementations/ciphers/cipher_desx.c
new file mode 100644
index 000000000000..41596554435e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_desx.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_tdes_default.h"
+#include "prov/implementations.h"
+
+/* desx_cbc_functions */
+IMPLEMENT_tdes_cipher(desx, DESX, cbc, CBC, TDES_FLAGS, 64*3, 64, 64, block);
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c
new file mode 100644
index 000000000000..31fd18e54ce5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_desx_hw.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/des.h>
+#include "cipher_tdes_default.h"
+
+/*
+ * Note the PROV_TDES_CTX has been used for the DESX cipher, just to reduce
+ * code size.
+ */
+#define ks1 tks.ks[0]
+#define ks2 tks.ks[1].ks[0].cblock
+#define ks3 tks.ks[2].ks[0].cblock
+
+static int cipher_hw_desx_cbc_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ DES_cblock *deskey = (DES_cblock *)key;
+
+ DES_set_key_unchecked(deskey, &tctx->ks1);
+ memcpy(&tctx->ks2, &key[8], 8);
+ memcpy(&tctx->ks3, &key[16], 8);
+
+ return 1;
+}
+
+static void cipher_hw_desx_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src)
+{
+ PROV_TDES_CTX *sctx = (PROV_TDES_CTX *)src;
+ PROV_TDES_CTX *dctx = (PROV_TDES_CTX *)dst;
+
+ *dctx = *sctx;
+ dst->ks = &dctx->tks.ks;
+}
+
+static int cipher_hw_desx_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+
+ while (inl >= MAXCHUNK) {
+ DES_xcbc_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1,
+ (DES_cblock *)ctx->iv, &tctx->ks2, &tctx->ks3,
+ ctx->enc);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ DES_xcbc_encrypt(in, out, (long)inl, &tctx->ks1,
+ (DES_cblock *)ctx->iv, &tctx->ks2, &tctx->ks3,
+ ctx->enc);
+ return 1;
+}
+
+static const PROV_CIPHER_HW desx_cbc = {
+ cipher_hw_desx_cbc_initkey,
+ cipher_hw_desx_cbc,
+ cipher_hw_desx_copyctx
+};
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_desx_cbc(void)
+{
+ return &desx_cbc;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea.c b/crypto/openssl/providers/implementations/ciphers/cipher_idea.c
new file mode 100644
index 000000000000..c69c6ac092b4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * IDEA low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+/* Dispatch functions for Idea cipher modes ecb, cbc, ofb, cfb */
+
+#include "cipher_idea.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn idea_freectx;
+static OSSL_FUNC_cipher_dupctx_fn idea_dupctx;
+
+static void idea_freectx(void *vctx)
+{
+ PROV_IDEA_CTX *ctx = (PROV_IDEA_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *idea_dupctx(void *ctx)
+{
+ PROV_IDEA_CTX *in = (PROV_IDEA_CTX *)ctx;
+ PROV_IDEA_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+/* ossl_idea128ecb_functions */
+IMPLEMENT_generic_cipher(idea, IDEA, ecb, ECB, 0, 128, 64, 0, block)
+/* ossl_idea128cbc_functions */
+IMPLEMENT_generic_cipher(idea, IDEA, cbc, CBC, 0, 128, 64, 64, block)
+/* ossl_idea128ofb64_functions */
+IMPLEMENT_generic_cipher(idea, IDEA, ofb64, OFB, 0, 128, 8, 64, stream)
+/* ossl_idea128cfb64_functions */
+IMPLEMENT_generic_cipher(idea, IDEA, cfb64, CFB, 0, 128, 8, 64, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea.h b/crypto/openssl/providers/implementations/ciphers/cipher_idea.h
new file mode 100644
index 000000000000..212efa8af575
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/idea.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_idea_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ IDEA_KEY_SCHEDULE ks;
+ } ks;
+} PROV_IDEA_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_ofb64(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_cfb64(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c
new file mode 100644
index 000000000000..1c451b77edc4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_idea_hw.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * IDEA low level APIs are deprecated for public use, but still ok for internal
+ * use where we're using them to implement the higher level EVP interface, as is
+ * the case here.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_idea.h"
+
+static int cipher_hw_idea_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_IDEA_CTX *ictx = (PROV_IDEA_CTX *)ctx;
+ IDEA_KEY_SCHEDULE *ks = &(ictx->ks.ks);
+
+ if (ctx->enc
+ || ctx->mode == EVP_CIPH_OFB_MODE
+ || ctx->mode == EVP_CIPH_CFB_MODE) {
+ IDEA_set_encrypt_key(key, ks);
+ } else {
+ IDEA_KEY_SCHEDULE tmp;
+
+ IDEA_set_encrypt_key(key, &tmp);
+ IDEA_set_decrypt_key(&tmp, ks);
+ OPENSSL_cleanse((unsigned char *)&tmp, sizeof(IDEA_KEY_SCHEDULE));
+ }
+ return 1;
+}
+
+# define PROV_CIPHER_HW_idea_mode_ex(mode, UCMODE, fname) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, idea, PROV_IDEA_CTX, IDEA_KEY_SCHEDULE, \
+ fname) \
+static const PROV_CIPHER_HW idea_##mode = { \
+ cipher_hw_idea_initkey, \
+ cipher_hw_idea_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_idea_##mode(size_t keybits) \
+{ \
+ return &idea_##mode; \
+}
+
+# define PROV_CIPHER_HW_idea_mode(mode, UCMODE) \
+ PROV_CIPHER_HW_idea_mode_ex(mode, UCMODE, IDEA_##mode)
+
+PROV_CIPHER_HW_idea_mode(cbc, CBC)
+PROV_CIPHER_HW_idea_mode(ofb64, OFB)
+PROV_CIPHER_HW_idea_mode(cfb64, CFB)
+/*
+ * IDEA_ecb_encrypt() does not have a enc parameter - so we create a macro
+ * that ignores this parameter when IMPLEMENT_CIPHER_HW_ecb() is called.
+ */
+#define IDEA2_ecb_encrypt(in, out, ks, enc) IDEA_ecb_encrypt(in, out, ks)
+
+PROV_CIPHER_HW_idea_mode_ex(ecb, ECB, IDEA2_ecb)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_null.c b/crypto/openssl/providers/implementations/ciphers/cipher_null.c
new file mode 100644
index 000000000000..7e934093ce5a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_null.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/ciphercommon.h"
+#include "prov/providercommon.h"
+
+typedef struct prov_cipher_null_ctx_st {
+ int enc;
+ size_t tlsmacsize;
+ const unsigned char *tlsmac;
+} PROV_CIPHER_NULL_CTX;
+
+static OSSL_FUNC_cipher_newctx_fn null_newctx;
+static void *null_newctx(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ return OPENSSL_zalloc(sizeof(PROV_CIPHER_NULL_CTX));
+}
+
+static OSSL_FUNC_cipher_freectx_fn null_freectx;
+static void null_freectx(void *vctx)
+{
+ OPENSSL_free(vctx);
+}
+
+static OSSL_FUNC_cipher_encrypt_init_fn null_einit;
+static int null_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = 1;
+ return 1;
+}
+
+static OSSL_FUNC_cipher_decrypt_init_fn null_dinit;
+static int null_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_prov_is_running())
+ return 0;
+
+ return 1;
+}
+
+static OSSL_FUNC_cipher_cipher_fn null_cipher;
+static int null_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ctx->enc && ctx->tlsmacsize > 0) {
+ /*
+ * TLS NULL cipher as per:
+ * https://tools.ietf.org/html/rfc5246#section-6.2.3.1
+ */
+ if (inl < ctx->tlsmacsize)
+ return 0;
+ ctx->tlsmac = in + inl - ctx->tlsmacsize;
+ inl -= ctx->tlsmacsize;
+ }
+ if (outsize < inl)
+ return 0;
+ if (out != NULL && in != out)
+ memcpy(out, in, inl);
+ *outl = inl;
+ return 1;
+}
+
+static OSSL_FUNC_cipher_final_fn null_final;
+static int null_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+static OSSL_FUNC_cipher_get_params_fn null_get_params;
+static int null_get_params(OSSL_PARAM params[])
+{
+ return ossl_cipher_generic_get_params(params, 0, 0, 0, 8, 0);
+}
+
+static const OSSL_PARAM null_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED },
+ OSSL_PARAM_END
+};
+
+static OSSL_FUNC_cipher_gettable_ctx_params_fn null_gettable_ctx_params;
+static const OSSL_PARAM *null_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return null_known_gettable_ctx_params;
+}
+
+static OSSL_FUNC_cipher_get_ctx_params_fn null_get_ctx_params;
+static int null_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM null_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL),
+ OSSL_PARAM_END
+};
+
+static OSSL_FUNC_cipher_settable_ctx_params_fn null_settable_ctx_params;
+static const OSSL_PARAM *null_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return null_known_settable_ctx_params;
+}
+
+
+static OSSL_FUNC_cipher_set_ctx_params_fn null_set_ctx_params;
+static int null_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_NULL_CTX *ctx = (PROV_CIPHER_NULL_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_null_functions[] = {
+ { OSSL_FUNC_CIPHER_NEWCTX,
+ (void (*)(void)) null_newctx },
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) null_freectx },
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) null_newctx },
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))null_einit },
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))null_dinit },
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))null_cipher },
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))null_final },
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))null_cipher },
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void)) null_get_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,
+ (void (*)(void))ossl_cipher_generic_gettable_params },
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, (void (*)(void))null_get_ctx_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))null_gettable_ctx_params },
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, (void (*)(void))null_set_ctx_params },
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))null_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c
new file mode 100644
index 000000000000..c535bd7e9724
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for RC2 cipher modes ecb, cbc, ofb, cfb */
+
+/*
+ * RC2 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_rc2.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define RC2_40_MAGIC 0xa0
+#define RC2_64_MAGIC 0x78
+#define RC2_128_MAGIC 0x3a
+#define RC2_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH
+
+static OSSL_FUNC_cipher_encrypt_init_fn rc2_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn rc2_dinit;
+static OSSL_FUNC_cipher_freectx_fn rc2_freectx;
+static OSSL_FUNC_cipher_dupctx_fn rc2_dupctx;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn rc2_gettable_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn rc2_settable_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn rc2_set_ctx_params;
+
+static void rc2_freectx(void *vctx)
+{
+ PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *rc2_dupctx(void *ctx)
+{
+ PROV_RC2_CTX *in = (PROV_RC2_CTX *)ctx;
+ PROV_RC2_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+static int rc2_keybits_to_magic(int keybits)
+{
+ switch (keybits) {
+ case 128:
+ return RC2_128_MAGIC;
+ case 64:
+ return RC2_64_MAGIC;
+ case 40:
+ return RC2_40_MAGIC;
+ }
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE);
+ return 0;
+}
+
+static int rc2_magic_to_keybits(int magic)
+{
+ switch (magic) {
+ case RC2_128_MAGIC:
+ return 128;
+ case RC2_64_MAGIC:
+ return 64;
+ case RC2_40_MAGIC:
+ return 40;
+ }
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_KEY_SIZE);
+ return 0;
+}
+
+static int rc2_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc2_set_ctx_params(ctx, params);
+}
+
+static int rc2_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc2_set_ctx_params(ctx, params);
+}
+
+static int rc2_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx;
+ OSSL_PARAM *p, *p1, *p2;
+
+ if (!ossl_cipher_generic_get_ctx_params(vctx, params))
+ return 0;
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RC2_KEYBITS);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->key_bits)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p1 = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS);
+ p2 = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS_OLD);
+ if (p1 != NULL || p2 != NULL) {
+ long num;
+ int i;
+ ASN1_TYPE *type;
+ unsigned char *d1 = (p1 == NULL) ? NULL : p1->data;
+ unsigned char *d2 = (p2 == NULL) ? NULL : p2->data;
+ unsigned char **dd1 = d1 == NULL ? NULL : &d1;
+ unsigned char **dd2 = d2 == NULL ? NULL : &d2;
+
+ if ((p1 != NULL && p1->data_type != OSSL_PARAM_OCTET_STRING)
+ || (p2 != NULL && p2->data_type != OSSL_PARAM_OCTET_STRING)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ if ((type = ASN1_TYPE_new()) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ /* Is this the original IV or the running IV? */
+ num = rc2_keybits_to_magic(ctx->key_bits);
+ if (!ASN1_TYPE_set_int_octetstring(type, num,
+ ctx->base.iv, ctx->base.ivlen)) {
+ ASN1_TYPE_free(type);
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ /*
+ * IF the caller has a buffer, we pray to the gods they got the
+ * size right. There's no way to tell the i2d functions...
+ */
+ i = i2d_ASN1_TYPE(type, dd1);
+ if (p1 != NULL && i >= 0)
+ p1->return_size = (size_t)i;
+
+ /*
+ * If the buffers differ, redo the i2d on the second buffer.
+ * Otherwise, just use |i| as computed above
+ */
+ if (d1 != d2)
+ i = i2d_ASN1_TYPE(type, dd2);
+ if (p2 != NULL && i >= 0)
+ p2->return_size = (size_t)i;
+
+ ASN1_TYPE_free(type);
+ if (i < 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int rc2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_RC2_CTX *ctx = (PROV_RC2_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_cipher_var_keylen_set_ctx_params(vctx, params))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_RC2_KEYBITS);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &ctx->key_bits)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS);
+ if (p != NULL) {
+ ASN1_TYPE *type = NULL;
+ long num = 0;
+ const unsigned char *d = p->data;
+ int ret = 1;
+ unsigned char iv[16];
+
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || ctx->base.ivlen > sizeof(iv)
+ || (type = d2i_ASN1_TYPE(NULL, &d, p->data_size)) == NULL
+ || ((size_t)ASN1_TYPE_get_int_octetstring(type, &num, iv,
+ ctx->base.ivlen)
+ != ctx->base.ivlen)
+ || !ossl_cipher_generic_initiv(&ctx->base, iv, ctx->base.ivlen)
+ || (ctx->key_bits = rc2_magic_to_keybits(num)) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ ret = 0;
+ }
+ ASN1_TYPE_free(type);
+ if (ret == 0)
+ return 0;
+ /*
+ * This code assumes that the caller will call
+ * EVP_CipherInit_ex() with a non NULL key in order to setup a key that
+ * uses the keylen and keybits that were set here.
+ */
+ ctx->base.keylen = ctx->key_bits / 8;
+ }
+ return 1;
+}
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(rc2)
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, NULL),
+OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS, NULL, 0),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(rc2)
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(rc2)
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_RC2_KEYBITS, NULL),
+OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_ALGORITHM_ID_PARAMS, NULL, 0),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(rc2)
+
+#define IMPLEMENT_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, blkbits, \
+ ivbits, typ) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \
+static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \
+static void *alg##_##kbits##_##lcmode##_newctx(void *provctx) \
+{ \
+ PROV_##UCALG##_CTX *ctx; \
+ if (!ossl_prov_is_running()) \
+ return NULL; \
+ ctx = OPENSSL_zalloc(sizeof(*ctx)); \
+ if (ctx != NULL) { \
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags, \
+ ossl_prov_cipher_hw_##alg##_##lcmode(kbits), \
+ NULL); \
+ ctx->key_bits = kbits; \
+ } \
+ return ctx; \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc2_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc2_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))rc2_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))rc2_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))rc2_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))rc2_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+/* ossl_rc2128ecb_functions */
+IMPLEMENT_cipher(rc2, RC2, ecb, ECB, RC2_FLAGS, 128, 64, 0, block)
+/* ossl_rc2128cbc_functions */
+IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 128, 64, 64, block)
+/* ossl_rc240cbc_functions */
+IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 40, 64, 64, block)
+/* ossl_rc264cbc_functions */
+IMPLEMENT_cipher(rc2, RC2, cbc, CBC, RC2_FLAGS, 64, 64, 64, block)
+
+/* ossl_rc2128ofb128_functions */
+IMPLEMENT_cipher(rc2, RC2, ofb128, OFB, RC2_FLAGS, 128, 8, 64, stream)
+/* ossl_rc2128cfb128_functions */
+IMPLEMENT_cipher(rc2, RC2, cfb128, CFB, RC2_FLAGS, 128, 8, 64, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h
new file mode 100644
index 000000000000..7a4bea5ac404
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/rc2.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_rc2_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ RC2_KEY ks;
+ } ks;
+ size_t key_bits;
+} PROV_RC2_CTX;
+
+#define ossl_prov_cipher_hw_rc2_ofb128 ossl_prov_cipher_hw_rc2_ofb64
+#define ossl_prov_cipher_hw_rc2_cfb128 ossl_prov_cipher_hw_rc2_cfb64
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_ofb64(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_cfb64(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c
new file mode 100644
index 000000000000..da9ff729cda0
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc2_hw.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RC2 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_rc2.h"
+
+static int cipher_hw_rc2_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_RC2_CTX *rctx = (PROV_RC2_CTX *)ctx;
+ RC2_KEY *ks = &(rctx->ks.ks);
+
+ RC2_set_key(ks, (int)ctx->keylen, key, (int)rctx->key_bits);
+ return 1;
+}
+
+# define PROV_CIPHER_HW_rc2_mode(mode, UCMODE) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, rc2, PROV_RC2_CTX, RC2_KEY, \
+ RC2_##mode) \
+static const PROV_CIPHER_HW rc2_##mode = { \
+ cipher_hw_rc2_initkey, \
+ cipher_hw_rc2_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc2_##mode(size_t keybits) \
+{ \
+ return &rc2_##mode; \
+}
+
+PROV_CIPHER_HW_rc2_mode(cbc, CBC)
+PROV_CIPHER_HW_rc2_mode(ecb, ECB)
+PROV_CIPHER_HW_rc2_mode(ofb64, OFB)
+PROV_CIPHER_HW_rc2_mode(cfb64, CFB)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c
new file mode 100644
index 000000000000..733524d36f5a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for RC4 ciphers */
+
+/*
+ * RC4 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_rc4.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define RC4_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH
+
+static OSSL_FUNC_cipher_encrypt_init_fn rc4_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn rc4_dinit;
+static OSSL_FUNC_cipher_freectx_fn rc4_freectx;
+static OSSL_FUNC_cipher_dupctx_fn rc4_dupctx;
+
+static void rc4_freectx(void *vctx)
+{
+ PROV_RC4_CTX *ctx = (PROV_RC4_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *rc4_dupctx(void *ctx)
+{
+ PROV_RC4_CTX *in = (PROV_RC4_CTX *)ctx;
+ PROV_RC4_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+static int rc4_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return ossl_cipher_var_keylen_set_ctx_params(ctx, params);
+}
+
+static int rc4_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return ossl_cipher_var_keylen_set_ctx_params(ctx, params);
+}
+
+#define IMPLEMENT_cipher(alg, UCALG, flags, kbits, blkbits, ivbits, typ) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_get_params; \
+static int alg##_##kbits##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, 0, flags, \
+ kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_newctx; \
+static void *alg##_##kbits##_newctx(void *provctx) \
+{ \
+ PROV_##UCALG##_CTX *ctx; \
+ if (!ossl_prov_is_running()) \
+ return NULL; \
+ ctx = OPENSSL_zalloc(sizeof(*ctx)); \
+ if (ctx != NULL) { \
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, 0, flags, \
+ ossl_prov_cipher_hw_##alg(kbits), NULL); \
+ } \
+ return ctx; \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc4_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc4_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_get_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_var_keylen_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_var_keylen_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+/* ossl_rc440_functions */
+IMPLEMENT_cipher(rc4, RC4, RC4_FLAGS, 40, 8, 0, stream)
+/* ossl_rc4128_functions */
+IMPLEMENT_cipher(rc4, RC4, RC4_FLAGS, 128, 8, 0, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h
new file mode 100644
index 000000000000..40d822ceb2ef
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/rc4.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_rc4_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ RC4_KEY ks;
+ } ks;
+} PROV_RC4_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c
new file mode 100644
index 000000000000..ec18777143bb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for RC4_HMAC_MD5 cipher */
+
+/*
+ * MD5 and RC4 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_rc4_hmac_md5.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define RC4_HMAC_MD5_FLAGS (PROV_CIPHER_FLAG_VARIABLE_LENGTH \
+ | PROV_CIPHER_FLAG_AEAD)
+
+#define RC4_HMAC_MD5_KEY_BITS (16 * 8)
+#define RC4_HMAC_MD5_BLOCK_BITS (1 * 8)
+#define RC4_HMAC_MD5_IV_BITS 0
+#define RC4_HMAC_MD5_MODE 0
+
+#define GET_HW(ctx) ((PROV_CIPHER_HW_RC4_HMAC_MD5 *)ctx->base.hw)
+
+static OSSL_FUNC_cipher_encrypt_init_fn rc4_hmac_md5_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn rc4_hmac_md5_dinit;
+static OSSL_FUNC_cipher_newctx_fn rc4_hmac_md5_newctx;
+static OSSL_FUNC_cipher_freectx_fn rc4_hmac_md5_freectx;
+static OSSL_FUNC_cipher_dupctx_fn rc4_hmac_md5_dupctx;
+static OSSL_FUNC_cipher_get_ctx_params_fn rc4_hmac_md5_get_ctx_params;
+static OSSL_FUNC_cipher_gettable_ctx_params_fn rc4_hmac_md5_gettable_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn rc4_hmac_md5_set_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn rc4_hmac_md5_settable_ctx_params;
+static OSSL_FUNC_cipher_get_params_fn rc4_hmac_md5_get_params;
+#define rc4_hmac_md5_gettable_params ossl_cipher_generic_gettable_params
+#define rc4_hmac_md5_update ossl_cipher_generic_stream_update
+#define rc4_hmac_md5_final ossl_cipher_generic_stream_final
+#define rc4_hmac_md5_cipher ossl_cipher_generic_cipher
+
+static void *rc4_hmac_md5_newctx(void *provctx)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_cipher_generic_initkey(ctx, RC4_HMAC_MD5_KEY_BITS,
+ RC4_HMAC_MD5_BLOCK_BITS,
+ RC4_HMAC_MD5_IV_BITS,
+ RC4_HMAC_MD5_MODE, RC4_HMAC_MD5_FLAGS,
+ ossl_prov_cipher_hw_rc4_hmac_md5(
+ RC4_HMAC_MD5_KEY_BITS
+ ), NULL);
+ return ctx;
+}
+
+static void rc4_hmac_md5_freectx(void *vctx)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *rc4_hmac_md5_dupctx(void *vctx)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = vctx;
+
+ if (ctx == NULL)
+ return NULL;
+ return OPENSSL_memdup(ctx, sizeof(*ctx));
+}
+
+static int rc4_hmac_md5_einit(void *ctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc4_hmac_md5_set_ctx_params(ctx, params);
+}
+
+static int rc4_hmac_md5_dinit(void *ctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc4_hmac_md5_set_ctx_params(ctx, params);
+}
+
+static const OSSL_PARAM rc4_hmac_md5_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *rc4_hmac_md5_gettable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return rc4_hmac_md5_known_gettable_ctx_params;
+}
+
+static int rc4_hmac_md5_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->base.ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM rc4_hmac_md5_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *rc4_hmac_md5_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return rc4_hmac_md5_known_settable_ctx_params;
+}
+
+static int rc4_hmac_md5_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t sz;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->base.keylen != sz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->base.ivlen != sz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ sz = GET_HW(ctx)->tls_init(&ctx->base, p->data, p->data_size);
+ if (sz == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
+ return 0;
+ }
+ ctx->tls_aad_pad_sz = sz;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_MAC_KEY);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ GET_HW(ctx)->init_mackey(&ctx->base, p->data, p->data_size);
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &ctx->base.tlsversion)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int rc4_hmac_md5_get_params(OSSL_PARAM params[])
+{
+ return ossl_cipher_generic_get_params(params, RC4_HMAC_MD5_MODE,
+ RC4_HMAC_MD5_FLAGS,
+ RC4_HMAC_MD5_KEY_BITS,
+ RC4_HMAC_MD5_BLOCK_BITS,
+ RC4_HMAC_MD5_IV_BITS);
+}
+
+const OSSL_DISPATCH ossl_rc4_hmac_ossl_md5_functions[] = {
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))rc4_hmac_md5_newctx },
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))rc4_hmac_md5_freectx },
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))rc4_hmac_md5_dupctx },
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc4_hmac_md5_einit },
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc4_hmac_md5_dinit },
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))rc4_hmac_md5_update },
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))rc4_hmac_md5_final },
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))rc4_hmac_md5_cipher },
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))rc4_hmac_md5_get_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS,
+ (void (*)(void))rc4_hmac_md5_gettable_params },
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS,
+ (void (*)(void))rc4_hmac_md5_get_ctx_params },
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS,
+ (void (*)(void))rc4_hmac_md5_gettable_ctx_params },
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS,
+ (void (*)(void))rc4_hmac_md5_set_ctx_params },
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))rc4_hmac_md5_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h
new file mode 100644
index 000000000000..4a1d154a7ceb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/rc4.h>
+#include <openssl/md5.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_rc4_hmac_md5_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ RC4_KEY ks;
+ } ks;
+ MD5_CTX head, tail, md;
+ size_t payload_length;
+ size_t tls_aad_pad_sz;
+} PROV_RC4_HMAC_MD5_CTX;
+
+typedef struct prov_cipher_hw_rc4_hmac_md5_st {
+ PROV_CIPHER_HW base; /* Must be first */
+ int (*tls_init)(PROV_CIPHER_CTX *ctx, unsigned char *aad, size_t aad_len);
+ void (*init_mackey)(PROV_CIPHER_CTX *ctx, const unsigned char *key,
+ size_t len);
+
+} PROV_CIPHER_HW_RC4_HMAC_MD5;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4_hmac_md5(size_t keybits);
+
+void rc4_md5_enc(RC4_KEY *key, const void *in0, void *out,
+ MD5_CTX *ctx, const void *inp, size_t blocks);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c
new file mode 100644
index 000000000000..8cce02b1c5af
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hmac_md5_hw.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* RC4_HMAC_MD5 cipher implementation */
+
+/*
+ * MD5 and RC4 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_rc4_hmac_md5.h"
+
+#define NO_PAYLOAD_LENGTH ((size_t)-1)
+
+#if defined(RC4_ASM) \
+ && defined(MD5_ASM) \
+ && (defined(__x86_64) \
+ || defined(__x86_64__) \
+ || defined(_M_AMD64) \
+ || defined(_M_X64))
+# define STITCHED_CALL
+# define MOD 32 /* 32 is $MOD from rc4_md5-x86_64.pl */
+#else
+# define rc4_off 0
+# define md5_off 0
+#endif
+
+static int cipher_hw_rc4_hmac_md5_initkey(PROV_CIPHER_CTX *bctx,
+ const uint8_t *key, size_t keylen)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx;
+
+ RC4_set_key(&ctx->ks.ks, keylen, key);
+ MD5_Init(&ctx->head); /* handy when benchmarking */
+ ctx->tail = ctx->head;
+ ctx->md = ctx->head;
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+ bctx->removetlsfixed = MD5_DIGEST_LENGTH;
+ return 1;
+}
+
+static int cipher_hw_rc4_hmac_md5_cipher(PROV_CIPHER_CTX *bctx,
+ unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx;
+ RC4_KEY *ks = &ctx->ks.ks;
+
+#if defined(STITCHED_CALL)
+ size_t rc4_off = MOD - 1 - (ks->x & (MOD - 1));
+ size_t md5_off = MD5_CBLOCK - ctx->md.num, blocks;
+ unsigned int l;
+#endif
+ size_t plen = ctx->payload_length;
+
+ if (plen != NO_PAYLOAD_LENGTH && len != (plen + MD5_DIGEST_LENGTH))
+ return 0;
+
+ if (ctx->base.enc) {
+ if (plen == NO_PAYLOAD_LENGTH)
+ plen = len;
+#if defined(STITCHED_CALL)
+ /* cipher has to "fall behind" */
+ if (rc4_off > md5_off)
+ md5_off += MD5_CBLOCK;
+
+ if (plen > md5_off
+ && (blocks = (plen - md5_off) / MD5_CBLOCK)
+ && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) {
+ MD5_Update(&ctx->md, in, md5_off);
+ RC4(ks, rc4_off, in, out);
+
+ rc4_md5_enc(ks, in + rc4_off, out + rc4_off,
+ &ctx->md, in + md5_off, blocks);
+ blocks *= MD5_CBLOCK;
+ rc4_off += blocks;
+ md5_off += blocks;
+ ctx->md.Nh += blocks >> 29;
+ ctx->md.Nl += blocks <<= 3;
+ if (ctx->md.Nl < (unsigned int)blocks)
+ ctx->md.Nh++;
+ } else {
+ rc4_off = 0;
+ md5_off = 0;
+ }
+#endif
+ MD5_Update(&ctx->md, in + md5_off, plen - md5_off);
+
+ if (plen != len) { /* "TLS" mode of operation */
+ if (in != out)
+ memcpy(out + rc4_off, in + rc4_off, plen - rc4_off);
+
+ /* calculate HMAC and append it to payload */
+ MD5_Final(out + plen, &ctx->md);
+ ctx->md = ctx->tail;
+ MD5_Update(&ctx->md, out + plen, MD5_DIGEST_LENGTH);
+ MD5_Final(out + plen, &ctx->md);
+ /* encrypt HMAC at once */
+ RC4(ks, len - rc4_off, out + rc4_off, out + rc4_off);
+ } else {
+ RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off);
+ }
+ } else {
+ unsigned char mac[MD5_DIGEST_LENGTH];
+
+#if defined(STITCHED_CALL)
+ /* digest has to "fall behind" */
+ if (md5_off > rc4_off)
+ rc4_off += 2 * MD5_CBLOCK;
+ else
+ rc4_off += MD5_CBLOCK;
+
+ if (len > rc4_off
+ && (blocks = (len - rc4_off) / MD5_CBLOCK)
+ && (OPENSSL_ia32cap_P[0] & (1 << 20)) == 0) {
+ RC4(ks, rc4_off, in, out);
+ MD5_Update(&ctx->md, out, md5_off);
+
+ rc4_md5_enc(ks, in + rc4_off, out + rc4_off,
+ &ctx->md, out + md5_off, blocks);
+ blocks *= MD5_CBLOCK;
+ rc4_off += blocks;
+ md5_off += blocks;
+ l = (ctx->md.Nl + (blocks << 3)) & 0xffffffffU;
+ if (l < ctx->md.Nl)
+ ctx->md.Nh++;
+ ctx->md.Nl = l;
+ ctx->md.Nh += blocks >> 29;
+ } else {
+ md5_off = 0;
+ rc4_off = 0;
+ }
+#endif
+ /* decrypt HMAC at once */
+ RC4(ks, len - rc4_off, in + rc4_off, out + rc4_off);
+ if (plen != NO_PAYLOAD_LENGTH) {
+ /* "TLS" mode of operation */
+ MD5_Update(&ctx->md, out + md5_off, plen - md5_off);
+
+ /* calculate HMAC and verify it */
+ MD5_Final(mac, &ctx->md);
+ ctx->md = ctx->tail;
+ MD5_Update(&ctx->md, mac, MD5_DIGEST_LENGTH);
+ MD5_Final(mac, &ctx->md);
+
+ if (CRYPTO_memcmp(out + plen, mac, MD5_DIGEST_LENGTH))
+ return 0;
+ } else {
+ MD5_Update(&ctx->md, out + md5_off, len - md5_off);
+ }
+ }
+
+ ctx->payload_length = NO_PAYLOAD_LENGTH;
+
+ return 1;
+}
+
+static int cipher_hw_rc4_hmac_md5_tls_init(PROV_CIPHER_CTX *bctx,
+ unsigned char *aad, size_t aad_len)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx;
+ unsigned int len;
+
+ if (aad_len != EVP_AEAD_TLS1_AAD_LEN)
+ return 0;
+
+ len = aad[aad_len - 2] << 8 | aad[aad_len - 1];
+
+ if (!bctx->enc) {
+ if (len < MD5_DIGEST_LENGTH)
+ return 0;
+ len -= MD5_DIGEST_LENGTH;
+ aad[aad_len - 2] = len >> 8;
+ aad[aad_len - 1] = len;
+ }
+ ctx->payload_length = len;
+ ctx->md = ctx->head;
+ MD5_Update(&ctx->md, aad, aad_len);
+
+ return MD5_DIGEST_LENGTH;
+}
+
+static void cipher_hw_rc4_hmac_md5_init_mackey(PROV_CIPHER_CTX *bctx,
+ const unsigned char *key,
+ size_t len)
+{
+ PROV_RC4_HMAC_MD5_CTX *ctx = (PROV_RC4_HMAC_MD5_CTX *)bctx;
+ unsigned int i;
+ unsigned char hmac_key[64];
+
+ memset(hmac_key, 0, sizeof(hmac_key));
+
+ if (len > (int)sizeof(hmac_key)) {
+ MD5_Init(&ctx->head);
+ MD5_Update(&ctx->head, key, len);
+ MD5_Final(hmac_key, &ctx->head);
+ } else {
+ memcpy(hmac_key, key, len);
+ }
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36; /* ipad */
+ MD5_Init(&ctx->head);
+ MD5_Update(&ctx->head, hmac_key, sizeof(hmac_key));
+
+ for (i = 0; i < sizeof(hmac_key); i++)
+ hmac_key[i] ^= 0x36 ^ 0x5c; /* opad */
+ MD5_Init(&ctx->tail);
+ MD5_Update(&ctx->tail, hmac_key, sizeof(hmac_key));
+
+ OPENSSL_cleanse(hmac_key, sizeof(hmac_key));
+}
+
+static const PROV_CIPHER_HW_RC4_HMAC_MD5 rc4_hmac_md5_hw = {
+ {
+ cipher_hw_rc4_hmac_md5_initkey,
+ cipher_hw_rc4_hmac_md5_cipher
+ },
+ cipher_hw_rc4_hmac_md5_tls_init,
+ cipher_hw_rc4_hmac_md5_init_mackey
+};
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4_hmac_md5(size_t keybits)
+{
+ return (PROV_CIPHER_HW *)&rc4_hmac_md5_hw;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c
new file mode 100644
index 000000000000..09192b5d5e14
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc4_hw.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RC4 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_rc4.h"
+
+static int cipher_hw_rc4_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_RC4_CTX *rctx = (PROV_RC4_CTX *)ctx;
+
+ RC4_set_key(&rctx->ks.ks, keylen, key);
+ return 1;
+}
+
+static int cipher_hw_rc4_cipher(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ PROV_RC4_CTX *rctx = (PROV_RC4_CTX *)ctx;
+
+ RC4(&rctx->ks.ks, len, in, out);
+ return 1;
+}
+
+static const PROV_CIPHER_HW rc4_hw = {
+ cipher_hw_rc4_initkey,
+ cipher_hw_rc4_cipher
+};
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc4(size_t keybits)
+{
+ return &rc4_hw;
+}
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c
new file mode 100644
index 000000000000..6fa491d83ef3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for RC5 cipher modes ecb, cbc, ofb, cfb */
+
+/*
+ * RC5 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/proverr.h>
+#include "cipher_rc5.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define RC5_FLAGS PROV_CIPHER_FLAG_VARIABLE_LENGTH
+
+static OSSL_FUNC_cipher_encrypt_init_fn rc5_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn rc5_dinit;
+static OSSL_FUNC_cipher_freectx_fn rc5_freectx;
+static OSSL_FUNC_cipher_dupctx_fn rc5_dupctx;
+OSSL_FUNC_cipher_gettable_ctx_params_fn rc5_gettable_ctx_params;
+OSSL_FUNC_cipher_settable_ctx_params_fn rc5_settable_ctx_params;
+static OSSL_FUNC_cipher_set_ctx_params_fn rc5_set_ctx_params;
+
+static void rc5_freectx(void *vctx)
+{
+ PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *rc5_dupctx(void *ctx)
+{
+ PROV_RC5_CTX *in = (PROV_RC5_CTX *)ctx;
+ PROV_RC5_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+static int rc5_einit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_einit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc5_set_ctx_params(ctx, params);
+}
+
+static int rc5_dinit(void *ctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ if (!ossl_cipher_generic_dinit(ctx, key, keylen, iv, ivlen, NULL))
+ return 0;
+ return rc5_set_ctx_params(ctx, params);
+}
+
+static int rc5_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_cipher_var_keylen_set_ctx_params(vctx, params))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_ROUNDS);
+ if (p != NULL) {
+ unsigned int rounds;
+
+ if (!OSSL_PARAM_get_uint(p, &rounds)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (rounds != RC5_8_ROUNDS
+ && rounds != RC5_12_ROUNDS
+ && rounds != RC5_16_ROUNDS) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_NUMBER_OF_ROUNDS);
+ return 0;
+ }
+ ctx->rounds = rounds;
+ }
+ return 1;
+}
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(rc5)
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_ROUNDS, NULL),
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(rc5)
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(rc5)
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_ROUNDS, NULL),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(rc5)
+
+
+static int rc5_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_RC5_CTX *ctx = (PROV_RC5_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (!ossl_cipher_generic_get_ctx_params(vctx, params))
+ return 0;
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_ROUNDS);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->rounds)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+#define IMPLEMENT_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \
+static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \
+static void *alg##_##kbits##_##lcmode##_newctx(void *provctx) \
+{ \
+ PROV_##UCALG##_CTX *ctx; \
+ if (!ossl_prov_is_running()) \
+ return NULL; \
+ ctx = OPENSSL_zalloc(sizeof(*ctx)); \
+ if (ctx != NULL) { \
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags, \
+ ossl_prov_cipher_hw_##alg##_##lcmode(kbits),\
+ NULL); \
+ ctx->rounds = RC5_12_ROUNDS; \
+ } \
+ return ctx; \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))rc5_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))rc5_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))rc5_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))rc5_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))rc5_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))rc5_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+};
+
+/* ossl_rc5128ecb_functions */
+IMPLEMENT_cipher(rc5, RC5, ecb, ECB, RC5_FLAGS, 128, 64, 0, block)
+/* ossl_rc5128cbc_functions */
+IMPLEMENT_cipher(rc5, RC5, cbc, CBC, RC5_FLAGS, 128, 64, 64, block)
+/* ossl_rc5128ofb64_functions */
+IMPLEMENT_cipher(rc5, RC5, ofb64, OFB, RC5_FLAGS, 128, 8, 64, stream)
+/* ossl_rc5128cfb64_functions */
+IMPLEMENT_cipher(rc5, RC5, cfb64, CFB, RC5_FLAGS, 128, 8, 64, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h
new file mode 100644
index 000000000000..421632f2ea1e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/rc5.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_rc5_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ RC5_32_KEY ks; /* key schedule */
+ } ks;
+ unsigned int rounds; /* number of rounds */
+} PROV_RC5_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_ofb64(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_cfb64(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c
new file mode 100644
index 000000000000..898bd383f95a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_rc5_hw.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RC5 low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_rc5.h"
+
+static int cipher_hw_rc5_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_RC5_CTX *rctx = (PROV_RC5_CTX *)ctx;
+
+ return RC5_32_set_key(&rctx->ks.ks, keylen, key, rctx->rounds);
+}
+
+# define PROV_CIPHER_HW_rc5_mode(mode, UCMODE) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, rc5, PROV_RC5_CTX, RC5_32_KEY, \
+ RC5_32_##mode) \
+static const PROV_CIPHER_HW rc5_##mode = { \
+ cipher_hw_rc5_initkey, \
+ cipher_hw_rc5_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_rc5_##mode(size_t keybits) \
+{ \
+ return &rc5_##mode; \
+}
+
+PROV_CIPHER_HW_rc5_mode(cbc, CBC)
+PROV_CIPHER_HW_rc5_mode(ecb, ECB)
+PROV_CIPHER_HW_rc5_mode(ofb64, OFB)
+PROV_CIPHER_HW_rc5_mode(cfb64, CFB)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed.c b/crypto/openssl/providers/implementations/ciphers/cipher_seed.c
new file mode 100644
index 000000000000..3644cb5e2252
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for Seed cipher modes ecb, cbc, ofb, cfb */
+
+/*
+ * SEED low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_seed.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn seed_freectx;
+static OSSL_FUNC_cipher_dupctx_fn seed_dupctx;
+
+static void seed_freectx(void *vctx)
+{
+ PROV_SEED_CTX *ctx = (PROV_SEED_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *seed_dupctx(void *ctx)
+{
+ PROV_SEED_CTX *in = (PROV_SEED_CTX *)ctx;
+ PROV_SEED_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ *ret = *in;
+
+ return ret;
+}
+
+/* ossl_seed128ecb_functions */
+IMPLEMENT_generic_cipher(seed, SEED, ecb, ECB, 0, 128, 128, 0, block)
+/* ossl_seed128cbc_functions */
+IMPLEMENT_generic_cipher(seed, SEED, cbc, CBC, 0, 128, 128, 128, block)
+/* ossl_seed128ofb128_functions */
+IMPLEMENT_generic_cipher(seed, SEED, ofb128, OFB, 0, 128, 8, 128, stream)
+/* ossl_seed128cfb128_functions */
+IMPLEMENT_generic_cipher(seed, SEED, cfb128, CFB, 0, 128, 8, 128, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed.h b/crypto/openssl/providers/implementations/ciphers/cipher_seed.h
new file mode 100644
index 000000000000..9006a9183b55
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/seed.h>
+#include "prov/ciphercommon.h"
+
+typedef struct prov_seed_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ SEED_KEY_SCHEDULE ks;
+ } ks;
+} PROV_SEED_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_ofb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_cfb128(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c
new file mode 100644
index 000000000000..2d1dba92bc73
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_seed_hw.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * SEED low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_seed.h"
+
+static int cipher_hw_seed_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_SEED_CTX *sctx = (PROV_SEED_CTX *)ctx;
+
+ SEED_set_key(key, &(sctx->ks.ks));
+ return 1;
+}
+
+# define PROV_CIPHER_HW_seed_mode(mode, UCMODE) \
+IMPLEMENT_CIPHER_HW_##UCMODE(mode, seed, PROV_SEED_CTX, SEED_KEY_SCHEDULE, \
+ SEED_##mode) \
+static const PROV_CIPHER_HW seed_##mode = { \
+ cipher_hw_seed_initkey, \
+ cipher_hw_seed_##mode##_cipher \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_seed_##mode(size_t keybits) \
+{ \
+ return &seed_##mode; \
+}
+
+PROV_CIPHER_HW_seed_mode(cbc, CBC)
+PROV_CIPHER_HW_seed_mode(ecb, ECB)
+PROV_CIPHER_HW_seed_mode(ofb128, OFB)
+PROV_CIPHER_HW_seed_mode(cfb128, CFB)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c
new file mode 100644
index 000000000000..863c9997f5aa
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for cast cipher modes ecb, cbc, ofb, cfb */
+
+#include "cipher_sm4.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn sm4_freectx;
+static OSSL_FUNC_cipher_dupctx_fn sm4_dupctx;
+
+static void sm4_freectx(void *vctx)
+{
+ PROV_SM4_CTX *ctx = (PROV_SM4_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *sm4_dupctx(void *ctx)
+{
+ PROV_SM4_CTX *in = (PROV_SM4_CTX *)ctx;
+ PROV_SM4_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+/* ossl_sm4128ecb_functions */
+IMPLEMENT_generic_cipher(sm4, SM4, ecb, ECB, 0, 128, 128, 0, block)
+/* ossl_sm4128cbc_functions */
+IMPLEMENT_generic_cipher(sm4, SM4, cbc, CBC, 0, 128, 128, 128, block)
+/* ossl_sm4128ctr_functions */
+IMPLEMENT_generic_cipher(sm4, SM4, ctr, CTR, 0, 128, 8, 128, stream)
+/* ossl_sm4128ofb128_functions */
+IMPLEMENT_generic_cipher(sm4, SM4, ofb128, OFB, 0, 128, 8, 128, stream)
+/* ossl_sm4128cfb128_functions */
+IMPLEMENT_generic_cipher(sm4, SM4, cfb128, CFB, 0, 128, 8, 128, stream)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h
new file mode 100644
index 000000000000..9ab49e3279f1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "crypto/sm4.h"
+#include "crypto/sm4_platform.h"
+
+typedef struct prov_cast_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ SM4_KEY ks;
+ } ks;
+} PROV_SM4_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_cbc(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ecb(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ctr(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_ofb128(size_t keybits);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_cfb128(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c
new file mode 100644
index 000000000000..3af84d85b7dc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for SM4 CCM mode */
+
+#include "cipher_sm4_ccm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn sm4_ccm_freectx;
+
+static void *sm4_ccm_newctx(void *provctx, size_t keybits)
+{
+ PROV_SM4_CCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_ccm_initctx(&ctx->base, keybits, ossl_prov_sm4_hw_ccm(keybits));
+ return ctx;
+}
+
+static void *sm4_ccm_dupctx(void *provctx)
+{
+ PROV_SM4_CCM_CTX *ctx = provctx;
+ PROV_SM4_CCM_CTX *dctx = NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.ccm_ctx.key != NULL)
+ dctx->base.ccm_ctx.key = &dctx->ks.ks;
+
+ return dctx;
+}
+
+static void sm4_ccm_freectx(void *vctx)
+{
+ PROV_SM4_CCM_CTX *ctx = (PROV_SM4_CCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* sm4128ccm functions */
+IMPLEMENT_aead_cipher(sm4, ccm, CCM, AEAD_FLAGS, 128, 8, 96);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h
new file mode 100644
index 000000000000..561437993a4a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/sm4.h"
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_ccm.h"
+#include "crypto/sm4_platform.h"
+
+typedef struct prov_sm4_ccm_ctx_st {
+ PROV_CCM_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ SM4_KEY ks;
+ } ks; /* SM4 key schedule to use */
+} PROV_SM4_CCM_CTX;
+
+const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keylen);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c
new file mode 100644
index 000000000000..1c1d60494ab6
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2021-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Generic support for SM4 CCM.
+ */
+
+#include "cipher_sm4_ccm.h"
+#include "crypto/sm4_platform.h"
+
+#define SM4_HW_CCM_SET_KEY_FN(fn_set_enc_key, fn_blk, fn_ccm_enc, fn_ccm_dec) \
+ fn_set_enc_key(key, &actx->ks.ks); \
+ CRYPTO_ccm128_init(&ctx->ccm_ctx, ctx->m, ctx->l, &actx->ks.ks, \
+ (block128_f)fn_blk); \
+ ctx->str = ctx->enc ? (ccm128_f)fn_ccm_enc : (ccm128_f)fn_ccm_dec; \
+ ctx->key_set = 1;
+
+static int ccm_sm4_initkey(PROV_CCM_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_SM4_CCM_CTX *actx = (PROV_SM4_CCM_CTX *)ctx;
+
+#ifdef HWSM4_CAPABLE
+ if (HWSM4_CAPABLE) {
+ SM4_HW_CCM_SET_KEY_FN(HWSM4_set_encrypt_key, HWSM4_encrypt, NULL, NULL);
+ } else
+#endif /* HWSM4_CAPABLE */
+
+#ifdef VPSM4_EX_CAPABLE
+ if (VPSM4_EX_CAPABLE) {
+ SM4_HW_CCM_SET_KEY_FN(vpsm4_ex_set_encrypt_key, vpsm4_ex_encrypt, NULL,
+ NULL);
+ } else
+#endif /* VPSM4_EX_CAPABLE */
+
+#ifdef VPSM4_CAPABLE
+ if (VPSM4_CAPABLE) {
+ SM4_HW_CCM_SET_KEY_FN(vpsm4_set_encrypt_key, vpsm4_encrypt, NULL, NULL);
+ } else
+#endif /* VPSM4_CAPABLE */
+ {
+ SM4_HW_CCM_SET_KEY_FN(ossl_sm4_set_key, ossl_sm4_encrypt, NULL, NULL);
+ }
+ return 1;
+}
+
+static const PROV_CCM_HW ccm_sm4 = {
+ ccm_sm4_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_sm4_ccm_hw_rv64i.inc"
+#else
+const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keybits)
+{
+ return &ccm_sm4;
+}
+#endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc
new file mode 100644
index 000000000000..c20c9f688ae8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_ccm_hw_rv64i.inc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RV64I ZVKSED support for SM4 CCM.
+ * This file is included by cipher_sm4_ccm_hw.c
+ */
+
+static int rv64i_zvksed_sm4_ccm_initkey(PROV_CCM_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_CCM_CTX *actx = (PROV_SM4_CCM_CTX *)ctx;
+
+ SM4_HW_CCM_SET_KEY_FN(rv64i_zvksed_sm4_set_encrypt_key,
+ rv64i_zvksed_sm4_encrypt, NULL, NULL);
+ return 1;
+}
+
+static const PROV_CCM_HW rv64i_zvksed_sm4_ccm = {
+ rv64i_zvksed_sm4_ccm_initkey,
+ ossl_ccm_generic_setiv,
+ ossl_ccm_generic_setaad,
+ ossl_ccm_generic_auth_encrypt,
+ ossl_ccm_generic_auth_decrypt,
+ ossl_ccm_generic_gettag
+};
+
+const PROV_CCM_HW *ossl_prov_sm4_hw_ccm(size_t keybits)
+{
+ if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128)
+ return &rv64i_zvksed_sm4_ccm;
+ else
+ return &ccm_sm4;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c
new file mode 100644
index 000000000000..1128f659393e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for SM4 GCM mode */
+
+#include "cipher_sm4_gcm.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_cipher_freectx_fn sm4_gcm_freectx;
+
+static void *sm4_gcm_newctx(void *provctx, size_t keybits)
+{
+ PROV_SM4_GCM_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ossl_gcm_initctx(provctx, &ctx->base, keybits,
+ ossl_prov_sm4_hw_gcm(keybits));
+ return ctx;
+}
+
+static void *sm4_gcm_dupctx(void *provctx)
+{
+ PROV_SM4_GCM_CTX *ctx = provctx;
+ PROV_SM4_GCM_CTX *dctx = NULL;
+
+ if (ctx == NULL)
+ return NULL;
+
+ dctx = OPENSSL_memdup(ctx, sizeof(*ctx));
+ if (dctx != NULL && dctx->base.gcm.key != NULL)
+ dctx->base.gcm.key = &dctx->ks.ks;
+
+ return dctx;
+}
+
+static void sm4_gcm_freectx(void *vctx)
+{
+ PROV_SM4_GCM_CTX *ctx = (PROV_SM4_GCM_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+/* ossl_sm4128gcm_functions */
+IMPLEMENT_aead_cipher(sm4, gcm, GCM, AEAD_FLAGS, 128, 8, 96);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h
new file mode 100644
index 000000000000..2b6b5f3ece74
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/sm4.h"
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_gcm.h"
+
+typedef struct prov_sm4_gcm_ctx_st {
+ PROV_GCM_CTX base; /* must be first entry in struct */
+ union {
+ OSSL_UNION_ALIGN;
+ SM4_KEY ks;
+ } ks;
+} PROV_SM4_GCM_CTX;
+
+const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c
new file mode 100644
index 000000000000..c1e354be45ba
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * Generic support for SM4 GCM.
+ */
+
+#include "cipher_sm4_gcm.h"
+#include "crypto/sm4_platform.h"
+
+# define SM4_GCM_HW_SET_KEY_CTR_FN(ks, fn_set_enc_key, fn_block, fn_ctr) \
+ fn_set_enc_key(key, ks); \
+ CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); \
+ ctx->ctr = (ctr128_f)fn_ctr; \
+ ctx->key_set = 1;
+
+static int sm4_gcm_initkey(PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_GCM_CTX *actx = (PROV_SM4_GCM_CTX *)ctx;
+ SM4_KEY *ks = &actx->ks.ks;
+
+# ifdef HWSM4_CAPABLE
+ if (HWSM4_CAPABLE) {
+# ifdef HWSM4_ctr32_encrypt_blocks
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, HWSM4_set_encrypt_key, HWSM4_encrypt,
+ HWSM4_ctr32_encrypt_blocks);
+# else /* HWSM4_ctr32_encrypt_blocks */
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, HWSM4_set_encrypt_key, HWSM4_encrypt, NULL);
+# endif
+ } else
+# endif /* HWSM4_CAPABLE */
+
+#ifdef VPSM4_EX_CAPABLE
+ if (VPSM4_EX_CAPABLE) {
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, vpsm4_ex_set_encrypt_key, vpsm4_ex_encrypt,
+ vpsm4_ex_ctr32_encrypt_blocks);
+ } else
+#endif /* VPSM4_EX_CAPABLE */
+
+# ifdef VPSM4_CAPABLE
+ if (VPSM4_CAPABLE) {
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, vpsm4_set_encrypt_key, vpsm4_encrypt,
+ vpsm4_ctr32_encrypt_blocks);
+ } else
+# endif /* VPSM4_CAPABLE */
+ {
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, ossl_sm4_set_key, ossl_sm4_encrypt, NULL);
+ }
+
+ return 1;
+}
+
+static int hw_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out)
+{
+ if (ctx->enc) {
+ if (ctx->ctr != NULL) {
+ if (CRYPTO_gcm128_encrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr))
+ return 0;
+ } else {
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ } else {
+ if (ctx->ctr != NULL) {
+ if (CRYPTO_gcm128_decrypt_ctr32(&ctx->gcm, in, out, len, ctx->ctr))
+ return 0;
+ } else {
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const PROV_GCM_HW sm4_gcm = {
+ sm4_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ hw_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_sm4_gcm_hw_rv64i.inc"
+#else
+const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits)
+{
+ return &sm4_gcm;
+}
+#endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc
new file mode 100644
index 000000000000..109d13b438e5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_gcm_hw_rv64i.inc
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 64 ZVKSED support for SM4 GCM.
+ * This file is included by cipher_sm4_gcm_hw.c
+ */
+
+static int rv64i_zvksed_sm4_gcm_initkey(PROV_GCM_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_GCM_CTX *actx = (PROV_SM4_GCM_CTX *)ctx;
+ SM4_KEY *ks = &actx->ks.ks;
+
+ SM4_GCM_HW_SET_KEY_CTR_FN(ks, rv64i_zvksed_sm4_set_encrypt_key,
+ rv64i_zvksed_sm4_encrypt, NULL);
+ return 1;
+}
+
+static const PROV_GCM_HW rv64i_zvksed_sm4_gcm = {
+ rv64i_zvksed_sm4_gcm_initkey,
+ ossl_gcm_setiv,
+ ossl_gcm_aad_update,
+ hw_gcm_cipher_update,
+ ossl_gcm_cipher_final,
+ ossl_gcm_one_shot
+};
+
+const PROV_GCM_HW *ossl_prov_sm4_hw_gcm(size_t keybits)
+{
+ if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128)
+ return &rv64i_zvksed_sm4_gcm;
+ else
+ return &sm4_gcm;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c
new file mode 100644
index 000000000000..05a83843eb49
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "cipher_sm4.h"
+
+static int cipher_hw_sm4_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_SM4_CTX *sctx = (PROV_SM4_CTX *)ctx;
+ SM4_KEY *ks = &sctx->ks.ks;
+
+ ctx->ks = ks;
+ if (ctx->enc
+ || (ctx->mode != EVP_CIPH_ECB_MODE
+ && ctx->mode != EVP_CIPH_CBC_MODE)) {
+#ifdef HWSM4_CAPABLE
+ if (HWSM4_CAPABLE) {
+ HWSM4_set_encrypt_key(key, ks);
+ ctx->block = (block128_f)HWSM4_encrypt;
+ ctx->stream.cbc = NULL;
+#ifdef HWSM4_cbc_encrypt
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)HWSM4_cbc_encrypt;
+ else
+#endif
+#ifdef HWSM4_ecb_encrypt
+ if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)HWSM4_ecb_encrypt;
+ else
+#endif
+#ifdef HWSM4_ctr32_encrypt_blocks
+ if (ctx->mode == EVP_CIPH_CTR_MODE)
+ ctx->stream.ctr = (ctr128_f)HWSM4_ctr32_encrypt_blocks;
+ else
+#endif
+ (void)0; /* terminate potentially open 'else' */
+ } else
+#endif
+#ifdef VPSM4_EX_CAPABLE
+ if (VPSM4_EX_CAPABLE) {
+ vpsm4_ex_set_encrypt_key(key, ks);
+ ctx->block = (block128_f)vpsm4_ex_encrypt;
+ ctx->stream.cbc = NULL;
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)vpsm4_ex_cbc_encrypt;
+ else if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)vpsm4_ex_ecb_encrypt;
+ else if (ctx->mode == EVP_CIPH_CTR_MODE)
+ ctx->stream.ctr = (ctr128_f)vpsm4_ex_ctr32_encrypt_blocks;
+ } else
+#endif
+#ifdef VPSM4_CAPABLE
+ if (VPSM4_CAPABLE) {
+ vpsm4_set_encrypt_key(key, ks);
+ ctx->block = (block128_f)vpsm4_encrypt;
+ ctx->stream.cbc = NULL;
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)vpsm4_cbc_encrypt;
+ else if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)vpsm4_ecb_encrypt;
+ else if (ctx->mode == EVP_CIPH_CTR_MODE)
+ ctx->stream.ctr = (ctr128_f)vpsm4_ctr32_encrypt_blocks;
+ } else
+#endif
+ {
+ ossl_sm4_set_key(key, ks);
+ ctx->block = (block128_f)ossl_sm4_encrypt;
+ }
+ } else {
+#ifdef HWSM4_CAPABLE
+ if (HWSM4_CAPABLE) {
+ HWSM4_set_decrypt_key(key, ks);
+ ctx->block = (block128_f)HWSM4_decrypt;
+ ctx->stream.cbc = NULL;
+#ifdef HWSM4_cbc_encrypt
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)HWSM4_cbc_encrypt;
+#endif
+#ifdef HWSM4_ecb_encrypt
+ if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)HWSM4_ecb_encrypt;
+#endif
+ } else
+#endif
+#ifdef VPSM4_EX_CAPABLE
+ if (VPSM4_EX_CAPABLE) {
+ vpsm4_ex_set_decrypt_key(key, ks);
+ ctx->block = (block128_f)vpsm4_ex_decrypt;
+ ctx->stream.cbc = NULL;
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)vpsm4_ex_cbc_encrypt;
+ else if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)vpsm4_ex_ecb_encrypt;
+ } else
+#endif
+#ifdef VPSM4_CAPABLE
+ if (VPSM4_CAPABLE) {
+ vpsm4_set_decrypt_key(key, ks);
+ ctx->block = (block128_f)vpsm4_decrypt;
+ ctx->stream.cbc = NULL;
+ if (ctx->mode == EVP_CIPH_CBC_MODE)
+ ctx->stream.cbc = (cbc128_f)vpsm4_cbc_encrypt;
+ else if (ctx->mode == EVP_CIPH_ECB_MODE)
+ ctx->stream.ecb = (ecb128_f)vpsm4_ecb_encrypt;
+ } else
+#endif
+ {
+ ossl_sm4_set_key(key, ks);
+ ctx->block = (block128_f)ossl_sm4_decrypt;
+ }
+ }
+
+ return 1;
+}
+
+IMPLEMENT_CIPHER_HW_COPYCTX(cipher_hw_sm4_copyctx, PROV_SM4_CTX)
+
+# define PROV_CIPHER_HW_sm4_mode(mode) \
+static const PROV_CIPHER_HW sm4_##mode = { \
+ cipher_hw_sm4_initkey, \
+ ossl_cipher_hw_generic_##mode, \
+ cipher_hw_sm4_copyctx \
+}; \
+PROV_CIPHER_HW_declare(mode) \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_##mode(size_t keybits) \
+{ \
+ PROV_CIPHER_HW_select(mode) \
+ return &sm4_##mode; \
+}
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_sm4_hw_rv64i.inc"
+#else
+/* The generic case */
+# define PROV_CIPHER_HW_declare(mode)
+# define PROV_CIPHER_HW_select(mode)
+#endif
+
+PROV_CIPHER_HW_sm4_mode(cbc)
+PROV_CIPHER_HW_sm4_mode(ecb)
+PROV_CIPHER_HW_sm4_mode(ofb128)
+PROV_CIPHER_HW_sm4_mode(cfb128)
+PROV_CIPHER_HW_sm4_mode(ctr)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc
new file mode 100644
index 000000000000..763d9d09dd9e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_hw_rv64i.inc
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RV64 ZVKSED support for AES modes ecb, cbc, ofb, cfb, ctr.
+ * This file is included by cipher_sm4_hw.c
+ */
+
+#define cipher_hw_rv64i_zvksed_sm4_cbc ossl_cipher_hw_generic_cbc
+#define cipher_hw_rv64i_zvksed_sm4_ecb ossl_cipher_hw_generic_ecb
+#define cipher_hw_rv64i_zvksed_sm4_ofb128 ossl_cipher_hw_generic_ofb128
+#define cipher_hw_rv64i_zvksed_sm4_cfb128 ossl_cipher_hw_generic_cfb128
+#define cipher_hw_rv64i_zvksed_sm4_ctr ossl_cipher_hw_generic_ctr
+
+static int cipher_hw_rv64i_zvksed_sm4_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_CTX *sctx = (PROV_SM4_CTX *)ctx;
+ SM4_KEY *ks = &sctx->ks.ks;
+
+ ctx->ks = ks;
+ if (ctx->enc
+ || (ctx->mode != EVP_CIPH_ECB_MODE
+ && ctx->mode != EVP_CIPH_CBC_MODE)) {
+ rv64i_zvksed_sm4_set_encrypt_key(key, ks);
+ ctx->block = (block128_f) rv64i_zvksed_sm4_encrypt;
+ ctx->stream.cbc = NULL;
+ } else {
+ rv64i_zvksed_sm4_set_decrypt_key(key, ks);
+ ctx->block = (block128_f) rv64i_zvksed_sm4_decrypt;
+ ctx->stream.cbc = NULL;
+ }
+
+ return 1;
+}
+
+#define PROV_CIPHER_HW_declare(mode) \
+static const PROV_CIPHER_HW rv64i_zvksed_sm4_##mode = { \
+ cipher_hw_rv64i_zvksed_sm4_initkey, \
+ cipher_hw_rv64i_zvksed_sm4_##mode, \
+ cipher_hw_sm4_copyctx \
+};
+#define PROV_CIPHER_HW_select(mode) \
+if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128) \
+ return &rv64i_zvksed_sm4_##mode;
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c
new file mode 100644
index 000000000000..31e86d6a8a0a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.c
@@ -0,0 +1,281 @@
+
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for SM4 XTS mode */
+
+#include <openssl/proverr.h>
+#include "cipher_sm4_xts.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define SM4_XTS_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV
+#define SM4_XTS_IV_BITS 128
+#define SM4_XTS_BLOCK_BITS 8
+
+/* forward declarations */
+static OSSL_FUNC_cipher_encrypt_init_fn sm4_xts_einit;
+static OSSL_FUNC_cipher_decrypt_init_fn sm4_xts_dinit;
+static OSSL_FUNC_cipher_update_fn sm4_xts_stream_update;
+static OSSL_FUNC_cipher_final_fn sm4_xts_stream_final;
+static OSSL_FUNC_cipher_cipher_fn sm4_xts_cipher;
+static OSSL_FUNC_cipher_freectx_fn sm4_xts_freectx;
+static OSSL_FUNC_cipher_dupctx_fn sm4_xts_dupctx;
+static OSSL_FUNC_cipher_set_ctx_params_fn sm4_xts_set_ctx_params;
+static OSSL_FUNC_cipher_settable_ctx_params_fn sm4_xts_settable_ctx_params;
+
+/*-
+ * Provider dispatch functions
+ */
+static int sm4_xts_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)vctx;
+ PROV_CIPHER_CTX *ctx = &xctx->base;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (!ossl_cipher_generic_initiv(vctx, iv, ivlen))
+ return 0;
+ }
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->init(ctx, key, keylen))
+ return 0;
+ }
+ return sm4_xts_set_ctx_params(xctx, params);
+}
+
+static int sm4_xts_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return sm4_xts_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+static int sm4_xts_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return sm4_xts_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+static void *sm4_xts_newctx(void *provctx, unsigned int mode, uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits)
+{
+ PROV_SM4_XTS_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ossl_cipher_generic_initkey(&ctx->base, kbits, blkbits, ivbits, mode,
+ flags, ossl_prov_cipher_hw_sm4_xts(kbits),
+ NULL);
+ }
+ return ctx;
+}
+
+static void sm4_xts_freectx(void *vctx)
+{
+ PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void *sm4_xts_dupctx(void *vctx)
+{
+ PROV_SM4_XTS_CTX *in = (PROV_SM4_XTS_CTX *)vctx;
+ PROV_SM4_XTS_CTX *ret = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if (in->xts.key1 != NULL) {
+ if (in->xts.key1 != &in->ks1)
+ return NULL;
+ }
+ if (in->xts.key2 != NULL) {
+ if (in->xts.key2 != &in->ks2)
+ return NULL;
+ }
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ in->base.hw->copyctx(&ret->base, &in->base);
+ return ret;
+}
+
+static int sm4_xts_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx;
+
+ if (!ossl_prov_is_running()
+ || ctx->xts.key1 == NULL
+ || ctx->xts.key2 == NULL
+ || !ctx->base.iv_set
+ || out == NULL
+ || in == NULL
+ || inl < SM4_BLOCK_SIZE)
+ return 0;
+
+ /*
+ * Impose a limit of 2^20 blocks per data unit as specified by
+ * IEEE Std 1619-2018. The earlier and obsolete IEEE Std 1619-2007
+ * indicated that this was a SHOULD NOT rather than a MUST NOT.
+ * NIST SP 800-38E mandates the same limit.
+ */
+ if (inl > XTS_MAX_BLOCKS_PER_DATA_UNIT * SM4_BLOCK_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XTS_DATA_UNIT_IS_TOO_LARGE);
+ return 0;
+ }
+ if (ctx->xts_standard) {
+ if (ctx->stream != NULL)
+ (*ctx->stream)(in, out, inl, ctx->xts.key1, ctx->xts.key2,
+ ctx->base.iv, ctx->base.enc);
+ else if (CRYPTO_xts128_encrypt(&ctx->xts, ctx->base.iv, in, out, inl,
+ ctx->base.enc))
+ return 0;
+ } else {
+ if (ctx->stream_gb != NULL)
+ (*ctx->stream_gb)(in, out, inl, ctx->xts.key1, ctx->xts.key2,
+ ctx->base.iv, ctx->base.enc);
+ else if (ossl_crypto_xts128gb_encrypt(&ctx->xts, ctx->base.iv, in, out,
+ inl, ctx->base.enc))
+ return 0;
+ }
+ *outl = inl;
+ return 1;
+}
+
+static int sm4_xts_stream_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_SM4_XTS_CTX *ctx = (PROV_SM4_XTS_CTX *)vctx;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!sm4_xts_cipher(ctx, out, outl, outsize, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int sm4_xts_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ *outl = 0;
+ return 1;
+}
+
+static const OSSL_PARAM sm4_xts_known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_CIPHER_PARAM_XTS_STANDARD, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *sm4_xts_settable_ctx_params(ossl_unused void *cctx,
+ ossl_unused void *provctx)
+{
+ return sm4_xts_known_settable_ctx_params;
+}
+
+static int sm4_xts_set_ctx_params(void *vxctx, const OSSL_PARAM params[])
+{
+ PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)vxctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ /*-
+ * Sets the XTS standard to use with SM4-XTS algorithm.
+ *
+ * Must be utf8 string "GB" or "IEEE",
+ * "GB" means the GB/T 17964-2021 standard
+ * "IEEE" means the IEEE Std 1619-2007 standard
+ */
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_XTS_STANDARD);
+
+ if (p != NULL) {
+ const char *xts_standard = NULL;
+
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+
+ if (!OSSL_PARAM_get_utf8_string_ptr(p, &xts_standard)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (OPENSSL_strcasecmp(xts_standard, "GB") == 0) {
+ xctx->xts_standard = 0;
+ } else if (OPENSSL_strcasecmp(xts_standard, "IEEE") == 0) {
+ xctx->xts_standard = 1;
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+#define IMPLEMENT_cipher(lcmode, UCMODE, kbits, flags) \
+static OSSL_FUNC_cipher_get_params_fn sm4_##kbits##_##lcmode##_get_params; \
+static int sm4_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, 2 * kbits, SM4_XTS_BLOCK_BITS,\
+ SM4_XTS_IV_BITS); \
+} \
+static OSSL_FUNC_cipher_newctx_fn sm4_##kbits##_xts_newctx; \
+static void *sm4_##kbits##_xts_newctx(void *provctx) \
+{ \
+ return sm4_xts_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, flags, 2 * kbits, \
+ SM4_XTS_BLOCK_BITS, SM4_XTS_IV_BITS); \
+} \
+const OSSL_DISPATCH ossl_sm4##kbits##xts_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))sm4_##kbits##_xts_newctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))sm4_xts_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))sm4_xts_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))sm4_xts_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))sm4_xts_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))sm4_xts_cipher }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))sm4_xts_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))sm4_xts_dupctx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))sm4_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))sm4_xts_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))sm4_xts_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+/* ossl_sm4128xts_functions */
+IMPLEMENT_cipher(xts, XTS, 128, SM4_XTS_FLAGS);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h
new file mode 100644
index 000000000000..43d9a212e55f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <crypto/sm4.h>
+#include "prov/ciphercommon.h"
+#include "crypto/sm4_platform.h"
+
+PROV_CIPHER_FUNC(void, xts_stream,
+ (const unsigned char *in, unsigned char *out, size_t len,
+ const SM4_KEY *key1, const SM4_KEY *key2,
+ const unsigned char iv[16], const int enc));
+
+typedef struct prov_sm4_xts_ctx_st {
+ /* Must be first */
+ PROV_CIPHER_CTX base;
+
+ /* SM4 key schedules to use */
+ union {
+ OSSL_UNION_ALIGN;
+ SM4_KEY ks;
+ } ks1, ks2;
+
+ /*-
+ * XTS standard to use with SM4-XTS algorithm
+ *
+ * Must be 0 or 1,
+ * 0 for XTS mode specified by GB/T 17964-2021
+ * 1 for XTS mode specified by IEEE Std 1619-2007
+ */
+ int xts_standard;
+
+ XTS128_CONTEXT xts;
+
+ /* Stream function for XTS mode specified by GB/T 17964-2021 */
+ OSSL_xts_stream_fn stream_gb;
+ /* Stream function for XTS mode specified by IEEE Std 1619-2007 */
+ OSSL_xts_stream_fn stream;
+} PROV_SM4_XTS_CTX;
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c
new file mode 100644
index 000000000000..d147cf1a6114
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "cipher_sm4_xts.h"
+
+#define XTS_SET_KEY_FN(fn_set_enc_key, fn_set_dec_key, \
+ fn_block_enc, fn_block_dec, \
+ fn_stream, fn_stream_gb) { \
+ size_t bytes = keylen / 2; \
+ \
+ if (ctx->enc) { \
+ fn_set_enc_key(key, &xctx->ks1.ks); \
+ xctx->xts.block1 = (block128_f)fn_block_enc; \
+ } else { \
+ fn_set_dec_key(key, &xctx->ks1.ks); \
+ xctx->xts.block1 = (block128_f)fn_block_dec; \
+ } \
+ fn_set_enc_key(key + bytes, &xctx->ks2.ks); \
+ xctx->xts.block2 = (block128_f)fn_block_enc; \
+ xctx->xts.key1 = &xctx->ks1; \
+ xctx->xts.key2 = &xctx->ks2; \
+ xctx->stream = fn_stream; \
+ xctx->stream_gb = fn_stream_gb; \
+}
+
+static int cipher_hw_sm4_xts_generic_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream = NULL;
+ OSSL_xts_stream_fn stream_gb = NULL;
+#ifdef HWSM4_CAPABLE
+ if (HWSM4_CAPABLE) {
+ XTS_SET_KEY_FN(HWSM4_set_encrypt_key, HWSM4_set_decrypt_key,
+ HWSM4_encrypt, HWSM4_decrypt, stream, stream_gb);
+ return 1;
+ } else
+#endif /* HWSM4_CAPABLE */
+#ifdef VPSM4_EX_CAPABLE
+ if (VPSM4_EX_CAPABLE) {
+ stream = vpsm4_ex_xts_encrypt;
+ stream_gb = vpsm4_ex_xts_encrypt_gb;
+ XTS_SET_KEY_FN(vpsm4_ex_set_encrypt_key, vpsm4_ex_set_decrypt_key,
+ vpsm4_ex_encrypt, vpsm4_ex_decrypt, stream, stream_gb);
+ return 1;
+ } else
+#endif /* VPSM4_EX_CAPABLE */
+#ifdef VPSM4_CAPABLE
+ if (VPSM4_CAPABLE) {
+ stream = vpsm4_xts_encrypt;
+ stream_gb = vpsm4_xts_encrypt_gb;
+ XTS_SET_KEY_FN(vpsm4_set_encrypt_key, vpsm4_set_decrypt_key,
+ vpsm4_encrypt, vpsm4_decrypt, stream, stream_gb);
+ return 1;
+ } else
+#endif /* VPSM4_CAPABLE */
+ {
+ (void)0;
+ }
+ {
+ XTS_SET_KEY_FN(ossl_sm4_set_key, ossl_sm4_set_key, ossl_sm4_encrypt,
+ ossl_sm4_decrypt, stream, stream_gb);
+ }
+ return 1;
+}
+
+static void cipher_hw_sm4_xts_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src)
+{
+ PROV_SM4_XTS_CTX *sctx = (PROV_SM4_XTS_CTX *)src;
+ PROV_SM4_XTS_CTX *dctx = (PROV_SM4_XTS_CTX *)dst;
+
+ *dctx = *sctx;
+ dctx->xts.key1 = &dctx->ks1.ks;
+ dctx->xts.key2 = &dctx->ks2.ks;
+}
+
+
+static const PROV_CIPHER_HW sm4_generic_xts = {
+ cipher_hw_sm4_xts_generic_initkey,
+ NULL,
+ cipher_hw_sm4_xts_copyctx
+};
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__riscv) && __riscv_xlen == 64
+# include "cipher_sm4_xts_hw_rv64i.inc"
+#else
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits)
+{
+ return &sm4_generic_xts;
+}
+#endif
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc
new file mode 100644
index 000000000000..2ab15269cc75
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_sm4_xts_hw_rv64i.inc
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*-
+ * RISC-V 64 ZVKSED support for SM4 GCM.
+ * This file is included by cipher_sm4_gcm_hw.c
+ */
+
+static int rv64i_zvksed_sm4_xts_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_SM4_XTS_CTX *xctx = (PROV_SM4_XTS_CTX *)ctx;
+ OSSL_xts_stream_fn stream_fn = NULL;
+ OSSL_xts_stream_fn stream_gb_fn = NULL;
+
+ XTS_SET_KEY_FN(rv64i_zvksed_sm4_set_encrypt_key,
+ rv64i_zvksed_sm4_set_decrypt_key,
+ rv64i_zvksed_sm4_encrypt,
+ rv64i_zvksed_sm4_decrypt,
+ stream_fn, stream_gb_fn);
+ return 1;
+}
+
+static const PROV_CIPHER_HW rv64i_zvksed_sm4_xts = {
+ rv64i_zvksed_sm4_xts_initkey,
+ NULL,
+ cipher_hw_sm4_xts_copyctx
+};
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_sm4_xts(size_t keybits)
+{
+ if (RISCV_HAS_ZVKB_AND_ZVKSED() && riscv_vlen() >= 128)
+ return &rv64i_zvksed_sm4_xts;
+ else
+ return &sm4_generic_xts;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c
new file mode 100644
index 000000000000..2e5f8c3f05bb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/ciphercommon.h"
+#include "cipher_tdes.h"
+#include "prov/implementations.h"
+
+/* ossl_tdes_ede3_ecb_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, ecb, ECB, TDES_FLAGS, 64*3, 64, 0, block);
+/* ossl_tdes_ede3_cbc_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, cbc, CBC, TDES_FLAGS, 64*3, 64, 64, block);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h
new file mode 100644
index 000000000000..55bd76045176
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/des.h>
+#include <openssl/core_dispatch.h>
+#include "prov/securitycheck.h"
+#include "crypto/des_platform.h"
+
+#define DES_BLOCK_SIZE 8
+#define TDES_IVLEN 8
+#define TDES_FLAGS PROV_CIPHER_FLAG_RAND_KEY
+
+typedef struct prov_tdes_ctx_st {
+ PROV_CIPHER_CTX base; /* Must be first */
+ union {
+ OSSL_UNION_ALIGN;
+ DES_key_schedule ks[3];
+ } tks;
+ union {
+ void (*cbc) (const void *, void *, size_t,
+ const DES_key_schedule *, unsigned char *);
+ } tstream;
+ OSSL_FIPS_IND_DECLARE
+
+} PROV_TDES_CTX;
+
+#define IMPLEMENT_tdes_cipher(type, UCTYPE, lcmode, UCMODE, flags, \
+ kbits, blkbits, ivbits, block) \
+static OSSL_FUNC_cipher_newctx_fn tdes_##type##_##lcmode##_newctx; \
+static void *tdes_##type##_##lcmode##_newctx(void *provctx) \
+{ \
+ return ossl_tdes_newctx(provctx, EVP_CIPH_##UCMODE##_MODE, kbits, blkbits, \
+ ivbits, flags, \
+ ossl_prov_cipher_hw_tdes_##type##_##lcmode()); \
+} \
+static OSSL_FUNC_cipher_get_params_fn tdes_##type##_##lcmode##_get_params; \
+static int tdes_##type##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_tdes_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+const OSSL_DISPATCH ossl_tdes_##type##_##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_tdes_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_tdes_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, \
+ (void (*)(void))ossl_cipher_generic_##block##_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, \
+ (void (*)(void))ossl_cipher_generic_##block##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void))tdes_##type##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))ossl_tdes_dupctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_tdes_freectx }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void))tdes_##type##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+void *ossl_tdes_newctx(void *provctx, int mode, size_t kbits, size_t blkbits,
+ size_t ivbits, uint64_t flags, const PROV_CIPHER_HW *hw);
+int ossl_tdes_get_params(OSSL_PARAM params[], unsigned int md, uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits);
+
+OSSL_FUNC_cipher_dupctx_fn ossl_tdes_dupctx;
+OSSL_FUNC_cipher_freectx_fn ossl_tdes_freectx;
+OSSL_FUNC_cipher_encrypt_init_fn ossl_tdes_einit;
+OSSL_FUNC_cipher_decrypt_init_fn ossl_tdes_dinit;
+OSSL_FUNC_cipher_get_ctx_params_fn ossl_tdes_get_ctx_params;
+OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_tdes_gettable_ctx_params;
+OSSL_FUNC_cipher_set_ctx_params_fn ossl_tdes_set_ctx_params;
+OSSL_FUNC_cipher_settable_ctx_params_fn ossl_tdes_settable_ctx_params;
+
+#define PROV_CIPHER_HW_tdes_mode(type, mode) \
+static const PROV_CIPHER_HW type##_##mode = { \
+ ossl_cipher_hw_tdes_##type##_initkey, \
+ ossl_cipher_hw_tdes_##mode, \
+ ossl_cipher_hw_tdes_copyctx \
+}; \
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_##type##_##mode(void) \
+{ \
+ return &type##_##mode; \
+}
+
+int ossl_cipher_hw_tdes_ede3_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen);
+void ossl_cipher_hw_tdes_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src);
+int ossl_cipher_hw_tdes_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl);
+int ossl_cipher_hw_tdes_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len);
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cbc(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_ecb(void);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c
new file mode 100644
index 000000000000..e8564c3d0f83
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_common.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/ciphercommon.h"
+#include "cipher_tdes.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+void *ossl_tdes_newctx(void *provctx, int mode, size_t kbits, size_t blkbits,
+ size_t ivbits, uint64_t flags, const PROV_CIPHER_HW *hw)
+{
+ PROV_TDES_CTX *tctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ tctx = OPENSSL_zalloc(sizeof(*tctx));
+ if (tctx != NULL) {
+ OSSL_FIPS_IND_INIT(tctx)
+ ossl_cipher_generic_initkey(tctx, kbits, blkbits, ivbits, mode, flags,
+ hw, provctx);
+ }
+ return tctx;
+}
+
+void *ossl_tdes_dupctx(void *ctx)
+{
+ PROV_TDES_CTX *in = (PROV_TDES_CTX *)ctx;
+ PROV_TDES_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ret = OPENSSL_malloc(sizeof(*ret));
+ if (ret == NULL)
+ return NULL;
+ OSSL_FIPS_IND_COPY(ret, in)
+ in->base.hw->copyctx(&ret->base, &in->base);
+
+ return ret;
+}
+
+void ossl_tdes_freectx(void *vctx)
+{
+ PROV_TDES_CTX *ctx = (PROV_TDES_CTX *)vctx;
+
+ ossl_cipher_generic_reset_ctx((PROV_CIPHER_CTX *)vctx);
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+#ifdef FIPS_MODULE
+static int tdes_encrypt_check_approved(PROV_TDES_CTX *ctx, int enc)
+{
+ /* Triple-DES encryption is not approved in FIPS 140-3 */
+ if (enc && !OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ ctx->base.libctx,
+ "Triple-DES", "Encryption",
+ ossl_fips_config_tdes_encrypt_disallowed))
+ return 0;
+ return 1;
+}
+#endif
+
+static int tdes_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->num = 0;
+ ctx->bufsz = 0;
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (!ossl_cipher_generic_initiv(ctx, iv, ivlen))
+ return 0;
+ } else if (ctx->iv_set
+ && (ctx->mode == EVP_CIPH_CBC_MODE
+ || ctx->mode == EVP_CIPH_CFB_MODE
+ || ctx->mode == EVP_CIPH_OFB_MODE)) {
+ /* reset IV to keep compatibility with 1.1.1 */
+ memcpy(ctx->iv, ctx->oiv, ctx->ivlen);
+ }
+
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->init(ctx, key, ctx->keylen))
+ return 0;
+ ctx->key_set = 1;
+ }
+ if (!ossl_tdes_set_ctx_params(ctx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!tdes_encrypt_check_approved((PROV_TDES_CTX *)ctx, enc))
+ return 0;
+#endif
+ return 1;
+}
+
+int ossl_tdes_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return tdes_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+int ossl_tdes_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return tdes_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(ossl_tdes)
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_RANDOM_KEY, NULL, 0),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(ossl_tdes)
+
+static int tdes_generatekey(PROV_CIPHER_CTX *ctx, void *ptr)
+{
+ DES_cblock *deskey = ptr;
+ size_t kl = ctx->keylen;
+
+ if (kl == 0 || RAND_priv_bytes_ex(ctx->libctx, ptr, kl, 0) <= 0)
+ return 0;
+ DES_set_odd_parity(deskey);
+ if (kl >= 16) {
+ DES_set_odd_parity(deskey + 1);
+ if (kl >= 24)
+ DES_set_odd_parity(deskey + 2);
+ }
+ return 1;
+}
+
+int ossl_tdes_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (!ossl_cipher_generic_get_ctx_params(vctx, params))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_RANDOM_KEY);
+ if (p != NULL && !tdes_generatekey(ctx, p->data)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ return 0;
+ }
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM((PROV_TDES_CTX *)vctx, params))
+ return 0;
+ return 1;
+}
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_tdes)
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_tdes)
+
+int ossl_tdes_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM((PROV_TDES_CTX *)vctx,
+ OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK))
+ return 0;
+ return ossl_cipher_generic_set_ctx_params(vctx, params);
+}
+
+int ossl_tdes_get_params(OSSL_PARAM params[], unsigned int md, uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits)
+{
+#ifdef FIPS_MODULE
+ const int decrypt_only = 1;
+#else
+ const int decrypt_only = 0;
+#endif
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_DECRYPT_ONLY);
+ if (p != NULL && !OSSL_PARAM_set_int(p, decrypt_only)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ return ossl_cipher_generic_get_params(params, md, flags,
+ kbits, blkbits, ivbits);
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c
new file mode 100644
index 000000000000..3b8908ff3988
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_tdes_default.h"
+#include "prov/implementations.h"
+
+/* ossl_tdes_ede3_ofb_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, ofb, OFB, TDES_FLAGS, 64*3, 8, 64, stream);
+/* ossl_tdes_ede3_cfb_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, cfb, CFB, TDES_FLAGS, 64*3, 8, 64, stream);
+/* ossl_tdes_ede3_cfb1_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, cfb1, CFB, TDES_FLAGS, 64*3, 8, 64, stream);
+/* ossl_tdes_ede3_cfb8_functions */
+IMPLEMENT_tdes_cipher(ede3, EDE3, cfb8, CFB, TDES_FLAGS, 64*3, 8, 64, stream);
+
+/* ossl_tdes_ede2_ecb_functions */
+IMPLEMENT_tdes_cipher(ede2, EDE2, ecb, ECB, TDES_FLAGS, 64*2, 64, 0, block);
+/* ossl_tdes_ede2_cbc_functions */
+IMPLEMENT_tdes_cipher(ede2, EDE2, cbc, CBC, TDES_FLAGS, 64*2, 64, 64, block);
+/* ossl_tdes_ede2_ofb_functions */
+IMPLEMENT_tdes_cipher(ede2, EDE2, ofb, OFB, TDES_FLAGS, 64*2, 8, 64, stream);
+/* ossl_tdes_ede2_cfb_functions */
+IMPLEMENT_tdes_cipher(ede2, EDE2, cfb, CFB, TDES_FLAGS, 64*2, 8, 64, stream);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h
new file mode 100644
index 000000000000..dc8458b5dafa
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "cipher_tdes.h"
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_ofb(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb1(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede3_cfb8(void);
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_cbc(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_ecb(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_ofb(void);
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_ede2_cfb(void);
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_desx_cbc(void);
+
+const PROV_CIPHER_HW *ossl_prov_cipher_hw_tdes_wrap_cbc(void);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c
new file mode 100644
index 000000000000..ccdf3941c86b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_default_hw.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_tdes_default.h"
+
+#define ks1 tks.ks[0]
+#define ks2 tks.ks[1]
+#define ks3 tks.ks[2]
+
+static int ossl_cipher_hw_tdes_ede2_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ size_t keylen)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ DES_cblock *deskey = (DES_cblock *)key;
+
+ tctx->tstream.cbc = NULL;
+# if defined(SPARC_DES_CAPABLE)
+ if (SPARC_DES_CAPABLE) {
+ if (ctx->mode == EVP_CIPH_CBC_MODE) {
+ des_t4_key_expand(&deskey[0], &tctx->ks1);
+ des_t4_key_expand(&deskey[1], &tctx->ks2);
+ memcpy(&tctx->ks3, &tctx->ks1, sizeof(tctx->ks1));
+ tctx->tstream.cbc = ctx->enc ? des_t4_ede3_cbc_encrypt :
+ des_t4_ede3_cbc_decrypt;
+ return 1;
+ }
+ }
+# endif
+ DES_set_key_unchecked(&deskey[0], &tctx->ks1);
+ DES_set_key_unchecked(&deskey[1], &tctx->ks2);
+ memcpy(&tctx->ks3, &tctx->ks1, sizeof(tctx->ks1));
+ return 1;
+}
+
+static int ossl_cipher_hw_tdes_ofb(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ int num = ctx->num;
+
+ while (inl >= MAXCHUNK) {
+ DES_ede3_ofb64_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1, &tctx->ks2,
+ &tctx->ks3, (DES_cblock *)ctx->iv, &num);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0) {
+ DES_ede3_ofb64_encrypt(in, out, (long)inl, &tctx->ks1, &tctx->ks2,
+ &tctx->ks3, (DES_cblock *)ctx->iv, &num);
+ }
+ ctx->num = num;
+ return 1;
+}
+
+static int ossl_cipher_hw_tdes_cfb(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ int num = ctx->num;
+
+ while (inl >= MAXCHUNK) {
+
+ DES_ede3_cfb64_encrypt(in, out, (long)MAXCHUNK,
+ &tctx->ks1, &tctx->ks2, &tctx->ks3,
+ (DES_cblock *)ctx->iv, &num, ctx->enc);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0) {
+ DES_ede3_cfb64_encrypt(in, out, (long)inl,
+ &tctx->ks1, &tctx->ks2, &tctx->ks3,
+ (DES_cblock *)ctx->iv, &num, ctx->enc);
+ }
+ ctx->num = num;
+ return 1;
+}
+
+/*
+ * Although we have a CFB-r implementation for 3-DES, it doesn't pack the
+ * right way, so wrap it here
+ */
+static int ossl_cipher_hw_tdes_cfb1(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ size_t n;
+ unsigned char c[1];
+ unsigned char d[1] = { 0 };
+
+ if (ctx->use_bits == 0)
+ inl *= 8;
+ for (n = 0; n < inl; ++n) {
+ c[0] = (in[n / 8] & (1 << (7 - n % 8))) ? 0x80 : 0;
+ DES_ede3_cfb_encrypt(c, d, 1, 1,
+ &tctx->ks1, &tctx->ks2, &tctx->ks3,
+ (DES_cblock *)ctx->iv, ctx->enc);
+ out[n / 8] = (out[n / 8] & ~(0x80 >> (unsigned int)(n % 8)))
+ | ((d[0] & 0x80) >> (unsigned int)(n % 8));
+ }
+
+ return 1;
+}
+
+static int ossl_cipher_hw_tdes_cfb8(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+
+ while (inl >= MAXCHUNK) {
+ DES_ede3_cfb_encrypt(in, out, 8, (long)MAXCHUNK,
+ &tctx->ks1, &tctx->ks2, &tctx->ks3,
+ (DES_cblock *)ctx->iv, ctx->enc);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ DES_ede3_cfb_encrypt(in, out, 8, (long)inl,
+ &tctx->ks1, &tctx->ks2, &tctx->ks3,
+ (DES_cblock *)ctx->iv, ctx->enc);
+ return 1;
+}
+
+PROV_CIPHER_HW_tdes_mode(ede3, ofb)
+PROV_CIPHER_HW_tdes_mode(ede3, cfb)
+PROV_CIPHER_HW_tdes_mode(ede3, cfb1)
+PROV_CIPHER_HW_tdes_mode(ede3, cfb8)
+
+PROV_CIPHER_HW_tdes_mode(ede2, ecb)
+PROV_CIPHER_HW_tdes_mode(ede2, cbc)
+PROV_CIPHER_HW_tdes_mode(ede2, ofb)
+PROV_CIPHER_HW_tdes_mode(ede2, cfb)
+
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c
new file mode 100644
index 000000000000..4382969f44bb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_hw.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "prov/ciphercommon.h"
+#include "cipher_tdes.h"
+
+#define ks1 tks.ks[0]
+#define ks2 tks.ks[1]
+#define ks3 tks.ks[2]
+
+int ossl_cipher_hw_tdes_ede3_initkey(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+ DES_cblock *deskey = (DES_cblock *)key;
+
+ tctx->tstream.cbc = NULL;
+# if defined(SPARC_DES_CAPABLE)
+ if (SPARC_DES_CAPABLE) {
+ if (ctx->mode == EVP_CIPH_CBC_MODE) {
+ des_t4_key_expand(&deskey[0], &tctx->ks1);
+ des_t4_key_expand(&deskey[1], &tctx->ks2);
+ des_t4_key_expand(&deskey[2], &tctx->ks3);
+ tctx->tstream.cbc = ctx->enc ? des_t4_ede3_cbc_encrypt :
+ des_t4_ede3_cbc_decrypt;
+ return 1;
+ }
+ }
+# endif
+ DES_set_key_unchecked(&deskey[0], &tctx->ks1);
+ DES_set_key_unchecked(&deskey[1], &tctx->ks2);
+ DES_set_key_unchecked(&deskey[2], &tctx->ks3);
+ return 1;
+}
+
+void ossl_cipher_hw_tdes_copyctx(PROV_CIPHER_CTX *dst,
+ const PROV_CIPHER_CTX *src)
+{
+ PROV_TDES_CTX *sctx = (PROV_TDES_CTX *)src;
+ PROV_TDES_CTX *dctx = (PROV_TDES_CTX *)dst;
+
+ *dctx = *sctx;
+ dst->ks = &dctx->tks.ks;
+}
+
+int ossl_cipher_hw_tdes_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+
+ if (tctx->tstream.cbc != NULL) {
+ (*tctx->tstream.cbc) (in, out, inl, tctx->tks.ks, ctx->iv);
+ return 1;
+ }
+
+ while (inl >= MAXCHUNK) {
+ DES_ede3_cbc_encrypt(in, out, (long)MAXCHUNK, &tctx->ks1, &tctx->ks2,
+ &tctx->ks3, (DES_cblock *)ctx->iv, ctx->enc);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ DES_ede3_cbc_encrypt(in, out, (long)inl, &tctx->ks1, &tctx->ks2,
+ &tctx->ks3, (DES_cblock *)ctx->iv, ctx->enc);
+ return 1;
+}
+
+int ossl_cipher_hw_tdes_ecb(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ size_t i;
+ PROV_TDES_CTX *tctx = (PROV_TDES_CTX *)ctx;
+
+ if (len < DES_BLOCK_SIZE)
+ return 1;
+
+ for (i = 0, len -= DES_BLOCK_SIZE; i <= len; i += DES_BLOCK_SIZE) {
+ DES_ecb3_encrypt((const_DES_cblock *)(in + i), (DES_cblock *)(out + i),
+ &tctx->ks1, &tctx->ks2, &tctx->ks3, ctx->enc);
+ }
+ return 1;
+}
+
+PROV_CIPHER_HW_tdes_mode(ede3, ecb)
+PROV_CIPHER_HW_tdes_mode(ede3, cbc)
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c
new file mode 100644
index 000000000000..b48c02ae2581
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES and SHA-1 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "cipher_tdes_default.h"
+#include "crypto/evp.h"
+#include "crypto/sha.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+#define TDES_WRAP_FLAGS PROV_CIPHER_FLAG_CUSTOM_IV | PROV_CIPHER_FLAG_RAND_KEY
+
+static OSSL_FUNC_cipher_update_fn tdes_wrap_update;
+static OSSL_FUNC_cipher_cipher_fn tdes_wrap_cipher;
+
+static const unsigned char wrap_iv[8] = {
+ 0x4a, 0xdd, 0xa2, 0x2c, 0x79, 0xe8, 0x21, 0x05
+};
+
+static int des_ede3_unwrap(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ unsigned char icv[8], iv[TDES_IVLEN], sha1tmp[SHA_DIGEST_LENGTH];
+ int rv = -1;
+
+ if (inl < 24)
+ return -1;
+ if (out == NULL)
+ return inl - 16;
+
+ memcpy(ctx->iv, wrap_iv, 8);
+ /* Decrypt first block which will end up as icv */
+ ctx->hw->cipher(ctx, icv, in, 8);
+ /* Decrypt central blocks */
+ /*
+ * If decrypting in place move whole output along a block so the next
+ * des_ede_cbc_cipher is in place.
+ */
+ if (out == in) {
+ memmove(out, out + 8, inl - 8);
+ in -= 8;
+ }
+ ctx->hw->cipher(ctx, out, in + 8, inl - 16);
+ /* Decrypt final block which will be IV */
+ ctx->hw->cipher(ctx, iv, in + inl - 8, 8);
+ /* Reverse order of everything */
+ BUF_reverse(icv, NULL, 8);
+ BUF_reverse(out, NULL, inl - 16);
+ BUF_reverse(ctx->iv, iv, 8);
+ /* Decrypt again using new IV */
+ ctx->hw->cipher(ctx, out, out, inl - 16);
+ ctx->hw->cipher(ctx, icv, icv, 8);
+ if (ossl_sha1(out, inl - 16, sha1tmp) /* Work out hash of first portion */
+ && CRYPTO_memcmp(sha1tmp, icv, 8) == 0)
+ rv = inl - 16;
+ OPENSSL_cleanse(icv, 8);
+ OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH);
+ OPENSSL_cleanse(iv, 8);
+ OPENSSL_cleanse(ctx->iv, sizeof(ctx->iv));
+ if (rv == -1)
+ OPENSSL_cleanse(out, inl - 16);
+
+ return rv;
+}
+
+static int des_ede3_wrap(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ unsigned char sha1tmp[SHA_DIGEST_LENGTH];
+ size_t ivlen = TDES_IVLEN;
+ size_t icvlen = TDES_IVLEN;
+ size_t len = inl + ivlen + icvlen;
+
+ if (out == NULL)
+ return len;
+
+ /* Copy input to output buffer + 8 so we have space for IV */
+ memmove(out + ivlen, in, inl);
+ /* Work out ICV */
+ if (!ossl_sha1(in, inl, sha1tmp))
+ return 0;
+ memcpy(out + inl + ivlen, sha1tmp, icvlen);
+ OPENSSL_cleanse(sha1tmp, SHA_DIGEST_LENGTH);
+ /* Generate random IV */
+ if (RAND_bytes_ex(ctx->libctx, ctx->iv, ivlen, 0) <= 0)
+ return 0;
+ memcpy(out, ctx->iv, ivlen);
+ /* Encrypt everything after IV in place */
+ ctx->hw->cipher(ctx, out + ivlen, out + ivlen, inl + ivlen);
+ BUF_reverse(out, NULL, len);
+ memcpy(ctx->iv, wrap_iv, ivlen);
+ ctx->hw->cipher(ctx, out, out, len);
+ return len;
+}
+
+static int tdes_wrap_cipher_internal(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ /*
+ * Sanity check input length: we typically only wrap keys so EVP_MAXCHUNK
+ * is more than will ever be needed. Also input length must be a multiple
+ * of 8 bits.
+ */
+ if (inl >= EVP_MAXCHUNK || inl % 8)
+ return -1;
+ if (ctx->enc)
+ return des_ede3_wrap(ctx, out, in, inl);
+ else
+ return des_ede3_unwrap(ctx, out, in, inl);
+}
+
+static int tdes_wrap_cipher(void *vctx,
+ unsigned char *out, size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ int ret;
+
+ *outl = 0;
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ ret = tdes_wrap_cipher_internal(ctx, out, in, inl);
+ if (ret <= 0)
+ return 0;
+
+ *outl = ret;
+ return 1;
+}
+
+static int tdes_wrap_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ *outl = 0;
+ if (inl == 0)
+ return 1;
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!tdes_wrap_cipher(vctx, out, outl, outsize, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ return 1;
+}
+
+
+# define IMPLEMENT_WRAP_CIPHER(flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_newctx_fn tdes_wrap_newctx; \
+static void *tdes_wrap_newctx(void *provctx) \
+{ \
+ return ossl_tdes_newctx(provctx, EVP_CIPH_WRAP_MODE, kbits, blkbits, \
+ ivbits, flags, \
+ ossl_prov_cipher_hw_tdes_wrap_cbc()); \
+} \
+static OSSL_FUNC_cipher_get_params_fn tdes_wrap_get_params; \
+static int tdes_wrap_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_WRAP_MODE, flags, \
+ kbits, blkbits, ivbits); \
+} \
+const OSSL_DISPATCH ossl_tdes_wrap_cbc_functions[] = \
+{ \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void)) ossl_tdes_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void)) ossl_tdes_dinit }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))tdes_wrap_cipher }, \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))tdes_wrap_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))ossl_tdes_freectx }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))tdes_wrap_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, \
+ (void (*)(void))ossl_cipher_generic_stream_final }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, (void (*)(void))tdes_wrap_get_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_tdes_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+/* ossl_tdes_wrap_cbc_functions */
+IMPLEMENT_WRAP_CIPHER(TDES_WRAP_FLAGS, 64*3, 64, 0);
diff --git a/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c
new file mode 100644
index 000000000000..3c8c83332070
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/cipher_tdes_wrap_hw.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include "cipher_tdes_default.h"
+
+#define ossl_cipher_hw_tdes_wrap_initkey ossl_cipher_hw_tdes_ede3_initkey
+
+PROV_CIPHER_HW_tdes_mode(wrap, cbc)
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon.c
new file mode 100644
index 000000000000..b1331b5b64f1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon.c
@@ -0,0 +1,736 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Generic dispatch table functions for ciphers.
+ */
+
+/* For SSL3_VERSION */
+#include <openssl/prov_ssl.h>
+#include <openssl/proverr.h>
+#include "ciphercommon_local.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "internal/skey.h"
+#include "crypto/types.h"
+
+/*-
+ * Generic cipher functions for OSSL_PARAM gettables and settables
+ */
+static const OSSL_PARAM cipher_known_gettable_params[] = {
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_MODE, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL),
+ OSSL_PARAM_int(OSSL_CIPHER_PARAM_AEAD, NULL),
+ OSSL_PARAM_int(OSSL_CIPHER_PARAM_CUSTOM_IV, NULL),
+ OSSL_PARAM_int(OSSL_CIPHER_PARAM_CTS, NULL),
+ OSSL_PARAM_int(OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK, NULL),
+ OSSL_PARAM_int(OSSL_CIPHER_PARAM_HAS_RAND_KEY, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *ossl_cipher_generic_gettable_params(ossl_unused void *provctx)
+{
+ return cipher_known_gettable_params;
+}
+
+int ossl_cipher_generic_get_params(OSSL_PARAM params[], unsigned int md,
+ uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits)
+{
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_MODE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_AEAD) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CUSTOM_IV);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_CUSTOM_IV) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_CTS);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_CTS) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS1_MULTIBLOCK);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_TLS1_MULTIBLOCK) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_HAS_RAND_KEY);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_CIPHER_FLAG_RAND_KEY) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, kbits / 8)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_BLOCK_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, blkbits / 8)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ivbits / 8)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(ossl_cipher_generic)
+{ OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED },
+CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(ossl_cipher_generic)
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_cipher_generic)
+OSSL_PARAM_uint(OSSL_CIPHER_PARAM_USE_BITS, NULL),
+OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL),
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_cipher_generic)
+
+/*
+ * Variable key length cipher functions for OSSL_PARAM settables
+ */
+int ossl_cipher_var_keylen_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_cipher_generic_set_ctx_params(vctx, params))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL) {
+ size_t keylen;
+
+ if (!OSSL_PARAM_get_size_t(p, &keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ctx->keylen != keylen) {
+ ctx->keylen = keylen;
+ ctx->key_set = 0;
+ }
+ }
+ return 1;
+}
+
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(ossl_cipher_var_keylen)
+OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(ossl_cipher_var_keylen)
+
+/*-
+ * AEAD cipher functions for OSSL_PARAM gettables and settables
+ */
+static const OSSL_PARAM cipher_aead_known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TAGLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN, NULL, 0),
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_AEAD_IV_GENERATED, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *ossl_cipher_aead_gettable_ctx_params(
+ ossl_unused void *cctx, ossl_unused void *provctx
+ )
+{
+ return cipher_aead_known_gettable_ctx_params;
+}
+
+static const OSSL_PARAM cipher_aead_known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_AEAD_IVLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_AAD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV, NULL, 0),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *ossl_cipher_aead_settable_ctx_params(
+ ossl_unused void *cctx, ossl_unused void *provctx
+ )
+{
+ return cipher_aead_known_settable_ctx_params;
+}
+
+void ossl_cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx)
+{
+ if (ctx != NULL && ctx->alloced) {
+ OPENSSL_free(ctx->tlsmac);
+ ctx->alloced = 0;
+ ctx->tlsmac = NULL;
+ }
+}
+
+static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ ctx->num = 0;
+ ctx->bufsz = 0;
+ ctx->updated = 0;
+ ctx->enc = enc ? 1 : 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (iv != NULL && ctx->mode != EVP_CIPH_ECB_MODE) {
+ if (!ossl_cipher_generic_initiv(ctx, iv, ivlen))
+ return 0;
+ }
+ if (iv == NULL && ctx->iv_set
+ && (ctx->mode == EVP_CIPH_CBC_MODE
+ || ctx->mode == EVP_CIPH_CFB_MODE
+ || ctx->mode == EVP_CIPH_OFB_MODE))
+ /* reset IV for these modes to keep compatibility with 1.1.1 */
+ memcpy(ctx->iv, ctx->oiv, ctx->ivlen);
+
+ if (key != NULL) {
+ if (ctx->variable_keylength == 0) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ } else {
+ ctx->keylen = keylen;
+ }
+ if (!ctx->hw->init(ctx, key, ctx->keylen))
+ return 0;
+ ctx->key_set = 1;
+ }
+ return ossl_cipher_generic_set_ctx_params(ctx, params);
+}
+
+int ossl_cipher_generic_einit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen,
+ iv, ivlen, params, 1);
+}
+
+int ossl_cipher_generic_dinit(void *vctx, const unsigned char *key,
+ size_t keylen, const unsigned char *iv,
+ size_t ivlen, const OSSL_PARAM params[])
+{
+ return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx, key, keylen,
+ iv, ivlen, params, 0);
+}
+
+int ossl_cipher_generic_skey_einit(void *vctx, void *skeydata,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ PROV_SKEY *key = skeydata;
+
+ return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx,
+ key->data, key->length,
+ iv, ivlen, params, 1);
+}
+
+int ossl_cipher_generic_skey_dinit(void *vctx, void *skeydata,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ PROV_SKEY *key = skeydata;
+
+ return cipher_generic_init_internal((PROV_CIPHER_CTX *)vctx,
+ key->data, key->length,
+ iv, ivlen, params, 0);
+}
+
+/* Max padding including padding length byte */
+#define MAX_PADDING 256
+
+int ossl_cipher_generic_block_update(void *vctx, unsigned char *out,
+ size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ size_t outlint = 0;
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ size_t blksz = ctx->blocksize;
+ size_t nextblocks;
+
+ if (!ctx->key_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (ctx->tlsversion > 0) {
+ /*
+ * Each update call corresponds to a TLS record and is individually
+ * padded
+ */
+
+ /* Sanity check inputs */
+ if (in == NULL
+ || in != out
+ || outsize < inl
+ || !ctx->pad) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ if (ctx->enc) {
+ unsigned char padval;
+ size_t padnum, loop;
+
+ /* Add padding */
+
+ padnum = blksz - (inl % blksz);
+
+ if (outsize < inl + padnum) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ if (padnum > MAX_PADDING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ padval = (unsigned char)(padnum - 1);
+ if (ctx->tlsversion == SSL3_VERSION) {
+ if (padnum > 1)
+ memset(out + inl, 0, padnum - 1);
+ *(out + inl + padnum - 1) = padval;
+ } else {
+ /* we need to add 'padnum' padding bytes of value padval */
+ for (loop = inl; loop < inl + padnum; loop++)
+ out[loop] = padval;
+ }
+ inl += padnum;
+ }
+
+ if ((inl % blksz) != 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+
+ /* Shouldn't normally fail */
+ if (!ctx->hw->cipher(ctx, out, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ if (ctx->alloced) {
+ OPENSSL_free(ctx->tlsmac);
+ ctx->alloced = 0;
+ ctx->tlsmac = NULL;
+ }
+
+ /* This only fails if padding is publicly invalid */
+ *outl = inl;
+ if (!ctx->enc
+ && !ossl_cipher_tlsunpadblock(ctx->libctx, ctx->tlsversion,
+ out, outl,
+ blksz, &ctx->tlsmac, &ctx->alloced,
+ ctx->tlsmacsize, 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ return 1;
+ }
+
+ if (ctx->bufsz != 0)
+ nextblocks = ossl_cipher_fillblock(ctx->buf, &ctx->bufsz, blksz,
+ &in, &inl);
+ else
+ nextblocks = inl & ~(blksz-1);
+
+ /*
+ * If we're decrypting and we end an update on a block boundary we hold
+ * the last block back in case this is the last update call and the last
+ * block is padded.
+ */
+ if (ctx->bufsz == blksz && (ctx->enc || inl > 0 || !ctx->pad)) {
+ if (outsize < blksz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ ctx->bufsz = 0;
+ outlint = blksz;
+ out += blksz;
+ }
+ if (nextblocks > 0) {
+ if (!ctx->enc && ctx->pad && nextblocks == inl) {
+ if (!ossl_assert(inl >= blksz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ nextblocks -= blksz;
+ }
+ outlint += nextblocks;
+ if (outsize < outlint) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ }
+ if (nextblocks > 0) {
+ if (!ctx->hw->cipher(ctx, out, in, nextblocks)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ in += nextblocks;
+ inl -= nextblocks;
+ }
+ if (inl != 0
+ && !ossl_cipher_trailingdata(ctx->buf, &ctx->bufsz, blksz, &in, &inl)) {
+ /* ERR_raise already called */
+ return 0;
+ }
+
+ *outl = outlint;
+ return inl == 0;
+}
+
+int ossl_cipher_generic_block_final(void *vctx, unsigned char *out,
+ size_t *outl, size_t outsize)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ size_t blksz = ctx->blocksize;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ctx->key_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (ctx->tlsversion > 0) {
+ /* We never finalize TLS, so this is an error */
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ if (ctx->enc) {
+ if (ctx->pad) {
+ ossl_cipher_padblock(ctx->buf, &ctx->bufsz, blksz);
+ } else if (ctx->bufsz == 0) {
+ *outl = 0;
+ return 1;
+ } else if (ctx->bufsz != blksz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH);
+ return 0;
+ }
+
+ if (outsize < blksz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!ctx->hw->cipher(ctx, out, ctx->buf, blksz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ ctx->bufsz = 0;
+ *outl = blksz;
+ return 1;
+ }
+
+ /* Decrypting */
+ if (ctx->bufsz != blksz) {
+ if (ctx->bufsz == 0 && !ctx->pad) {
+ *outl = 0;
+ return 1;
+ }
+ ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH);
+ return 0;
+ }
+
+ if (!ctx->hw->cipher(ctx, ctx->buf, ctx->buf, blksz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ if (ctx->pad && !ossl_cipher_unpadblock(ctx->buf, &ctx->bufsz, blksz)) {
+ /* ERR_raise already called */
+ return 0;
+ }
+
+ if (outsize < ctx->bufsz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ memcpy(out, ctx->buf, ctx->bufsz);
+ *outl = ctx->bufsz;
+ ctx->bufsz = 0;
+ return 1;
+}
+
+int ossl_cipher_generic_stream_update(void *vctx, unsigned char *out,
+ size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if (!ctx->key_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (inl == 0) {
+ *outl = 0;
+ return 1;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!ctx->hw->cipher(ctx, out, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ *outl = inl;
+ if (!ctx->enc && ctx->tlsversion > 0) {
+ /*
+ * Remove any TLS padding. Only used by cipher_aes_cbc_hmac_sha1_hw.c and
+ * cipher_aes_cbc_hmac_sha256_hw.c
+ */
+ if (ctx->removetlspad) {
+ /*
+ * We should have already failed in the cipher() call above if this
+ * isn't true.
+ */
+ if (!ossl_assert(*outl >= (size_t)(out[inl - 1] + 1)))
+ return 0;
+ /* The actual padding length */
+ *outl -= out[inl - 1] + 1;
+ }
+
+ /* TLS MAC and explicit IV if relevant. We should have already failed
+ * in the cipher() call above if *outl is too short.
+ */
+ if (!ossl_assert(*outl >= ctx->removetlsfixed))
+ return 0;
+ *outl -= ctx->removetlsfixed;
+
+ /* Extract the MAC if there is one */
+ if (ctx->tlsmacsize > 0) {
+ if (*outl < ctx->tlsmacsize)
+ return 0;
+
+ ctx->tlsmac = out + *outl - ctx->tlsmacsize;
+ *outl -= ctx->tlsmacsize;
+ }
+ }
+
+ return 1;
+}
+int ossl_cipher_generic_stream_final(void *vctx, unsigned char *out,
+ size_t *outl, size_t outsize)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ctx->key_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ *outl = 0;
+ return 1;
+}
+
+int ossl_cipher_generic_cipher(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ctx->key_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!ctx->hw->cipher(ctx, out, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+
+ *outl = inl;
+ return 1;
+}
+
+int ossl_cipher_generic_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_PADDING);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->pad)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->oiv, ctx->ivlen)
+ && !OSSL_PARAM_set_octet_string(p, &ctx->oiv, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)
+ && !OSSL_PARAM_set_octet_string(p, &ctx->iv, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_NUM);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->num)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+int ossl_cipher_generic_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_PADDING);
+ if (p != NULL) {
+ unsigned int pad;
+
+ if (!OSSL_PARAM_get_uint(p, &pad)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->pad = pad ? 1 : 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_USE_BITS);
+ if (p != NULL) {
+ unsigned int bits;
+
+ if (!OSSL_PARAM_get_uint(p, &bits)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->use_bits = bits ? 1 : 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &ctx->tlsversion)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_NUM);
+ if (p != NULL) {
+ unsigned int num;
+
+ if (!OSSL_PARAM_get_uint(p, &num)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ctx->num = num;
+ }
+ return 1;
+}
+
+int ossl_cipher_generic_initiv(PROV_CIPHER_CTX *ctx, const unsigned char *iv,
+ size_t ivlen)
+{
+ if (ivlen != ctx->ivlen
+ || ivlen > sizeof(ctx->iv)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ ctx->iv_set = 1;
+ memcpy(ctx->iv, iv, ivlen);
+ memcpy(ctx->oiv, iv, ivlen);
+ return 1;
+}
+
+void ossl_cipher_generic_initkey(void *vctx, size_t kbits, size_t blkbits,
+ size_t ivbits, unsigned int mode,
+ uint64_t flags, const PROV_CIPHER_HW *hw,
+ void *provctx)
+{
+ PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
+
+ if ((flags & PROV_CIPHER_FLAG_INVERSE_CIPHER) != 0)
+ ctx->inverse_cipher = 1;
+ if ((flags & PROV_CIPHER_FLAG_VARIABLE_LENGTH) != 0)
+ ctx->variable_keylength = 1;
+
+ ctx->pad = 1;
+ ctx->keylen = ((kbits) / 8);
+ ctx->ivlen = ((ivbits) / 8);
+ ctx->hw = hw;
+ ctx->mode = mode;
+ ctx->blocksize = blkbits / 8;
+ if (provctx != NULL)
+ ctx->libctx = PROV_LIBCTX_OF(provctx); /* used for rand */
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c
new file mode 100644
index 000000000000..cfc78e07709f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_block.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+/* For SSL3_VERSION, TLS1_VERSION etc */
+#include <openssl/prov_ssl.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "internal/constant_time.h"
+#include "internal/ssl3_cbc.h"
+#include "ciphercommon_local.h"
+
+/*
+ * Fills a single block of buffered data from the input, and returns the amount
+ * of data remaining in the input that is a multiple of the blocksize. The buffer
+ * is only filled if it already has some data in it, isn't full already or we
+ * don't have at least one block in the input.
+ *
+ * buf: a buffer of blocksize bytes
+ * buflen: contains the amount of data already in buf on entry. Updated with the
+ * amount of data in buf at the end. On entry *buflen must always be
+ * less than the blocksize
+ * blocksize: size of a block. Must be greater than 0 and a power of 2
+ * in: pointer to a pointer containing the input data
+ * inlen: amount of input data available
+ *
+ * On return buf is filled with as much data as possible up to a full block,
+ * *buflen is updated containing the amount of data in buf. *in is updated to
+ * the new location where input data should be read from, *inlen is updated with
+ * the remaining amount of data in *in. Returns the largest value <= *inlen
+ * which is a multiple of the blocksize.
+ */
+size_t ossl_cipher_fillblock(unsigned char *buf, size_t *buflen,
+ size_t blocksize,
+ const unsigned char **in, size_t *inlen)
+{
+ size_t blockmask = ~(blocksize - 1);
+ size_t bufremain = blocksize - *buflen;
+
+ assert(*buflen <= blocksize);
+ assert(blocksize > 0 && (blocksize & (blocksize - 1)) == 0);
+
+ if (*inlen < bufremain)
+ bufremain = *inlen;
+ memcpy(buf + *buflen, *in, bufremain);
+ *in += bufremain;
+ *inlen -= bufremain;
+ *buflen += bufremain;
+
+ return *inlen & blockmask;
+}
+
+/*
+ * Fills the buffer with trailing data from an encryption/decryption that didn't
+ * fit into a full block.
+ */
+int ossl_cipher_trailingdata(unsigned char *buf, size_t *buflen, size_t blocksize,
+ const unsigned char **in, size_t *inlen)
+{
+ if (*inlen == 0)
+ return 1;
+
+ if (*buflen + *inlen > blocksize) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ memcpy(buf + *buflen, *in, *inlen);
+ *buflen += *inlen;
+ *inlen = 0;
+
+ return 1;
+}
+
+/* Pad the final block for encryption */
+void ossl_cipher_padblock(unsigned char *buf, size_t *buflen, size_t blocksize)
+{
+ size_t i;
+ unsigned char pad = (unsigned char)(blocksize - *buflen);
+
+ for (i = *buflen; i < blocksize; i++)
+ buf[i] = pad;
+}
+
+int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize)
+{
+ size_t pad, i;
+ size_t len = *buflen;
+
+ if (len != blocksize) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ /*
+ * The following assumes that the ciphertext has been authenticated.
+ * Otherwise it provides a padding oracle.
+ */
+ pad = buf[blocksize - 1];
+ if (pad == 0 || pad > blocksize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT);
+ return 0;
+ }
+ for (i = 0; i < pad; i++) {
+ if (buf[--len] != pad) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT);
+ return 0;
+ }
+ }
+ *buflen = len;
+ return 1;
+}
+
+/*-
+ * ossl_cipher_tlsunpadblock removes the CBC padding from the decrypted, TLS, CBC
+ * record in constant time. Also removes the MAC from the record in constant
+ * time.
+ *
+ * libctx: Our library context
+ * tlsversion: The TLS version in use, e.g. SSL3_VERSION, TLS1_VERSION, etc
+ * buf: The decrypted TLS record data
+ * buflen: The length of the decrypted TLS record data. Updated with the new
+ * length after the padding is removed
+ * block_size: the block size of the cipher used to encrypt the record.
+ * mac: Location to store the pointer to the MAC
+ * alloced: Whether the MAC is stored in a newly allocated buffer, or whether
+ * *mac points into *buf
+ * macsize: the size of the MAC inside the record (or 0 if there isn't one)
+ * aead: whether this is an aead cipher
+ * returns:
+ * 0: (in non-constant time) if the record is publicly invalid.
+ * 1: (in constant time) Record is publicly valid. If padding is invalid then
+ * the mac is random
+ */
+int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion,
+ unsigned char *buf, size_t *buflen,
+ size_t blocksize,
+ unsigned char **mac, int *alloced, size_t macsize,
+ int aead)
+{
+ int ret;
+
+ switch (tlsversion) {
+ case SSL3_VERSION:
+ return ssl3_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
+ alloced, blocksize, macsize,
+ libctx);
+
+ case TLS1_2_VERSION:
+ case DTLS1_2_VERSION:
+ case TLS1_1_VERSION:
+ case DTLS1_VERSION:
+ case DTLS1_BAD_VER:
+ /* Remove the explicit IV */
+ buf += blocksize;
+ *buflen -= blocksize;
+ /* Fall through */
+ case TLS1_VERSION:
+ ret = tls1_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
+ alloced, blocksize, macsize,
+ aead, libctx);
+ return ret;
+
+ default:
+ return 0;
+ }
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c
new file mode 100644
index 000000000000..80008a815182
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for ccm mode */
+
+#include <openssl/proverr.h>
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_ccm.h"
+#include "prov/providercommon.h"
+
+static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len);
+
+static int ccm_tls_init(PROV_CCM_CTX *ctx, unsigned char *aad, size_t alen)
+{
+ size_t len;
+
+ if (!ossl_prov_is_running() || alen != EVP_AEAD_TLS1_AAD_LEN)
+ return 0;
+
+ /* Save the aad for later use. */
+ memcpy(ctx->buf, aad, alen);
+ ctx->tls_aad_len = alen;
+
+ len = ctx->buf[alen - 2] << 8 | ctx->buf[alen - 1];
+ if (len < EVP_CCM_TLS_EXPLICIT_IV_LEN)
+ return 0;
+
+ /* Correct length for explicit iv. */
+ len -= EVP_CCM_TLS_EXPLICIT_IV_LEN;
+
+ if (!ctx->enc) {
+ if (len < ctx->m)
+ return 0;
+ /* Correct length for tag. */
+ len -= ctx->m;
+ }
+ ctx->buf[alen - 2] = (unsigned char)(len >> 8);
+ ctx->buf[alen - 1] = (unsigned char)(len & 0xff);
+
+ /* Extra padding: tag appended to record. */
+ return ctx->m;
+}
+
+static int ccm_tls_iv_set_fixed(PROV_CCM_CTX *ctx, unsigned char *fixed,
+ size_t flen)
+{
+ if (flen != EVP_CCM_TLS_FIXED_IV_LEN)
+ return 0;
+
+ /* Copy to first part of the iv. */
+ memcpy(ctx->iv, fixed, flen);
+ return 1;
+}
+
+static size_t ccm_get_ivlen(PROV_CCM_CTX *ctx)
+{
+ return 15 - ctx->l;
+}
+
+int ossl_ccm_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t sz;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if ((p->data_size & 1) || (p->data_size < 4) || p->data_size > 16) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG_LENGTH);
+ return 0;
+ }
+
+ if (p->data != NULL) {
+ if (ctx->enc) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_NEEDED);
+ return 0;
+ }
+ memcpy(ctx->buf, p->data, p->data_size);
+ ctx->tag_set = 1;
+ }
+ ctx->m = p->data_size;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_IVLEN);
+ if (p != NULL) {
+ size_t ivlen;
+
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ ivlen = 15 - sz;
+ if (ivlen < 2 || ivlen > 8) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (ctx->l != ivlen) {
+ ctx->l = ivlen;
+ ctx->iv_set = 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ sz = ccm_tls_init(ctx, p->data, p->data_size);
+ if (sz == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
+ return 0;
+ }
+ ctx->tls_aad_pad_sz = sz;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (ccm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+int ossl_ccm_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IVLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ccm_get_ivlen(ctx))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAGLEN);
+ if (p != NULL) {
+ size_t m = ctx->m;
+
+ if (!OSSL_PARAM_set_size_t(p, m)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_IV);
+ if (p != NULL) {
+ if (ccm_get_ivlen(ctx) > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_UPDATED_IV);
+ if (p != NULL) {
+ if (ccm_get_ivlen(ctx) > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->iv, p->data_size)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, p->data_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_KEYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_AEAD_TAG);
+ if (p != NULL) {
+ if (!ctx->enc || !ctx->tag_set) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_TAG_NOT_SET);
+ return 0;
+ }
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ if (!ctx->hw->gettag(ctx, p->data, p->data_size))
+ return 0;
+ ctx->tag_set = 0;
+ ctx->iv_set = 0;
+ ctx->len_set = 0;
+ }
+ return 1;
+}
+
+static int ccm_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (ivlen != ccm_get_ivlen(ctx)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ memcpy(ctx->iv, iv, ivlen);
+ ctx->iv_set = 1;
+ }
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->setkey(ctx, key, keylen))
+ return 0;
+ }
+ return ossl_ccm_set_ctx_params(ctx, params);
+}
+
+int ossl_ccm_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ccm_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+int ossl_ccm_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return ccm_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+int ossl_ccm_stream_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in,
+ size_t inl)
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (!ccm_cipher_internal(ctx, out, outl, in, inl)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ return 1;
+}
+
+int ossl_ccm_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+ int i;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ i = ccm_cipher_internal(ctx, out, outl, NULL, 0);
+ if (i <= 0)
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+int ossl_ccm_cipher(void *vctx, unsigned char *out, size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_CCM_CTX *ctx = (PROV_CCM_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (ccm_cipher_internal(ctx, out, outl, in, inl) <= 0)
+ return 0;
+
+ *outl = inl;
+ return 1;
+}
+
+/* Copy the buffered iv */
+static int ccm_set_iv(PROV_CCM_CTX *ctx, size_t mlen)
+{
+ const PROV_CCM_HW *hw = ctx->hw;
+
+ if (!hw->setiv(ctx, ctx->iv, ccm_get_ivlen(ctx), mlen))
+ return 0;
+ ctx->len_set = 1;
+ return 1;
+}
+
+static int ccm_tls_cipher(PROV_CCM_CTX *ctx,
+ unsigned char *out, size_t *padlen,
+ const unsigned char *in, size_t len)
+{
+ int rv = 0;
+ size_t olen = 0;
+
+ if (!ossl_prov_is_running())
+ goto err;
+
+ /* Encrypt/decrypt must be performed in place */
+ if (in == NULL || out != in || len < EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m)
+ goto err;
+
+ /* If encrypting set explicit IV from sequence number (start of AAD) */
+ if (ctx->enc)
+ memcpy(out, ctx->buf, EVP_CCM_TLS_EXPLICIT_IV_LEN);
+ /* Get rest of IV from explicit IV */
+ memcpy(ctx->iv + EVP_CCM_TLS_FIXED_IV_LEN, in, EVP_CCM_TLS_EXPLICIT_IV_LEN);
+ /* Correct length value */
+ len -= EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m;
+ if (!ccm_set_iv(ctx, len))
+ goto err;
+
+ /* Use saved AAD */
+ if (!ctx->hw->setaad(ctx, ctx->buf, ctx->tls_aad_len))
+ goto err;
+
+ /* Fix buffer to point to payload */
+ in += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+ out += EVP_CCM_TLS_EXPLICIT_IV_LEN;
+ if (ctx->enc) {
+ if (!ctx->hw->auth_encrypt(ctx, in, out, len, out + len, ctx->m))
+ goto err;
+ olen = len + EVP_CCM_TLS_EXPLICIT_IV_LEN + ctx->m;
+ } else {
+ if (!ctx->hw->auth_decrypt(ctx, in, out, len,
+ (unsigned char *)in + len, ctx->m))
+ goto err;
+ olen = len;
+ }
+ rv = 1;
+err:
+ *padlen = olen;
+ return rv;
+}
+
+static int ccm_cipher_internal(PROV_CCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len)
+{
+ int rv = 0;
+ size_t olen = 0;
+ const PROV_CCM_HW *hw = ctx->hw;
+
+ /* If no key set, return error */
+ if (!ctx->key_set)
+ return 0;
+
+ if (ctx->tls_aad_len != UNINITIALISED_SIZET)
+ return ccm_tls_cipher(ctx, out, padlen, in, len);
+
+ /* EVP_*Final() doesn't return any data */
+ if (in == NULL && out != NULL)
+ goto finish;
+
+ if (!ctx->iv_set)
+ goto err;
+
+ if (out == NULL) {
+ if (in == NULL) {
+ if (!ccm_set_iv(ctx, len))
+ goto err;
+ } else {
+ /* If we have AAD, we need a message length */
+ if (!ctx->len_set && len)
+ goto err;
+ if (!hw->setaad(ctx, in, len))
+ goto err;
+ }
+ } else {
+ /* If not set length yet do it */
+ if (!ctx->len_set && !ccm_set_iv(ctx, len))
+ goto err;
+
+ if (ctx->enc) {
+ if (!hw->auth_encrypt(ctx, in, out, len, NULL, 0))
+ goto err;
+ ctx->tag_set = 1;
+ } else {
+ /* The tag must be set before actually decrypting data */
+ if (!ctx->tag_set)
+ goto err;
+
+ if (!hw->auth_decrypt(ctx, in, out, len, ctx->buf, ctx->m))
+ goto err;
+ /* Finished - reset flags so calling this method again will fail */
+ ctx->iv_set = 0;
+ ctx->tag_set = 0;
+ ctx->len_set = 0;
+ }
+ }
+ olen = len;
+finish:
+ rv = 1;
+err:
+ *padlen = olen;
+ return rv;
+}
+
+void ossl_ccm_initctx(PROV_CCM_CTX *ctx, size_t keybits, const PROV_CCM_HW *hw)
+{
+ ctx->keylen = keybits / 8;
+ ctx->key_set = 0;
+ ctx->iv_set = 0;
+ ctx->tag_set = 0;
+ ctx->len_set = 0;
+ ctx->l = 8;
+ ctx->m = 12;
+ ctx->tls_aad_len = UNINITIALISED_SIZET;
+ ctx->hw = hw;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c
new file mode 100644
index 000000000000..ad3fbc59e4fc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_ccm_hw.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_ccm.h"
+
+int ossl_ccm_generic_setiv(PROV_CCM_CTX *ctx, const unsigned char *nonce,
+ size_t nlen, size_t mlen)
+{
+ return CRYPTO_ccm128_setiv(&ctx->ccm_ctx, nonce, nlen, mlen) == 0;
+}
+
+int ossl_ccm_generic_setaad(PROV_CCM_CTX *ctx, const unsigned char *aad,
+ size_t alen)
+{
+ CRYPTO_ccm128_aad(&ctx->ccm_ctx, aad, alen);
+ return 1;
+}
+
+int ossl_ccm_generic_gettag(PROV_CCM_CTX *ctx, unsigned char *tag, size_t tlen)
+{
+ return CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, tlen) > 0;
+}
+
+int ossl_ccm_generic_auth_encrypt(PROV_CCM_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *tag, size_t taglen)
+{
+ int rv;
+
+ if (ctx->str != NULL)
+ rv = CRYPTO_ccm128_encrypt_ccm64(&ctx->ccm_ctx, in,
+ out, len, ctx->str) == 0;
+ else
+ rv = CRYPTO_ccm128_encrypt(&ctx->ccm_ctx, in, out, len) == 0;
+
+ if (rv == 1 && tag != NULL)
+ rv = (CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, taglen) > 0);
+ return rv;
+}
+
+int ossl_ccm_generic_auth_decrypt(PROV_CCM_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *expected_tag, size_t taglen)
+{
+ int rv = 0;
+
+ if (ctx->str != NULL)
+ rv = CRYPTO_ccm128_decrypt_ccm64(&ctx->ccm_ctx, in, out, len,
+ ctx->str) == 0;
+ else
+ rv = CRYPTO_ccm128_decrypt(&ctx->ccm_ctx, in, out, len) == 0;
+ if (rv) {
+ unsigned char tag[16];
+
+ if (!CRYPTO_ccm128_tag(&ctx->ccm_ctx, tag, taglen)
+ || CRYPTO_memcmp(tag, expected_tag, taglen) != 0)
+ rv = 0;
+ }
+ if (rv == 0)
+ OPENSSL_cleanse(out, len);
+ return rv;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c
new file mode 100644
index 000000000000..475693f336a4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Dispatch functions for gcm mode */
+
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_gcm.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "internal/param_names.h"
+
+static int gcm_tls_init(PROV_GCM_CTX *dat, unsigned char *aad, size_t aad_len);
+static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv,
+ size_t len);
+static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
+ const unsigned char *in, size_t len);
+static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len);
+
+/*
+ * Called from EVP_CipherInit when there is currently no context via
+ * the new_ctx() function
+ */
+void ossl_gcm_initctx(void *provctx, PROV_GCM_CTX *ctx, size_t keybits,
+ const PROV_GCM_HW *hw)
+{
+ ctx->pad = 1;
+ ctx->mode = EVP_CIPH_GCM_MODE;
+ ctx->taglen = UNINITIALISED_SIZET;
+ ctx->tls_aad_len = UNINITIALISED_SIZET;
+ ctx->ivlen = (EVP_GCM_TLS_FIXED_IV_LEN + EVP_GCM_TLS_EXPLICIT_IV_LEN);
+ ctx->keylen = keybits / 8;
+ ctx->hw = hw;
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+}
+
+/*
+ * Called by EVP_CipherInit via the _einit and _dinit functions
+ */
+static int gcm_init(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[], int enc)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx->enc = enc;
+
+ if (iv != NULL) {
+ if (ivlen == 0 || ivlen > sizeof(ctx->iv)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ ctx->ivlen = ivlen;
+ memcpy(ctx->iv, iv, ivlen);
+ ctx->iv_state = IV_STATE_BUFFERED;
+ }
+
+ if (key != NULL) {
+ if (keylen != ctx->keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!ctx->hw->setkey(ctx, key, ctx->keylen))
+ return 0;
+ ctx->tls_enc_records = 0;
+ }
+ return ossl_gcm_set_ctx_params(ctx, params);
+}
+
+int ossl_gcm_einit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return gcm_init(vctx, key, keylen, iv, ivlen, params, 1);
+}
+
+int ossl_gcm_dinit(void *vctx, const unsigned char *key, size_t keylen,
+ const unsigned char *iv, size_t ivlen,
+ const OSSL_PARAM params[])
+{
+ return gcm_init(vctx, key, keylen, iv, ivlen, params, 0);
+}
+
+/* increment counter (64-bit int) by 1 */
+static void ctr64_inc(unsigned char *counter)
+{
+ int n = 8;
+ unsigned char c;
+
+ do {
+ --n;
+ c = counter[n];
+ ++c;
+ counter[n] = c;
+ if (c > 0)
+ return;
+ } while (n > 0);
+}
+
+static int getivgen(PROV_GCM_CTX *ctx, unsigned char *out, size_t olen)
+{
+ if (!ctx->iv_gen
+ || !ctx->key_set
+ || !ctx->hw->setiv(ctx, ctx->iv, ctx->ivlen))
+ return 0;
+ if (olen == 0 || olen > ctx->ivlen)
+ olen = ctx->ivlen;
+ memcpy(out, ctx->iv + ctx->ivlen - olen, olen);
+ /*
+ * Invocation field will be at least 8 bytes in size and so no need
+ * to check wrap around or increment more than last 8 bytes.
+ */
+ ctr64_inc(ctx->iv + ctx->ivlen - 8);
+ ctx->iv_state = IV_STATE_COPIED;
+ return 1;
+}
+
+static int setivinv(PROV_GCM_CTX *ctx, unsigned char *in, size_t inl)
+{
+ if (!ctx->iv_gen
+ || !ctx->key_set
+ || ctx->enc)
+ return 0;
+
+ memcpy(ctx->iv + ctx->ivlen - inl, in, inl);
+ if (!ctx->hw->setiv(ctx, ctx->iv, ctx->ivlen))
+ return 0;
+ ctx->iv_state = IV_STATE_COPIED;
+ return 1;
+}
+
+int ossl_gcm_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ OSSL_PARAM *p;
+ size_t sz;
+ int type;
+
+ for (p = params; p->key != NULL; p++) {
+ type = ossl_param_find_pidx(p->key);
+ switch (type) {
+ default:
+ break;
+
+ case PIDX_CIPHER_PARAM_IVLEN:
+ if (!OSSL_PARAM_set_size_t(p, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_KEYLEN:
+ if (!OSSL_PARAM_set_size_t(p, ctx->keylen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TAGLEN:
+ {
+ size_t taglen = (ctx->taglen != UNINITIALISED_SIZET) ? ctx->taglen :
+ GCM_TAG_MAX_SIZE;
+
+ if (!OSSL_PARAM_set_size_t(p, taglen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_IV:
+ if (ctx->iv_state == IV_STATE_UNINITIALISED)
+ return 0;
+ if (ctx->ivlen > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_UPDATED_IV:
+ if (ctx->iv_state == IV_STATE_UNINITIALISED)
+ return 0;
+ if (ctx->ivlen > p->data_size) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->iv, ctx->ivlen)
+ && !OSSL_PARAM_set_octet_ptr(p, &ctx->iv, ctx->ivlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TLS1_AAD_PAD:
+ if (!OSSL_PARAM_set_size_t(p, ctx->tls_aad_pad_sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TAG:
+ sz = p->data_size;
+ if (sz == 0
+ || sz > EVP_GCM_TLS_TAG_LEN
+ || !ctx->enc
+ || ctx->taglen == UNINITIALISED_SIZET) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG);
+ return 0;
+ }
+ if (!OSSL_PARAM_set_octet_string(p, ctx->buf, sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN:
+ if (p->data == NULL
+ || p->data_type != OSSL_PARAM_OCTET_STRING
+ || !getivgen(ctx, p->data, p->data_size))
+ return 0;
+ break;
+ case PIDX_CIPHER_PARAM_AEAD_IV_GENERATED:
+ if (!OSSL_PARAM_set_uint(p, ctx->iv_gen_rand))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+int ossl_gcm_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t sz;
+ void *vp;
+ int type;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ for (p = params; p->key != NULL; p++) {
+ type = ossl_param_find_pidx(p->key);
+ switch (type) {
+ default:
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TAG:
+ vp = ctx->buf;
+ if (!OSSL_PARAM_get_octet_string(p, &vp, EVP_GCM_TLS_TAG_LEN, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (sz == 0 || ctx->enc) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_TAG);
+ return 0;
+ }
+ ctx->taglen = sz;
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_IVLEN:
+ if (!OSSL_PARAM_get_size_t(p, &sz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (sz == 0 || sz > sizeof(ctx->iv)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_IV_LENGTH);
+ return 0;
+ }
+ if (ctx->ivlen != sz) {
+ /* If the iv was already set or autogenerated, it is invalid. */
+ if (ctx->iv_state != IV_STATE_UNINITIALISED)
+ ctx->iv_state = IV_STATE_FINISHED;
+ ctx->ivlen = sz;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TLS1_AAD:
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ sz = gcm_tls_init(ctx, p->data, p->data_size);
+ if (sz == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_AAD);
+ return 0;
+ }
+ ctx->tls_aad_pad_sz = sz;
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TLS1_IV_FIXED:
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ if (gcm_tls_iv_set_fixed(ctx, p->data, p->data_size) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ break;
+
+ case PIDX_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV:
+ if (p->data == NULL
+ || p->data_type != OSSL_PARAM_OCTET_STRING
+ || !setivinv(ctx, p->data, p->data_size))
+ return 0;
+ break;
+ }
+ }
+
+ return 1;
+}
+
+int ossl_gcm_stream_update(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize, const unsigned char *in, size_t inl)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ if (inl == 0) {
+ *outl = 0;
+ return 1;
+ }
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED);
+ return 0;
+ }
+ return 1;
+}
+
+int ossl_gcm_stream_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+ int i;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ i = gcm_cipher_internal(ctx, out, outl, NULL, 0);
+ if (i <= 0)
+ return 0;
+
+ *outl = 0;
+ return 1;
+}
+
+int ossl_gcm_cipher(void *vctx,
+ unsigned char *out, size_t *outl, size_t outsize,
+ const unsigned char *in, size_t inl)
+{
+ PROV_GCM_CTX *ctx = (PROV_GCM_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (outsize < inl) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (gcm_cipher_internal(ctx, out, outl, in, inl) <= 0)
+ return 0;
+
+ *outl = inl;
+ return 1;
+}
+
+/*
+ * See SP800-38D (GCM) Section 8 "Uniqueness requirement on IVS and keys"
+ *
+ * See also 8.2.2 RBG-based construction.
+ * Random construction consists of a free field (which can be NULL) and a
+ * random field which will use a DRBG that can return at least 96 bits of
+ * entropy strength. (The DRBG must be seeded by the FIPS module).
+ */
+static int gcm_iv_generate(PROV_GCM_CTX *ctx, int offset)
+{
+ int sz = ctx->ivlen - offset;
+
+ /* Must be at least 96 bits */
+ if (sz <= 0 || ctx->ivlen < GCM_IV_DEFAULT_SIZE)
+ return 0;
+
+ /* Use DRBG to generate random iv */
+ if (RAND_bytes_ex(ctx->libctx, ctx->iv + offset, sz, 0) <= 0)
+ return 0;
+ ctx->iv_state = IV_STATE_BUFFERED;
+ ctx->iv_gen_rand = 1;
+ return 1;
+}
+
+static int gcm_cipher_internal(PROV_GCM_CTX *ctx, unsigned char *out,
+ size_t *padlen, const unsigned char *in,
+ size_t len)
+{
+ size_t olen = 0;
+ int rv = 0;
+ const PROV_GCM_HW *hw = ctx->hw;
+
+ if (ctx->tls_aad_len != UNINITIALISED_SIZET)
+ return gcm_tls_cipher(ctx, out, padlen, in, len);
+
+ if (!ctx->key_set || ctx->iv_state == IV_STATE_FINISHED)
+ goto err;
+
+ /*
+ * FIPS requires generation of AES-GCM IV's inside the FIPS module.
+ * The IV can still be set externally (the security policy will state that
+ * this is not FIPS compliant). There are some applications
+ * where setting the IV externally is the only option available.
+ */
+ if (ctx->iv_state == IV_STATE_UNINITIALISED) {
+ if (!ctx->enc || !gcm_iv_generate(ctx, 0))
+ goto err;
+ }
+
+ if (ctx->iv_state == IV_STATE_BUFFERED) {
+ if (!hw->setiv(ctx, ctx->iv, ctx->ivlen))
+ goto err;
+ ctx->iv_state = IV_STATE_COPIED;
+ }
+
+ if (in != NULL) {
+ /* The input is AAD if out is NULL */
+ if (out == NULL) {
+ if (!hw->aadupdate(ctx, in, len))
+ goto err;
+ } else {
+ /* The input is ciphertext OR plaintext */
+ if (!hw->cipherupdate(ctx, in, len, out))
+ goto err;
+ }
+ } else {
+ /* The tag must be set before actually decrypting data */
+ if (!ctx->enc && ctx->taglen == UNINITIALISED_SIZET)
+ goto err;
+ if (!hw->cipherfinal(ctx, ctx->buf))
+ goto err;
+ ctx->iv_state = IV_STATE_FINISHED; /* Don't reuse the IV */
+ goto finish;
+ }
+ olen = len;
+finish:
+ rv = 1;
+err:
+ *padlen = olen;
+ return rv;
+}
+
+static int gcm_tls_init(PROV_GCM_CTX *dat, unsigned char *aad, size_t aad_len)
+{
+ unsigned char *buf;
+ size_t len;
+
+ if (!ossl_prov_is_running() || aad_len != EVP_AEAD_TLS1_AAD_LEN)
+ return 0;
+
+ /* Save the aad for later use. */
+ buf = dat->buf;
+ memcpy(buf, aad, aad_len);
+ dat->tls_aad_len = aad_len;
+
+ len = buf[aad_len - 2] << 8 | buf[aad_len - 1];
+ /* Correct length for explicit iv. */
+ if (len < EVP_GCM_TLS_EXPLICIT_IV_LEN)
+ return 0;
+ len -= EVP_GCM_TLS_EXPLICIT_IV_LEN;
+
+ /* If decrypting correct for tag too. */
+ if (!dat->enc) {
+ if (len < EVP_GCM_TLS_TAG_LEN)
+ return 0;
+ len -= EVP_GCM_TLS_TAG_LEN;
+ }
+ buf[aad_len - 2] = (unsigned char)(len >> 8);
+ buf[aad_len - 1] = (unsigned char)(len & 0xff);
+ /* Extra padding: tag appended to record. */
+ return EVP_GCM_TLS_TAG_LEN;
+}
+
+static int gcm_tls_iv_set_fixed(PROV_GCM_CTX *ctx, unsigned char *iv,
+ size_t len)
+{
+ /* Special case: -1 length restores whole IV */
+ if (len == (size_t)-1) {
+ memcpy(ctx->iv, iv, ctx->ivlen);
+ ctx->iv_gen = 1;
+ ctx->iv_state = IV_STATE_BUFFERED;
+ return 1;
+ }
+ /* Fixed field must be at least 4 bytes and invocation field at least 8 */
+ if ((len < EVP_GCM_TLS_FIXED_IV_LEN)
+ || (ctx->ivlen - (int)len) < EVP_GCM_TLS_EXPLICIT_IV_LEN)
+ return 0;
+ if (len > 0)
+ memcpy(ctx->iv, iv, len);
+ if (ctx->enc) {
+ if (RAND_bytes_ex(ctx->libctx, ctx->iv + len, ctx->ivlen - len, 0) <= 0)
+ return 0;
+ ctx->iv_gen_rand = 1;
+ }
+ ctx->iv_gen = 1;
+ ctx->iv_state = IV_STATE_BUFFERED;
+ return 1;
+}
+
+/*
+ * Handle TLS GCM packet format. This consists of the last portion of the IV
+ * followed by the payload and finally the tag. On encrypt generate IV,
+ * encrypt payload and write the tag. On verify retrieve IV, decrypt payload
+ * and verify tag.
+ */
+static int gcm_tls_cipher(PROV_GCM_CTX *ctx, unsigned char *out, size_t *padlen,
+ const unsigned char *in, size_t len)
+{
+ int rv = 0;
+ size_t arg = EVP_GCM_TLS_EXPLICIT_IV_LEN;
+ size_t plen = 0;
+ unsigned char *tag = NULL;
+
+ if (!ossl_prov_is_running() || !ctx->key_set)
+ goto err;
+
+ /* Encrypt/decrypt must be performed in place */
+ if (out != in || len < (EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN))
+ goto err;
+
+ /*
+ * Check for too many keys as per FIPS 140-2 IG A.5 "Key/IV Pair Uniqueness
+ * Requirements from SP 800-38D". The requirements is for one party to the
+ * communication to fail after 2^64 - 1 keys. We do this on the encrypting
+ * side only.
+ */
+ if (ctx->enc && ++ctx->tls_enc_records == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_TOO_MANY_RECORDS);
+ goto err;
+ }
+
+ /*
+ * Set IV from start of buffer or generate IV and write to start of
+ * buffer.
+ */
+ if (ctx->enc) {
+ if (!getivgen(ctx, out, arg))
+ goto err;
+ } else {
+ if (!setivinv(ctx, out, arg))
+ goto err;
+ }
+
+ /* Fix buffer and length to point to payload */
+ in += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+ out += EVP_GCM_TLS_EXPLICIT_IV_LEN;
+ len -= EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+
+ tag = ctx->enc ? out + len : (unsigned char *)in + len;
+ if (!ctx->hw->oneshot(ctx, ctx->buf, ctx->tls_aad_len, in, len, out, tag,
+ EVP_GCM_TLS_TAG_LEN)) {
+ if (!ctx->enc)
+ OPENSSL_cleanse(out, len);
+ goto err;
+ }
+ if (ctx->enc)
+ plen = len + EVP_GCM_TLS_EXPLICIT_IV_LEN + EVP_GCM_TLS_TAG_LEN;
+ else
+ plen = len;
+
+ rv = 1;
+err:
+ ctx->iv_state = IV_STATE_FINISHED;
+ ctx->tls_aad_len = UNINITIALISED_SIZET;
+ *padlen = plen;
+ return rv;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c
new file mode 100644
index 000000000000..c0a7399640fd
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_gcm_hw.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2001-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+#include "prov/ciphercommon_gcm.h"
+
+
+int ossl_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, size_t ivlen)
+{
+ CRYPTO_gcm128_setiv(&ctx->gcm, iv, ivlen);
+ return 1;
+}
+
+int ossl_gcm_aad_update(PROV_GCM_CTX *ctx, const unsigned char *aad,
+ size_t aad_len)
+{
+ return CRYPTO_gcm128_aad(&ctx->gcm, aad, aad_len) == 0;
+}
+
+int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out)
+{
+ if (ctx->enc) {
+ if (CRYPTO_gcm128_encrypt(&ctx->gcm, in, out, len))
+ return 0;
+ } else {
+ if (CRYPTO_gcm128_decrypt(&ctx->gcm, in, out, len))
+ return 0;
+ }
+ return 1;
+}
+
+int ossl_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag)
+{
+ if (ctx->enc) {
+ CRYPTO_gcm128_tag(&ctx->gcm, tag, GCM_TAG_MAX_SIZE);
+ ctx->taglen = GCM_TAG_MAX_SIZE;
+ } else {
+ if (CRYPTO_gcm128_finish(&ctx->gcm, tag, ctx->taglen) != 0)
+ return 0;
+ }
+ return 1;
+}
+
+int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len,
+ const unsigned char *in, size_t in_len,
+ unsigned char *out, unsigned char *tag, size_t tag_len)
+{
+ int ret = 0;
+
+ /* Use saved AAD */
+ if (!ctx->hw->aadupdate(ctx, aad, aad_len))
+ goto err;
+ if (!ctx->hw->cipherupdate(ctx, in, in_len, out))
+ goto err;
+ ctx->taglen = GCM_TAG_MAX_SIZE;
+ if (!ctx->hw->cipherfinal(ctx, tag))
+ goto err;
+ ret = 1;
+
+err:
+ return ret;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c b/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c
new file mode 100644
index 000000000000..e73416a1c5c8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_hw.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+
+/*-
+ * The generic cipher functions for cipher modes cbc, ecb, ofb, cfb and ctr.
+ * Used if there is no special hardware implementations.
+ */
+int ossl_cipher_hw_generic_cbc(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ if (dat->stream.cbc)
+ (*dat->stream.cbc) (in, out, len, dat->ks, dat->iv, dat->enc);
+ else if (dat->enc)
+ CRYPTO_cbc128_encrypt(in, out, len, dat->ks, dat->iv, dat->block);
+ else
+ CRYPTO_cbc128_decrypt(in, out, len, dat->ks, dat->iv, dat->block);
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_ecb(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ size_t i, bl = dat->blocksize;
+
+ if (len < bl)
+ return 1;
+
+ if (dat->stream.ecb) {
+ (*dat->stream.ecb) (in, out, len, dat->ks, dat->enc);
+ }
+ else {
+ for (i = 0, len -= bl; i <= len; i += bl)
+ (*dat->block) (in + i, out + i, dat->ks);
+ }
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_ofb128(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ int num = dat->num;
+
+ CRYPTO_ofb128_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->block);
+ dat->num = num;
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_cfb128(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ int num = dat->num;
+
+ CRYPTO_cfb128_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->enc,
+ dat->block);
+ dat->num = num;
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_cfb8(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ int num = dat->num;
+
+ CRYPTO_cfb128_8_encrypt(in, out, len, dat->ks, dat->iv, &num, dat->enc,
+ dat->block);
+ dat->num = num;
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_cfb1(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ int num = dat->num;
+
+ if (dat->use_bits) {
+ CRYPTO_cfb128_1_encrypt(in, out, len, dat->ks, dat->iv, &num,
+ dat->enc, dat->block);
+ dat->num = num;
+ return 1;
+ }
+
+ while (len >= MAXBITCHUNK) {
+ CRYPTO_cfb128_1_encrypt(in, out, MAXBITCHUNK * 8, dat->ks,
+ dat->iv, &num, dat->enc, dat->block);
+ len -= MAXBITCHUNK;
+ out += MAXBITCHUNK;
+ in += MAXBITCHUNK;
+ }
+ if (len)
+ CRYPTO_cfb128_1_encrypt(in, out, len * 8, dat->ks, dat->iv, &num,
+ dat->enc, dat->block);
+
+ dat->num = num;
+
+ return 1;
+}
+
+int ossl_cipher_hw_generic_ctr(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len)
+{
+ unsigned int num = dat->num;
+
+ if (dat->stream.ctr)
+ CRYPTO_ctr128_encrypt_ctr32(in, out, len, dat->ks, dat->iv, dat->buf,
+ &num, dat->stream.ctr);
+ else
+ CRYPTO_ctr128_encrypt(in, out, len, dat->ks, dat->iv, dat->buf,
+ &num, dat->block);
+ dat->num = num;
+
+ return 1;
+}
+
+/*-
+ * The chunked cipher functions for cipher modes cbc, ecb, ofb, cfb and ctr.
+ * Used if there is no special hardware implementations.
+ */
+
+int ossl_cipher_hw_chunked_cbc(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ while (inl >= MAXCHUNK) {
+ ossl_cipher_hw_generic_cbc(ctx, out, in, MAXCHUNK);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ ossl_cipher_hw_generic_cbc(ctx, out, in, inl);
+ return 1;
+}
+
+int ossl_cipher_hw_chunked_cfb8(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ size_t chunk = MAXCHUNK;
+
+ if (inl < chunk)
+ chunk = inl;
+ while (inl > 0 && inl >= chunk) {
+ ossl_cipher_hw_generic_cfb8(ctx, out, in, inl);
+ inl -= chunk;
+ in += chunk;
+ out += chunk;
+ if (inl < chunk)
+ chunk = inl;
+ }
+ return 1;
+}
+
+int ossl_cipher_hw_chunked_cfb128(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ size_t chunk = MAXCHUNK;
+
+ if (inl < chunk)
+ chunk = inl;
+ while (inl > 0 && inl >= chunk) {
+ ossl_cipher_hw_generic_cfb128(ctx, out, in, inl);
+ inl -= chunk;
+ in += chunk;
+ out += chunk;
+ if (inl < chunk)
+ chunk = inl;
+ }
+ return 1;
+}
+
+int ossl_cipher_hw_chunked_ofb128(PROV_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, size_t inl)
+{
+ while (inl >= MAXCHUNK) {
+ ossl_cipher_hw_generic_ofb128(ctx, out, in, MAXCHUNK);
+ inl -= MAXCHUNK;
+ in += MAXCHUNK;
+ out += MAXCHUNK;
+ }
+ if (inl > 0)
+ ossl_cipher_hw_generic_ofb128(ctx, out, in, inl);
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h b/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h
new file mode 100644
index 000000000000..11cb6116a815
--- /dev/null
+++ b/crypto/openssl/providers/implementations/ciphers/ciphercommon_local.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/ciphercommon.h"
+
+void ossl_cipher_padblock(unsigned char *buf, size_t *buflen, size_t blocksize);
+int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize);
+int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion,
+ unsigned char *buf, size_t *buflen, size_t blocksize,
+ unsigned char **mac, int *alloced, size_t macsize, int aead);
diff --git a/crypto/openssl/providers/implementations/digests/blake2_impl.h b/crypto/openssl/providers/implementations/digests/blake2_impl.h
new file mode 100644
index 000000000000..e7c31474a364
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/blake2_impl.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves <sneves@dei.uc.pt>
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include <string.h>
+#include "internal/endian.h"
+
+static ossl_inline uint32_t load32(const uint8_t *src)
+{
+ DECLARE_IS_ENDIAN;
+
+ if (IS_LITTLE_ENDIAN) {
+ uint32_t w;
+ memcpy(&w, src, sizeof(w));
+ return w;
+ } else {
+ uint32_t w = ((uint32_t)src[0])
+ | ((uint32_t)src[1] << 8)
+ | ((uint32_t)src[2] << 16)
+ | ((uint32_t)src[3] << 24);
+ return w;
+ }
+}
+
+static ossl_inline uint64_t load64(const uint8_t *src)
+{
+ DECLARE_IS_ENDIAN;
+
+ if (IS_LITTLE_ENDIAN) {
+ uint64_t w;
+ memcpy(&w, src, sizeof(w));
+ return w;
+ } else {
+ uint64_t w = ((uint64_t)src[0])
+ | ((uint64_t)src[1] << 8)
+ | ((uint64_t)src[2] << 16)
+ | ((uint64_t)src[3] << 24)
+ | ((uint64_t)src[4] << 32)
+ | ((uint64_t)src[5] << 40)
+ | ((uint64_t)src[6] << 48)
+ | ((uint64_t)src[7] << 56);
+ return w;
+ }
+}
+
+static ossl_inline void store32(uint8_t *dst, uint32_t w)
+{
+ DECLARE_IS_ENDIAN;
+
+ if (IS_LITTLE_ENDIAN) {
+ memcpy(dst, &w, sizeof(w));
+ } else {
+ uint8_t *p = (uint8_t *)dst;
+ int i;
+
+ for (i = 0; i < 4; i++)
+ p[i] = (uint8_t)(w >> (8 * i));
+ }
+}
+
+static ossl_inline void store64(uint8_t *dst, uint64_t w)
+{
+ DECLARE_IS_ENDIAN;
+
+ if (IS_LITTLE_ENDIAN) {
+ memcpy(dst, &w, sizeof(w));
+ } else {
+ uint8_t *p = (uint8_t *)dst;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ p[i] = (uint8_t)(w >> (8 * i));
+ }
+}
+
+static ossl_inline uint64_t load48(const uint8_t *src)
+{
+ uint64_t w = ((uint64_t)src[0])
+ | ((uint64_t)src[1] << 8)
+ | ((uint64_t)src[2] << 16)
+ | ((uint64_t)src[3] << 24)
+ | ((uint64_t)src[4] << 32)
+ | ((uint64_t)src[5] << 40);
+ return w;
+}
+
+static ossl_inline void store48(uint8_t *dst, uint64_t w)
+{
+ uint8_t *p = (uint8_t *)dst;
+ p[0] = (uint8_t)w;
+ p[1] = (uint8_t)(w>>8);
+ p[2] = (uint8_t)(w>>16);
+ p[3] = (uint8_t)(w>>24);
+ p[4] = (uint8_t)(w>>32);
+ p[5] = (uint8_t)(w>>40);
+}
+
+static ossl_inline uint32_t rotr32(const uint32_t w, const unsigned int c)
+{
+ return (w >> c) | (w << (32 - c));
+}
+
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c)
+{
+ return (w >> c) | (w << (64 - c));
+}
diff --git a/crypto/openssl/providers/implementations/digests/blake2_prov.c b/crypto/openssl/providers/implementations/digests/blake2_prov.c
new file mode 100644
index 000000000000..5495aab61a87
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/blake2_prov.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include <openssl/err.h>
+#include "prov/blake2.h"
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+#define IMPLEMENT_BLAKE_functions(variant, VARIANT, variantsize) \
+static const OSSL_PARAM known_blake##variant##_ctx_params[] = { \
+ {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0}, \
+ OSSL_PARAM_END \
+}; \
+ \
+const OSSL_PARAM *ossl_blake##variant##_gettable_ctx_params(ossl_unused void *ctx, \
+ ossl_unused void *pctx) \
+{ \
+ return known_blake##variant##_ctx_params; \
+} \
+ \
+const OSSL_PARAM *ossl_blake##variant##_settable_ctx_params(ossl_unused void *ctx, \
+ ossl_unused void *pctx) \
+{ \
+ return known_blake##variant##_ctx_params; \
+} \
+ \
+int ossl_blake##variant##_get_ctx_params(void *vctx, OSSL_PARAM params[]) \
+{ \
+ struct blake##variant##_md_data_st *mdctx = vctx; \
+ OSSL_PARAM *p; \
+ \
+ BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \
+ \
+ if (ctx == NULL) \
+ return 0; \
+ if (ossl_param_is_empty(params)) \
+ return 1; \
+ \
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE); \
+ if (p != NULL \
+ && !OSSL_PARAM_set_uint(p, (unsigned int)mdctx->params.digest_length)) { \
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); \
+ return 0; \
+ } \
+ \
+ return 1; \
+} \
+ \
+int ossl_blake##variant##_set_ctx_params(void *vctx, const OSSL_PARAM params[]) \
+{ \
+ size_t size; \
+ struct blake##variant##_md_data_st *mdctx = vctx; \
+ const OSSL_PARAM *p; \
+ \
+ BLAKE##VARIANT##_CTX *ctx = &mdctx->ctx; \
+ \
+ if (ctx == NULL) \
+ return 0; \
+ if (ossl_param_is_empty(params)) \
+ return 1; \
+ \
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE); \
+ if (p != NULL) { \
+ if (!OSSL_PARAM_get_size_t(p, &size)) { \
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); \
+ return 0; \
+ } \
+ if (size < 1 || size > BLAKE##VARIANT##_OUTBYTES) { \
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \
+ return 0; \
+ } \
+ ossl_blake##variant##_param_set_digest_length(&mdctx->params, (uint8_t)size); \
+ } \
+ \
+ return 1; \
+} \
+ \
+static int ossl_blake##variantsize##_init(void *ctx) \
+{ \
+ struct blake##variant##_md_data_st *mdctx = ctx; \
+ uint8_t digest_length = mdctx->params.digest_length; \
+ \
+ ossl_blake##variant##_param_init(&mdctx->params); \
+ if (digest_length != 0) \
+ mdctx->params.digest_length = digest_length; \
+ return ossl_blake##variant##_init(&mdctx->ctx, &mdctx->params); \
+} \
+ \
+static OSSL_FUNC_digest_init_fn blake##variantsize##_internal_init; \
+static OSSL_FUNC_digest_newctx_fn blake##variantsize##_newctx; \
+static OSSL_FUNC_digest_freectx_fn blake##variantsize##_freectx; \
+static OSSL_FUNC_digest_dupctx_fn blake##variantsize##_dupctx; \
+static OSSL_FUNC_digest_final_fn blake##variantsize##_internal_final; \
+static OSSL_FUNC_digest_get_params_fn blake##variantsize##_get_params; \
+ \
+static int blake##variantsize##_internal_init(void *ctx, const OSSL_PARAM params[]) \
+{ \
+ return ossl_prov_is_running() && ossl_blake##variant##_set_ctx_params(ctx, params) \
+ && ossl_blake##variantsize##_init(ctx); \
+} \
+ \
+static void *blake##variantsize##_newctx(void *prov_ctx) \
+{ \
+ struct blake##variant##_md_data_st *ctx; \
+ \
+ ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; \
+ return ctx; \
+} \
+ \
+static void blake##variantsize##_freectx(void *vctx) \
+{ \
+ struct blake##variant##_md_data_st *ctx; \
+ \
+ ctx = (struct blake##variant##_md_data_st *)vctx; \
+ OPENSSL_clear_free(ctx, sizeof(*ctx)); \
+} \
+ \
+static void *blake##variantsize##_dupctx(void *ctx) \
+{ \
+ struct blake##variant##_md_data_st *in, *ret; \
+ \
+ in = (struct blake##variant##_md_data_st *)ctx; \
+ ret = ossl_prov_is_running()? OPENSSL_malloc(sizeof(*ret)) : NULL; \
+ if (ret != NULL) \
+ *ret = *in; \
+ return ret; \
+} \
+\
+static void blake##variantsize##_copyctx(void *voutctx, void *vinctx) \
+{ \
+ struct blake##variant##_md_data_st *inctx, *outctx; \
+ \
+ outctx = (struct blake##variant##_md_data_st *)voutctx; \
+ inctx = (struct blake##variant##_md_data_st *)vinctx; \
+ *outctx = *inctx; \
+} \
+ \
+static int blake##variantsize##_internal_final(void *ctx, unsigned char *out, \
+ size_t *outl, size_t outsz) \
+{ \
+ struct blake##variant##_md_data_st *b_ctx; \
+ \
+ b_ctx = (struct blake##variant##_md_data_st *)ctx; \
+ \
+ if (!ossl_prov_is_running()) \
+ return 0; \
+ \
+ *outl = b_ctx->ctx.outlen; \
+ \
+ if (outsz == 0) \
+ return 1; \
+ \
+ if (outsz < *outl) { \
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE); \
+ return 0; \
+ } \
+ \
+ return ossl_blake##variant##_final(out, ctx); \
+} \
+ \
+static int blake##variantsize##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_digest_default_get_params(params, BLAKE##VARIANT##_BLOCKBYTES, BLAKE##VARIANT##_OUTBYTES, 0); \
+} \
+ \
+const OSSL_DISPATCH ossl_blake##variantsize##_functions[] = { \
+ {OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))blake##variantsize##_newctx}, \
+ {OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))ossl_blake##variant##_update}, \
+ {OSSL_FUNC_DIGEST_FINAL, (void (*)(void))blake##variantsize##_internal_final}, \
+ {OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))blake##variantsize##_freectx}, \
+ {OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))blake##variantsize##_dupctx}, \
+ {OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))blake##variantsize##_copyctx}, \
+ {OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))blake##variantsize##_get_params}, \
+ {OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_digest_default_gettable_params}, \
+ {OSSL_FUNC_DIGEST_INIT, (void (*)(void))blake##variantsize##_internal_init}, \
+ {OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_blake##variant##_gettable_ctx_params}, \
+ {OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_blake##variant##_settable_ctx_params}, \
+ {OSSL_FUNC_DIGEST_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_blake##variant##_get_ctx_params}, \
+ {OSSL_FUNC_DIGEST_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_blake##variant##_set_ctx_params}, \
+ {0, NULL} \
+};
+
+IMPLEMENT_BLAKE_functions(2s, 2S, 2s256)
+IMPLEMENT_BLAKE_functions(2b, 2B, 2b512)
diff --git a/crypto/openssl/providers/implementations/digests/blake2b_prov.c b/crypto/openssl/providers/implementations/digests/blake2b_prov.c
new file mode 100644
index 000000000000..6ef7fac008cc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/blake2b_prov.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves <sneves@dei.uc.pt>
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include "internal/numbers.h"
+#include "blake2_impl.h"
+#include "prov/blake2.h"
+
+static const uint64_t blake2b_IV[8] =
+{
+ 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL
+};
+
+static const uint8_t blake2b_sigma[12][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 }
+};
+
+/* Set that it's the last block we'll compress */
+static ossl_inline void blake2b_set_lastblock(BLAKE2B_CTX *S)
+{
+ S->f[0] = -1;
+}
+
+/* Initialize the hashing state. */
+static ossl_inline void blake2b_init0(BLAKE2B_CTX *S)
+{
+ int i;
+
+ memset(S, 0, sizeof(BLAKE2B_CTX));
+ for (i = 0; i < 8; ++i) {
+ S->h[i] = blake2b_IV[i];
+ }
+}
+
+/* init xors IV with input parameter block and sets the output length */
+static void blake2b_init_param(BLAKE2B_CTX *S, const BLAKE2B_PARAM *P)
+{
+ size_t i;
+ const uint8_t *p = (const uint8_t *)(P);
+
+ blake2b_init0(S);
+ S->outlen = P->digest_length;
+
+ /* The param struct is carefully hand packed, and should be 64 bytes on
+ * every platform. */
+ assert(sizeof(BLAKE2B_PARAM) == 64);
+ /* IV XOR ParamBlock */
+ for (i = 0; i < 8; ++i) {
+ S->h[i] ^= load64(p + sizeof(S->h[i]) * i);
+ }
+}
+
+/* Initialize the parameter block with default values */
+void ossl_blake2b_param_init(BLAKE2B_PARAM *P)
+{
+ P->digest_length = BLAKE2B_DIGEST_LENGTH;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32(P->leaf_length, 0);
+ store64(P->node_offset, 0);
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset(P->reserved, 0, sizeof(P->reserved));
+ memset(P->salt, 0, sizeof(P->salt));
+ memset(P->personal, 0, sizeof(P->personal));
+}
+
+void ossl_blake2b_param_set_digest_length(BLAKE2B_PARAM *P, uint8_t outlen)
+{
+ P->digest_length = outlen;
+}
+
+void ossl_blake2b_param_set_key_length(BLAKE2B_PARAM *P, uint8_t keylen)
+{
+ P->key_length = keylen;
+}
+
+void ossl_blake2b_param_set_personal(BLAKE2B_PARAM *P, const uint8_t *personal,
+ size_t len)
+{
+ memcpy(P->personal, personal, len);
+ memset(P->personal + len, 0, BLAKE2B_PERSONALBYTES - len);
+}
+
+void ossl_blake2b_param_set_salt(BLAKE2B_PARAM *P, const uint8_t *salt,
+ size_t len)
+{
+ memcpy(P->salt, salt, len);
+ memset(P->salt + len, 0, BLAKE2B_SALTBYTES - len);
+}
+
+/*
+ * Initialize the hashing context with the given parameter block.
+ * Always returns 1.
+ */
+int ossl_blake2b_init(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P)
+{
+ blake2b_init_param(c, P);
+ return 1;
+}
+
+/*
+ * Initialize the hashing context with the given parameter block and key.
+ * Always returns 1.
+ */
+int ossl_blake2b_init_key(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P,
+ const void *key)
+{
+ blake2b_init_param(c, P);
+
+ /* Pad the key to form first data block */
+ {
+ uint8_t block[BLAKE2B_BLOCKBYTES] = {0};
+
+ memcpy(block, key, P->key_length);
+ ossl_blake2b_update(c, block, BLAKE2B_BLOCKBYTES);
+ OPENSSL_cleanse(block, BLAKE2B_BLOCKBYTES);
+ }
+
+ return 1;
+}
+
+/* Permute the state while xoring in the block of data. */
+static void blake2b_compress(BLAKE2B_CTX *S,
+ const uint8_t *blocks,
+ size_t len)
+{
+ uint64_t m[16];
+ uint64_t v[16];
+ int i;
+ size_t increment;
+
+ /*
+ * There are two distinct usage vectors for this function:
+ *
+ * a) BLAKE2b_Update uses it to process complete blocks,
+ * possibly more than one at a time;
+ *
+ * b) BLAK2b_Final uses it to process last block, always
+ * single but possibly incomplete, in which case caller
+ * pads input with zeros.
+ */
+ assert(len < BLAKE2B_BLOCKBYTES || len % BLAKE2B_BLOCKBYTES == 0);
+
+ /*
+ * Since last block is always processed with separate call,
+ * |len| not being multiple of complete blocks can be observed
+ * only with |len| being less than BLAKE2B_BLOCKBYTES ("less"
+ * including even zero), which is why following assignment doesn't
+ * have to reside inside the main loop below.
+ */
+ increment = len < BLAKE2B_BLOCKBYTES ? len : BLAKE2B_BLOCKBYTES;
+
+ for (i = 0; i < 8; ++i) {
+ v[i] = S->h[i];
+ }
+
+ do {
+ for (i = 0; i < 16; ++i) {
+ m[i] = load64(blocks + i * sizeof(m[i]));
+ }
+
+ /* blake2b_increment_counter */
+ S->t[0] += increment;
+ S->t[1] += (S->t[0] < increment);
+
+ v[8] = blake2b_IV[0];
+ v[9] = blake2b_IV[1];
+ v[10] = blake2b_IV[2];
+ v[11] = blake2b_IV[3];
+ v[12] = S->t[0] ^ blake2b_IV[4];
+ v[13] = S->t[1] ^ blake2b_IV[5];
+ v[14] = S->f[0] ^ blake2b_IV[6];
+ v[15] = S->f[1] ^ blake2b_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2b_sigma[r][2*i+0]]; \
+ d = rotr64(d ^ a, 32); \
+ c = c + d; \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + m[blake2b_sigma[r][2*i+1]]; \
+ d = rotr64(d ^ a, 16); \
+ c = c + d; \
+ b = rotr64(b ^ c, 63); \
+ } while (0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while (0)
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+ /* 3x size reduction on x86_64, almost 7x on ARMv8, 9x on ARMv4 */
+ for (i = 0; i < 12; i++) {
+ ROUND(i);
+ }
+#else
+ ROUND(0);
+ ROUND(1);
+ ROUND(2);
+ ROUND(3);
+ ROUND(4);
+ ROUND(5);
+ ROUND(6);
+ ROUND(7);
+ ROUND(8);
+ ROUND(9);
+ ROUND(10);
+ ROUND(11);
+#endif
+
+ for (i = 0; i < 8; ++i) {
+ S->h[i] = v[i] ^= v[i + 8] ^ S->h[i];
+ }
+#undef G
+#undef ROUND
+ blocks += increment;
+ len -= increment;
+ } while (len);
+}
+
+/* Absorb the input data into the hash state. Always returns 1. */
+int ossl_blake2b_update(BLAKE2B_CTX *c, const void *data, size_t datalen)
+{
+ const uint8_t *in = data;
+ size_t fill;
+
+ /*
+ * Intuitively one would expect intermediate buffer, c->buf, to
+ * store incomplete blocks. But in this case we are interested to
+ * temporarily stash even complete blocks, because last one in the
+ * stream has to be treated in special way, and at this point we
+ * don't know if last block in *this* call is last one "ever". This
+ * is the reason for why |datalen| is compared as >, and not >=.
+ */
+ fill = sizeof(c->buf) - c->buflen;
+ if (datalen > fill) {
+ if (c->buflen) {
+ memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */
+ blake2b_compress(c, c->buf, BLAKE2B_BLOCKBYTES);
+ c->buflen = 0;
+ in += fill;
+ datalen -= fill;
+ }
+ if (datalen > BLAKE2B_BLOCKBYTES) {
+ size_t stashlen = datalen % BLAKE2B_BLOCKBYTES;
+ /*
+ * If |datalen| is a multiple of the blocksize, stash
+ * last complete block, it can be final one...
+ */
+ stashlen = stashlen ? stashlen : BLAKE2B_BLOCKBYTES;
+ datalen -= stashlen;
+ blake2b_compress(c, in, datalen);
+ in += datalen;
+ datalen = stashlen;
+ }
+ }
+
+ assert(datalen <= BLAKE2B_BLOCKBYTES);
+
+ memcpy(c->buf + c->buflen, in, datalen);
+ c->buflen += datalen; /* Be lazy, do not compress */
+
+ return 1;
+}
+
+/*
+ * Calculate the final hash and save it in md.
+ * Always returns 1.
+ */
+int ossl_blake2b_final(unsigned char *md, BLAKE2B_CTX *c)
+{
+ uint8_t outbuffer[BLAKE2B_OUTBYTES] = {0};
+ uint8_t *target = outbuffer;
+ int iter = (c->outlen + 7) / 8;
+ int i;
+
+ /* Avoid writing to the temporary buffer if possible */
+ if ((c->outlen % sizeof(c->h[0])) == 0)
+ target = md;
+
+ blake2b_set_lastblock(c);
+ /* Padding */
+ memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen);
+ blake2b_compress(c, c->buf, c->buflen);
+
+ /* Output full hash to buffer */
+ for (i = 0; i < iter; ++i)
+ store64(target + sizeof(c->h[i]) * i, c->h[i]);
+
+ if (target != md) {
+ memcpy(md, target, c->outlen);
+ OPENSSL_cleanse(target, sizeof(outbuffer));
+ }
+
+ OPENSSL_cleanse(c, sizeof(BLAKE2B_CTX));
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/digests/blake2s_prov.c b/crypto/openssl/providers/implementations/digests/blake2s_prov.c
new file mode 100644
index 000000000000..a9251d8996d9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/blake2s_prov.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Derived from the BLAKE2 reference implementation written by Samuel Neves.
+ * Copyright 2012, Samuel Neves <sneves@dei.uc.pt>
+ * More information about the BLAKE2 hash function and its implementations
+ * can be found at https://blake2.net.
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include "blake2_impl.h"
+#include "prov/blake2.h"
+
+static const uint32_t blake2s_IV[8] = {
+ 0x6A09E667U, 0xBB67AE85U, 0x3C6EF372U, 0xA54FF53AU,
+ 0x510E527FU, 0x9B05688CU, 0x1F83D9ABU, 0x5BE0CD19U
+};
+
+static const uint8_t blake2s_sigma[10][16] = {
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+/* Set that it's the last block we'll compress */
+static ossl_inline void blake2s_set_lastblock(BLAKE2S_CTX *S)
+{
+ S->f[0] = -1;
+}
+
+/* Initialize the hashing state. */
+static ossl_inline void blake2s_init0(BLAKE2S_CTX *S)
+{
+ int i;
+
+ memset(S, 0, sizeof(BLAKE2S_CTX));
+ for (i = 0; i < 8; ++i) {
+ S->h[i] = blake2s_IV[i];
+ }
+}
+
+/* init xors IV with input parameter block and sets the output length */
+static void blake2s_init_param(BLAKE2S_CTX *S, const BLAKE2S_PARAM *P)
+{
+ size_t i;
+ const uint8_t *p = (const uint8_t *)(P);
+
+ blake2s_init0(S);
+ S->outlen = P->digest_length;
+
+ /* The param struct is carefully hand packed, and should be 32 bytes on
+ * every platform. */
+ assert(sizeof(BLAKE2S_PARAM) == 32);
+ /* IV XOR ParamBlock */
+ for (i = 0; i < 8; ++i) {
+ S->h[i] ^= load32(&p[i*4]);
+ }
+}
+
+void ossl_blake2s_param_init(BLAKE2S_PARAM *P)
+{
+ P->digest_length = BLAKE2S_DIGEST_LENGTH;
+ P->key_length = 0;
+ P->fanout = 1;
+ P->depth = 1;
+ store32(P->leaf_length, 0);
+ store48(P->node_offset, 0);
+ P->node_depth = 0;
+ P->inner_length = 0;
+ memset(P->salt, 0, sizeof(P->salt));
+ memset(P->personal, 0, sizeof(P->personal));
+}
+
+void ossl_blake2s_param_set_digest_length(BLAKE2S_PARAM *P, uint8_t outlen)
+{
+ P->digest_length = outlen;
+}
+
+void ossl_blake2s_param_set_key_length(BLAKE2S_PARAM *P, uint8_t keylen)
+{
+ P->key_length = keylen;
+}
+
+void ossl_blake2s_param_set_personal(BLAKE2S_PARAM *P, const uint8_t *personal,
+ size_t len)
+{
+ memcpy(P->personal, personal, len);
+ memset(P->personal + len, 0, BLAKE2S_PERSONALBYTES - len);
+}
+
+void ossl_blake2s_param_set_salt(BLAKE2S_PARAM *P, const uint8_t *salt,
+ size_t len)
+{
+ memcpy(P->salt, salt, len);
+ memset(P->salt + len, 0, BLAKE2S_SALTBYTES - len);}
+
+/*
+ * Initialize the hashing context with the given parameter block.
+ * Always returns 1.
+ */
+int ossl_blake2s_init(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P)
+{
+ blake2s_init_param(c, P);
+ return 1;
+}
+
+/*
+ * Initialize the hashing context with the given parameter block and key.
+ * Always returns 1.
+ */
+int ossl_blake2s_init_key(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P,
+ const void *key)
+{
+ blake2s_init_param(c, P);
+
+ /* Pad the key to form first data block */
+ {
+ uint8_t block[BLAKE2S_BLOCKBYTES] = {0};
+
+ memcpy(block, key, P->key_length);
+ ossl_blake2s_update(c, block, BLAKE2S_BLOCKBYTES);
+ OPENSSL_cleanse(block, BLAKE2S_BLOCKBYTES);
+ }
+
+ return 1;
+}
+
+/* Permute the state while xoring in the block of data. */
+static void blake2s_compress(BLAKE2S_CTX *S,
+ const uint8_t *blocks,
+ size_t len)
+{
+ uint32_t m[16];
+ uint32_t v[16];
+ size_t i;
+ size_t increment;
+
+ /*
+ * There are two distinct usage vectors for this function:
+ *
+ * a) BLAKE2s_Update uses it to process complete blocks,
+ * possibly more than one at a time;
+ *
+ * b) BLAK2s_Final uses it to process last block, always
+ * single but possibly incomplete, in which case caller
+ * pads input with zeros.
+ */
+ assert(len < BLAKE2S_BLOCKBYTES || len % BLAKE2S_BLOCKBYTES == 0);
+
+ /*
+ * Since last block is always processed with separate call,
+ * |len| not being multiple of complete blocks can be observed
+ * only with |len| being less than BLAKE2S_BLOCKBYTES ("less"
+ * including even zero), which is why following assignment doesn't
+ * have to reside inside the main loop below.
+ */
+ increment = len < BLAKE2S_BLOCKBYTES ? len : BLAKE2S_BLOCKBYTES;
+
+ for (i = 0; i < 8; ++i) {
+ v[i] = S->h[i];
+ }
+
+ do {
+ for (i = 0; i < 16; ++i) {
+ m[i] = load32(blocks + i * sizeof(m[i]));
+ }
+
+ /* blake2s_increment_counter */
+ S->t[0] += increment;
+ S->t[1] += (S->t[0] < increment);
+
+ v[ 8] = blake2s_IV[0];
+ v[ 9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+#define G(r,i,a,b,c,d) \
+ do { \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7); \
+ } while (0)
+#define ROUND(r) \
+ do { \
+ G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
+ G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
+ G(r,2,v[ 2],v[ 6],v[10],v[14]); \
+ G(r,3,v[ 3],v[ 7],v[11],v[15]); \
+ G(r,4,v[ 0],v[ 5],v[10],v[15]); \
+ G(r,5,v[ 1],v[ 6],v[11],v[12]); \
+ G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
+ G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \
+ } while (0)
+#if defined(OPENSSL_SMALL_FOOTPRINT)
+ /* almost 3x reduction on x86_64, 4.5x on ARMv8, 4x on ARMv4 */
+ for (i = 0; i < 10; i++) {
+ ROUND(i);
+ }
+#else
+ ROUND(0);
+ ROUND(1);
+ ROUND(2);
+ ROUND(3);
+ ROUND(4);
+ ROUND(5);
+ ROUND(6);
+ ROUND(7);
+ ROUND(8);
+ ROUND(9);
+#endif
+
+ for (i = 0; i < 8; ++i) {
+ S->h[i] = v[i] ^= v[i + 8] ^ S->h[i];
+ }
+#undef G
+#undef ROUND
+ blocks += increment;
+ len -= increment;
+ } while (len);
+}
+
+/* Absorb the input data into the hash state. Always returns 1. */
+int ossl_blake2s_update(BLAKE2S_CTX *c, const void *data, size_t datalen)
+{
+ const uint8_t *in = data;
+ size_t fill;
+
+ /*
+ * Intuitively one would expect intermediate buffer, c->buf, to
+ * store incomplete blocks. But in this case we are interested to
+ * temporarily stash even complete blocks, because last one in the
+ * stream has to be treated in special way, and at this point we
+ * don't know if last block in *this* call is last one "ever". This
+ * is the reason for why |datalen| is compared as >, and not >=.
+ */
+ fill = sizeof(c->buf) - c->buflen;
+ if (datalen > fill) {
+ if (c->buflen) {
+ memcpy(c->buf + c->buflen, in, fill); /* Fill buffer */
+ blake2s_compress(c, c->buf, BLAKE2S_BLOCKBYTES);
+ c->buflen = 0;
+ in += fill;
+ datalen -= fill;
+ }
+ if (datalen > BLAKE2S_BLOCKBYTES) {
+ size_t stashlen = datalen % BLAKE2S_BLOCKBYTES;
+ /*
+ * If |datalen| is a multiple of the blocksize, stash
+ * last complete block, it can be final one...
+ */
+ stashlen = stashlen ? stashlen : BLAKE2S_BLOCKBYTES;
+ datalen -= stashlen;
+ blake2s_compress(c, in, datalen);
+ in += datalen;
+ datalen = stashlen;
+ }
+ }
+
+ assert(datalen <= BLAKE2S_BLOCKBYTES);
+
+ memcpy(c->buf + c->buflen, in, datalen);
+ c->buflen += datalen; /* Be lazy, do not compress */
+
+ return 1;
+}
+
+/*
+ * Calculate the final hash and save it in md.
+ * Always returns 1.
+ */
+int ossl_blake2s_final(unsigned char *md, BLAKE2S_CTX *c)
+{
+ uint8_t outbuffer[BLAKE2S_OUTBYTES] = {0};
+ uint8_t *target = outbuffer;
+ int iter = (c->outlen + 3) / 4;
+ int i;
+
+ /* Avoid writing to the temporary buffer if possible */
+ if ((c->outlen % sizeof(c->h[0])) == 0)
+ target = md;
+
+ blake2s_set_lastblock(c);
+ /* Padding */
+ memset(c->buf + c->buflen, 0, sizeof(c->buf) - c->buflen);
+ blake2s_compress(c, c->buf, c->buflen);
+
+ /* Output full hash to buffer */
+ for (i = 0; i < iter; ++i)
+ store32(target + sizeof(c->h[i]) * i, c->h[i]);
+
+ if (target != md) {
+ memcpy(md, target, c->outlen);
+ OPENSSL_cleanse(target, sizeof(outbuffer));
+ }
+
+ OPENSSL_cleanse(c, sizeof(BLAKE2S_CTX));
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/digests/build.info b/crypto/openssl/providers/implementations/digests/build.info
new file mode 100644
index 000000000000..d30975028e9f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/build.info
@@ -0,0 +1,62 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$COMMON_GOAL=../../libcommon.a
+
+$SHA1_GOAL=../../libdefault.a ../../libfips.a
+$SHA2_GOAL=../../libdefault.a ../../libfips.a
+$SHA3_GOAL=../../libdefault.a ../../libfips.a
+$BLAKE2_GOAL=../../libdefault.a
+$SM3_GOAL=../../libdefault.a
+$MD5_GOAL=../../libdefault.a
+$NULL_GOAL=../../libdefault.a
+
+$MD2_GOAL=../../liblegacy.a
+$MD4_GOAL=../../liblegacy.a
+$MDC2_GOAL=../../liblegacy.a
+$WHIRLPOOL_GOAL=../../liblegacy.a
+IF[{- !$disabled{module} -}]
+ $RIPEMD_GOAL=../../libdefault.a ../../liblegacy.a
+ELSE
+ $RIPEMD_GOAL=../../libdefault.a
+ENDIF
+
+# This source is common for all digests in all our providers.
+SOURCE[$COMMON_GOAL]=digestcommon.c
+
+SOURCE[$SHA2_GOAL]=sha2_prov.c
+SOURCE[$SHA3_GOAL]=sha3_prov.c
+
+SOURCE[$NULL_GOAL]=null_prov.c
+
+IF[{- !$disabled{blake2} -}]
+ SOURCE[$BLAKE2_GOAL]=blake2_prov.c blake2b_prov.c blake2s_prov.c
+ENDIF
+
+IF[{- !$disabled{sm3} -}]
+ SOURCE[$SM3_GOAL]=sm3_prov.c
+ENDIF
+
+IF[{- !$disabled{md5} -}]
+ SOURCE[$MD5_GOAL]=md5_prov.c md5_sha1_prov.c
+ENDIF
+
+IF[{- !$disabled{md2} -}]
+ SOURCE[$MD2_GOAL]=md2_prov.c
+ENDIF
+
+IF[{- !$disabled{md4} -}]
+ SOURCE[$MD4_GOAL]=md4_prov.c
+ENDIF
+
+IF[{- !$disabled{mdc2} -}]
+ SOURCE[$MDC2_GOAL]=mdc2_prov.c
+ENDIF
+
+IF[{- !$disabled{whirlpool} -}]
+ SOURCE[$WHIRLPOOL_GOAL]=wp_prov.c
+ENDIF
+
+IF[{- !$disabled{rmd160} -}]
+ SOURCE[$RIPEMD_GOAL]=ripemd_prov.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/digests/digestcommon.c b/crypto/openssl/providers/implementations/digests/digestcommon.c
new file mode 100644
index 000000000000..5cd1d1620062
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/digestcommon.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "prov/digestcommon.h"
+
+int ossl_digest_default_get_params(OSSL_PARAM params[], size_t blksz,
+ size_t paramsz, unsigned long flags)
+{
+ OSSL_PARAM *p = NULL;
+
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_BLOCK_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, blksz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, paramsz)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOF);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_DIGEST_FLAG_XOF) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_ALGID_ABSENT);
+ if (p != NULL
+ && !OSSL_PARAM_set_int(p, (flags & PROV_DIGEST_FLAG_ALGID_ABSENT) != 0)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM digest_default_known_gettable_params[] = {
+ OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, NULL),
+ OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_SIZE, NULL),
+ OSSL_PARAM_int(OSSL_DIGEST_PARAM_XOF, NULL),
+ OSSL_PARAM_int(OSSL_DIGEST_PARAM_ALGID_ABSENT, NULL),
+ OSSL_PARAM_END
+};
+const OSSL_PARAM *ossl_digest_default_gettable_params(void *provctx)
+{
+ return digest_default_known_gettable_params;
+}
diff --git a/crypto/openssl/providers/implementations/digests/md2_prov.c b/crypto/openssl/providers/implementations/digests/md2_prov.c
new file mode 100644
index 000000000000..a41a02c19890
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/md2_prov.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * MD2 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/md2.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_md2_functions */
+IMPLEMENT_digest_functions(md2, MD2_CTX,
+ MD2_BLOCK, MD2_DIGEST_LENGTH, 0,
+ MD2_Init, MD2_Update, MD2_Final)
diff --git a/crypto/openssl/providers/implementations/digests/md4_prov.c b/crypto/openssl/providers/implementations/digests/md4_prov.c
new file mode 100644
index 000000000000..97f73018c275
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/md4_prov.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * MD4 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/md4.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_md4_functions */
+IMPLEMENT_digest_functions(md4, MD4_CTX,
+ MD4_CBLOCK, MD4_DIGEST_LENGTH, 0,
+ MD4_Init, MD4_Update, MD4_Final)
diff --git a/crypto/openssl/providers/implementations/digests/md5_prov.c b/crypto/openssl/providers/implementations/digests/md5_prov.c
new file mode 100644
index 000000000000..a330e057f547
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/md5_prov.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * MD5 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/md5.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_md5_functions */
+IMPLEMENT_digest_functions(md5, MD5_CTX,
+ MD5_CBLOCK, MD5_DIGEST_LENGTH, 0,
+ MD5_Init, MD5_Update, MD5_Final)
diff --git a/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c b/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c
new file mode 100644
index 000000000000..9735c3f7e40f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/md5_sha1_prov.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * MD5 and SHA-1 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "prov/md5_sha1.h"
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+static OSSL_FUNC_digest_set_ctx_params_fn md5_sha1_set_ctx_params;
+static OSSL_FUNC_digest_settable_ctx_params_fn md5_sha1_settable_ctx_params;
+
+static const OSSL_PARAM known_md5_sha1_settable_ctx_params[] = {
+ {OSSL_DIGEST_PARAM_SSL3_MS, OSSL_PARAM_OCTET_STRING, NULL, 0, 0},
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *md5_sha1_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_md5_sha1_settable_ctx_params;
+}
+
+/* Special set_params method for SSL3 */
+static int md5_sha1_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ MD5_SHA1_CTX *ctx = (MD5_SHA1_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SSL3_MS);
+ if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING)
+ return ossl_md5_sha1_ctrl(ctx, EVP_CTRL_SSL3_MASTER_SECRET,
+ p->data_size, p->data);
+ return 1;
+}
+
+/* ossl_md5_sha1_functions */
+IMPLEMENT_digest_functions_with_settable_ctx(
+ md5_sha1, MD5_SHA1_CTX, MD5_SHA1_CBLOCK, MD5_SHA1_DIGEST_LENGTH, 0,
+ ossl_md5_sha1_init, ossl_md5_sha1_update, ossl_md5_sha1_final,
+ md5_sha1_settable_ctx_params, md5_sha1_set_ctx_params)
diff --git a/crypto/openssl/providers/implementations/digests/mdc2_prov.c b/crypto/openssl/providers/implementations/digests/mdc2_prov.c
new file mode 100644
index 000000000000..e1fc477d21e5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/mdc2_prov.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * MDC2 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/mdc2.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+static OSSL_FUNC_digest_set_ctx_params_fn mdc2_set_ctx_params;
+static OSSL_FUNC_digest_settable_ctx_params_fn mdc2_settable_ctx_params;
+
+static const OSSL_PARAM known_mdc2_settable_ctx_params[] = {
+ OSSL_PARAM_uint(OSSL_DIGEST_PARAM_PAD_TYPE, NULL),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *mdc2_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_mdc2_settable_ctx_params;
+}
+
+static int mdc2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ MDC2_CTX *ctx = (MDC2_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_PAD_TYPE);
+ if (p != NULL && !OSSL_PARAM_get_uint(p, &ctx->pad_type)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+/* ossl_mdc2_functions */
+IMPLEMENT_digest_functions_with_settable_ctx(
+ mdc2, MDC2_CTX, MDC2_BLOCK, MDC2_DIGEST_LENGTH, 0,
+ MDC2_Init, MDC2_Update, MDC2_Final,
+ mdc2_settable_ctx_params, mdc2_set_ctx_params)
diff --git a/crypto/openssl/providers/implementations/digests/null_prov.c b/crypto/openssl/providers/implementations/digests/null_prov.c
new file mode 100644
index 000000000000..b220a1966ff7
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/null_prov.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+typedef struct {
+ unsigned char nothing;
+} NULLMD_CTX;
+
+static int null_init(NULLMD_CTX *ctx)
+{
+ return 1;
+}
+
+static int null_update(NULLMD_CTX *ctx, const void *data, size_t datalen)
+{
+ return 1;
+}
+
+static int null_final(unsigned char *md, NULLMD_CTX *ctx)
+{
+ return 1;
+}
+
+/*
+ * We must override the PROV_FUNC_DIGEST_FINAL as dgstsize == 0
+ * and that would cause compilation warnings with the default implementation.
+ */
+#undef PROV_FUNC_DIGEST_FINAL
+#define PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \
+static OSSL_FUNC_digest_final_fn name##_internal_final; \
+static int name##_internal_final(void *ctx, unsigned char *out, size_t *outl, \
+ size_t outsz) \
+{ \
+ if (ossl_prov_is_running() && fin(out, ctx)) { \
+ *outl = dgstsize; \
+ return 1; \
+ } \
+ return 0; \
+}
+
+IMPLEMENT_digest_functions(nullmd, NULLMD_CTX,
+ 0, 0, 0,
+ null_init, null_update, null_final)
diff --git a/crypto/openssl/providers/implementations/digests/ripemd_prov.c b/crypto/openssl/providers/implementations/digests/ripemd_prov.c
new file mode 100644
index 000000000000..526706c06dcc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/ripemd_prov.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RIPEMD160 low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/ripemd.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_ripemd160_functions */
+IMPLEMENT_digest_functions(ripemd160, RIPEMD160_CTX,
+ RIPEMD160_CBLOCK, RIPEMD160_DIGEST_LENGTH, 0,
+ RIPEMD160_Init, RIPEMD160_Update, RIPEMD160_Final)
diff --git a/crypto/openssl/providers/implementations/digests/sha2_prov.c b/crypto/openssl/providers/implementations/digests/sha2_prov.c
new file mode 100644
index 000000000000..4c35586b00ed
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/sha2_prov.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * SHA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+#include "crypto/sha.h"
+
+#define SHA2_FLAGS PROV_DIGEST_FLAG_ALGID_ABSENT
+
+static OSSL_FUNC_digest_set_ctx_params_fn sha1_set_ctx_params;
+static OSSL_FUNC_digest_settable_ctx_params_fn sha1_settable_ctx_params;
+
+static const OSSL_PARAM known_sha1_settable_ctx_params[] = {
+ {OSSL_DIGEST_PARAM_SSL3_MS, OSSL_PARAM_OCTET_STRING, NULL, 0, 0},
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *sha1_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_sha1_settable_ctx_params;
+}
+
+/* Special set_params method for SSL3 */
+static int sha1_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ SHA_CTX *ctx = (SHA_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SSL3_MS);
+ if (p != NULL && p->data_type == OSSL_PARAM_OCTET_STRING)
+ return ossl_sha1_ctrl(ctx, EVP_CTRL_SSL3_MASTER_SECRET,
+ p->data_size, p->data);
+ return 1;
+}
+
+/* ossl_sha1_functions */
+IMPLEMENT_digest_functions_with_settable_ctx(
+ sha1, SHA_CTX, SHA_CBLOCK, SHA_DIGEST_LENGTH, SHA2_FLAGS,
+ SHA1_Init, SHA1_Update, SHA1_Final,
+ sha1_settable_ctx_params, sha1_set_ctx_params)
+
+/* ossl_sha224_functions */
+IMPLEMENT_digest_functions(sha224, SHA256_CTX,
+ SHA256_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS,
+ SHA224_Init, SHA224_Update, SHA224_Final)
+
+/* ossl_sha256_functions */
+IMPLEMENT_digest_functions(sha256, SHA256_CTX,
+ SHA256_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS,
+ SHA256_Init, SHA256_Update, SHA256_Final)
+#ifndef FIPS_MODULE
+/* ossl_sha256_192_functions */
+IMPLEMENT_digest_functions(sha256_192, SHA256_CTX,
+ SHA256_CBLOCK, SHA256_192_DIGEST_LENGTH, SHA2_FLAGS,
+ ossl_sha256_192_init, SHA256_Update, SHA256_Final)
+#endif
+/* ossl_sha384_functions */
+IMPLEMENT_digest_functions(sha384, SHA512_CTX,
+ SHA512_CBLOCK, SHA384_DIGEST_LENGTH, SHA2_FLAGS,
+ SHA384_Init, SHA384_Update, SHA384_Final)
+
+/* ossl_sha512_functions */
+IMPLEMENT_digest_functions(sha512, SHA512_CTX,
+ SHA512_CBLOCK, SHA512_DIGEST_LENGTH, SHA2_FLAGS,
+ SHA512_Init, SHA512_Update, SHA512_Final)
+
+/* ossl_sha512_224_functions */
+IMPLEMENT_digest_functions(sha512_224, SHA512_CTX,
+ SHA512_CBLOCK, SHA224_DIGEST_LENGTH, SHA2_FLAGS,
+ sha512_224_init, SHA512_Update, SHA512_Final)
+
+/* ossl_sha512_256_functions */
+IMPLEMENT_digest_functions(sha512_256, SHA512_CTX,
+ SHA512_CBLOCK, SHA256_DIGEST_LENGTH, SHA2_FLAGS,
+ sha512_256_init, SHA512_Update, SHA512_Final)
diff --git a/crypto/openssl/providers/implementations/digests/sha3_prov.c b/crypto/openssl/providers/implementations/digests/sha3_prov.c
new file mode 100644
index 000000000000..a3d209e0eac3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/sha3_prov.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "internal/numbers.h"
+#include "internal/sha3.h"
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+#define SHA3_FLAGS PROV_DIGEST_FLAG_ALGID_ABSENT
+#define SHAKE_FLAGS (PROV_DIGEST_FLAG_XOF | PROV_DIGEST_FLAG_ALGID_ABSENT)
+#define KMAC_FLAGS PROV_DIGEST_FLAG_XOF
+
+/*
+ * Forward declaration of any unique methods implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_digest_init_fn keccak_init;
+static OSSL_FUNC_digest_init_fn keccak_init_params;
+static OSSL_FUNC_digest_update_fn keccak_update;
+static OSSL_FUNC_digest_final_fn keccak_final;
+static OSSL_FUNC_digest_freectx_fn keccak_freectx;
+static OSSL_FUNC_digest_copyctx_fn keccak_copyctx;
+static OSSL_FUNC_digest_dupctx_fn keccak_dupctx;
+static OSSL_FUNC_digest_squeeze_fn shake_squeeze;
+static OSSL_FUNC_digest_get_ctx_params_fn shake_get_ctx_params;
+static OSSL_FUNC_digest_gettable_ctx_params_fn shake_gettable_ctx_params;
+static OSSL_FUNC_digest_set_ctx_params_fn shake_set_ctx_params;
+static OSSL_FUNC_digest_settable_ctx_params_fn shake_settable_ctx_params;
+static sha3_absorb_fn generic_sha3_absorb;
+static sha3_final_fn generic_sha3_final;
+static sha3_squeeze_fn generic_sha3_squeeze;
+
+#if defined(OPENSSL_CPUID_OBJ) && defined(__s390__) && defined(KECCAK1600_ASM)
+/*
+ * IBM S390X support
+ */
+# include "s390x_arch.h"
+# define S390_SHA3 1
+# define S390_SHA3_CAPABLE(name) \
+ ((OPENSSL_s390xcap_P.kimd[0] & S390X_CAPBIT(S390X_##name)) && \
+ (OPENSSL_s390xcap_P.klmd[0] & S390X_CAPBIT(S390X_##name)))
+
+#endif
+
+static int keccak_init(void *vctx, ossl_unused const OSSL_PARAM params[])
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ /* The newctx() handles most of the ctx fixed setup. */
+ ossl_sha3_reset((KECCAK1600_CTX *)vctx);
+ return 1;
+}
+
+static int keccak_init_params(void *vctx, const OSSL_PARAM params[])
+{
+ return keccak_init(vctx, NULL)
+ && shake_set_ctx_params(vctx, params);
+}
+
+static int keccak_update(void *vctx, const unsigned char *inp, size_t len)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ const size_t bsz = ctx->block_size;
+ size_t num, rem;
+
+ if (len == 0)
+ return 1;
+
+ /* Is there anything in the buffer already ? */
+ if ((num = ctx->bufsz) != 0) {
+ /* Calculate how much space is left in the buffer */
+ rem = bsz - num;
+ /* If the new input does not fill the buffer then just add it */
+ if (len < rem) {
+ memcpy(ctx->buf + num, inp, len);
+ ctx->bufsz += len;
+ return 1;
+ }
+ /* otherwise fill up the buffer and absorb the buffer */
+ memcpy(ctx->buf + num, inp, rem);
+ /* Update the input pointer */
+ inp += rem;
+ len -= rem;
+ ctx->meth.absorb(ctx, ctx->buf, bsz);
+ ctx->bufsz = 0;
+ }
+ /* Absorb the input - rem = leftover part of the input < blocksize) */
+ rem = ctx->meth.absorb(ctx, inp, len);
+ /* Copy the leftover bit of the input into the buffer */
+ if (rem) {
+ memcpy(ctx->buf, inp + len - rem, rem);
+ ctx->bufsz = rem;
+ }
+ return 1;
+}
+
+static int keccak_final(void *vctx, unsigned char *out, size_t *outl,
+ size_t outlen)
+{
+ int ret = 1;
+ KECCAK1600_CTX *ctx = vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (ctx->md_size == SIZE_MAX) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ if (outlen > 0)
+ ret = ctx->meth.final(ctx, out, ctx->md_size);
+
+ *outl = ctx->md_size;
+ return ret;
+}
+
+static int shake_squeeze(void *vctx, unsigned char *out, size_t *outl,
+ size_t outlen)
+{
+ int ret = 1;
+ KECCAK1600_CTX *ctx = vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (ctx->meth.squeeze == NULL)
+ return 0;
+ if (outlen > 0)
+ ret = ctx->meth.squeeze(ctx, out, outlen);
+
+ *outl = outlen;
+ return ret;
+}
+
+/*-
+ * Generic software version of the absorb() and final().
+ */
+static size_t generic_sha3_absorb(void *vctx, const void *inp, size_t len)
+{
+ KECCAK1600_CTX *ctx = vctx;
+
+ if (!(ctx->xof_state == XOF_STATE_INIT ||
+ ctx->xof_state == XOF_STATE_ABSORB))
+ return 0;
+ ctx->xof_state = XOF_STATE_ABSORB;
+ return SHA3_absorb(ctx->A, inp, len, ctx->block_size);
+}
+
+static int generic_sha3_final(void *vctx, unsigned char *out, size_t outlen)
+{
+ return ossl_sha3_final((KECCAK1600_CTX *)vctx, out, outlen);
+}
+
+static int generic_sha3_squeeze(void *vctx, unsigned char *out, size_t outlen)
+{
+ return ossl_sha3_squeeze((KECCAK1600_CTX *)vctx, out, outlen);
+}
+
+static PROV_SHA3_METHOD sha3_generic_md = {
+ generic_sha3_absorb,
+ generic_sha3_final,
+ NULL
+};
+
+static PROV_SHA3_METHOD shake_generic_md =
+{
+ generic_sha3_absorb,
+ generic_sha3_final,
+ generic_sha3_squeeze
+};
+
+#if defined(S390_SHA3)
+
+static sha3_absorb_fn s390x_sha3_absorb;
+static sha3_final_fn s390x_sha3_final;
+static sha3_final_fn s390x_shake_final;
+
+/*-
+ * The platform specific parts of the absorb() and final() for S390X.
+ */
+static size_t s390x_sha3_absorb(void *vctx, const void *inp, size_t len)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ size_t rem = len % ctx->block_size;
+ unsigned int fc;
+
+ if (!(ctx->xof_state == XOF_STATE_INIT ||
+ ctx->xof_state == XOF_STATE_ABSORB))
+ return 0;
+ if (len - rem > 0) {
+ fc = ctx->pad;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0;
+ ctx->xof_state = XOF_STATE_ABSORB;
+ s390x_kimd(inp, len - rem, fc, ctx->A);
+ }
+ return rem;
+}
+
+static int s390x_sha3_final(void *vctx, unsigned char *out, size_t outlen)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ unsigned int fc;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (!(ctx->xof_state == XOF_STATE_INIT ||
+ ctx->xof_state == XOF_STATE_ABSORB))
+ return 0;
+ fc = ctx->pad | S390X_KLMD_DUFOP;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0;
+ ctx->xof_state = XOF_STATE_FINAL;
+ s390x_klmd(ctx->buf, ctx->bufsz, NULL, 0, fc, ctx->A);
+ memcpy(out, ctx->A, outlen);
+ return 1;
+}
+
+static int s390x_shake_final(void *vctx, unsigned char *out, size_t outlen)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ unsigned int fc;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (!(ctx->xof_state == XOF_STATE_INIT ||
+ ctx->xof_state == XOF_STATE_ABSORB))
+ return 0;
+ fc = ctx->pad | S390X_KLMD_DUFOP;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0;
+ ctx->xof_state = XOF_STATE_FINAL;
+ s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A);
+ return 1;
+}
+
+static int s390x_shake_squeeze(void *vctx, unsigned char *out, size_t outlen)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ unsigned int fc;
+ size_t len;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (ctx->xof_state == XOF_STATE_FINAL)
+ return 0;
+ /*
+ * On the first squeeze call, finish the absorb process (incl. padding).
+ */
+ if (ctx->xof_state != XOF_STATE_SQUEEZE) {
+ fc = ctx->pad;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KLMD_NIP : 0;
+ ctx->xof_state = XOF_STATE_SQUEEZE;
+ s390x_klmd(ctx->buf, ctx->bufsz, out, outlen, fc, ctx->A);
+ ctx->bufsz = outlen % ctx->block_size;
+ /* reuse ctx->bufsz to count bytes squeezed from current sponge */
+ return 1;
+ }
+ ctx->xof_state = XOF_STATE_SQUEEZE;
+ if (ctx->bufsz != 0) {
+ len = ctx->block_size - ctx->bufsz;
+ if (outlen < len)
+ len = outlen;
+ memcpy(out, (char *)ctx->A + ctx->bufsz, len);
+ out += len;
+ outlen -= len;
+ ctx->bufsz += len;
+ if (ctx->bufsz == ctx->block_size)
+ ctx->bufsz = 0;
+ }
+ if (outlen == 0)
+ return 1;
+ s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A);
+ ctx->bufsz = outlen % ctx->block_size;
+
+ return 1;
+}
+
+static int s390x_keccakc_final(void *vctx, unsigned char *out, size_t outlen,
+ int padding)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ size_t bsz = ctx->block_size;
+ size_t num = ctx->bufsz;
+ size_t needed = outlen;
+ unsigned int fc;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (!(ctx->xof_state == XOF_STATE_INIT ||
+ ctx->xof_state == XOF_STATE_ABSORB))
+ return 0;
+ fc = ctx->pad;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0;
+ ctx->xof_state = XOF_STATE_FINAL;
+ if (outlen == 0)
+ return 1;
+ memset(ctx->buf + num, 0, bsz - num);
+ ctx->buf[num] = padding;
+ ctx->buf[bsz - 1] |= 0x80;
+ s390x_kimd(ctx->buf, bsz, fc, ctx->A);
+ num = needed > bsz ? bsz : needed;
+ memcpy(out, ctx->A, num);
+ needed -= num;
+ if (needed > 0)
+ s390x_klmd(NULL, 0, out + bsz, needed,
+ ctx->pad | S390X_KLMD_PS | S390X_KLMD_DUFOP, ctx->A);
+
+ return 1;
+}
+
+static int s390x_keccak_final(void *vctx, unsigned char *out, size_t outlen)
+{
+ return s390x_keccakc_final(vctx, out, outlen, 0x01);
+}
+
+static int s390x_kmac_final(void *vctx, unsigned char *out, size_t outlen)
+{
+ return s390x_keccakc_final(vctx, out, outlen, 0x04);
+}
+
+static int s390x_keccakc_squeeze(void *vctx, unsigned char *out, size_t outlen,
+ int padding)
+{
+ KECCAK1600_CTX *ctx = vctx;
+ size_t len;
+ unsigned int fc;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (ctx->xof_state == XOF_STATE_FINAL)
+ return 0;
+ /*
+ * On the first squeeze call, finish the absorb process
+ * by adding the trailing padding and then doing
+ * a final absorb.
+ */
+ if (ctx->xof_state != XOF_STATE_SQUEEZE) {
+ len = ctx->block_size - ctx->bufsz;
+ memset(ctx->buf + ctx->bufsz, 0, len);
+ ctx->buf[ctx->bufsz] = padding;
+ ctx->buf[ctx->block_size - 1] |= 0x80;
+ fc = ctx->pad;
+ fc |= ctx->xof_state == XOF_STATE_INIT ? S390X_KIMD_NIP : 0;
+ s390x_kimd(ctx->buf, ctx->block_size, fc, ctx->A);
+ ctx->bufsz = 0;
+ /* reuse ctx->bufsz to count bytes squeezed from current sponge */
+ }
+ if (ctx->bufsz != 0 || ctx->xof_state != XOF_STATE_SQUEEZE) {
+ len = ctx->block_size - ctx->bufsz;
+ if (outlen < len)
+ len = outlen;
+ memcpy(out, (char *)ctx->A + ctx->bufsz, len);
+ out += len;
+ outlen -= len;
+ ctx->bufsz += len;
+ if (ctx->bufsz == ctx->block_size)
+ ctx->bufsz = 0;
+ }
+ ctx->xof_state = XOF_STATE_SQUEEZE;
+ if (outlen == 0)
+ return 1;
+ s390x_klmd(NULL, 0, out, outlen, ctx->pad | S390X_KLMD_PS, ctx->A);
+ ctx->bufsz = outlen % ctx->block_size;
+
+ return 1;
+}
+
+static int s390x_keccak_squeeze(void *vctx, unsigned char *out, size_t outlen)
+{
+ return s390x_keccakc_squeeze(vctx, out, outlen, 0x01);
+}
+
+static int s390x_kmac_squeeze(void *vctx, unsigned char *out, size_t outlen)
+{
+ return s390x_keccakc_squeeze(vctx, out, outlen, 0x04);
+}
+
+static PROV_SHA3_METHOD sha3_s390x_md = {
+ s390x_sha3_absorb,
+ s390x_sha3_final,
+ NULL,
+};
+
+static PROV_SHA3_METHOD keccak_s390x_md = {
+ s390x_sha3_absorb,
+ s390x_keccak_final,
+ s390x_keccak_squeeze,
+};
+
+static PROV_SHA3_METHOD shake_s390x_md = {
+ s390x_sha3_absorb,
+ s390x_shake_final,
+ s390x_shake_squeeze,
+};
+
+static PROV_SHA3_METHOD kmac_s390x_md = {
+ s390x_sha3_absorb,
+ s390x_kmac_final,
+ s390x_kmac_squeeze,
+};
+
+# define SHAKE_SET_MD(uname, typ) \
+ if (S390_SHA3_CAPABLE(uname)) { \
+ ctx->pad = S390X_##uname; \
+ ctx->meth = typ##_s390x_md; \
+ } else { \
+ ctx->meth = shake_generic_md; \
+ }
+
+# define SHA3_SET_MD(uname, typ) \
+ if (S390_SHA3_CAPABLE(uname)) { \
+ ctx->pad = S390X_##uname; \
+ ctx->meth = typ##_s390x_md; \
+ } else { \
+ ctx->meth = sha3_generic_md; \
+ }
+# define KMAC_SET_MD(bitlen) \
+ if (S390_SHA3_CAPABLE(SHAKE_##bitlen)) { \
+ ctx->pad = S390X_SHAKE_##bitlen; \
+ ctx->meth = kmac_s390x_md; \
+ } else { \
+ ctx->meth = sha3_generic_md; \
+ }
+#elif defined(__aarch64__) && defined(KECCAK1600_ASM)
+# include "arm_arch.h"
+
+static sha3_absorb_fn armsha3_sha3_absorb;
+
+size_t SHA3_absorb_cext(uint64_t A[5][5], const unsigned char *inp, size_t len,
+ size_t r);
+/*-
+ * Hardware-assisted ARMv8.2 SHA3 extension version of the absorb()
+ */
+static size_t armsha3_sha3_absorb(void *vctx, const void *inp, size_t len)
+{
+ KECCAK1600_CTX *ctx = vctx;
+
+ return SHA3_absorb_cext(ctx->A, inp, len, ctx->block_size);
+}
+
+static PROV_SHA3_METHOD sha3_ARMSHA3_md = {
+ armsha3_sha3_absorb,
+ generic_sha3_final
+};
+static PROV_SHA3_METHOD shake_ARMSHA3_md =
+{
+ armsha3_sha3_absorb,
+ generic_sha3_final,
+ generic_sha3_squeeze
+};
+# define SHAKE_SET_MD(uname, typ) \
+ if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \
+ ctx->meth = shake_ARMSHA3_md; \
+ } else { \
+ ctx->meth = shake_generic_md; \
+ }
+
+# define SHA3_SET_MD(uname, typ) \
+ if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \
+ ctx->meth = sha3_ARMSHA3_md; \
+ } else { \
+ ctx->meth = sha3_generic_md; \
+ }
+# define KMAC_SET_MD(bitlen) \
+ if (OPENSSL_armcap_P & ARMV8_HAVE_SHA3_AND_WORTH_USING) { \
+ ctx->meth = sha3_ARMSHA3_md; \
+ } else { \
+ ctx->meth = sha3_generic_md; \
+ }
+#else
+# define SHA3_SET_MD(uname, typ) ctx->meth = sha3_generic_md;
+# define KMAC_SET_MD(bitlen) ctx->meth = sha3_generic_md;
+# define SHAKE_SET_MD(uname, typ) ctx->meth = shake_generic_md;
+#endif /* S390_SHA3 */
+
+#define SHA3_newctx(typ, uname, name, bitlen, pad) \
+static OSSL_FUNC_digest_newctx_fn name##_newctx; \
+static void *name##_newctx(void *provctx) \
+{ \
+ KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) \
+ : NULL; \
+ \
+ if (ctx == NULL) \
+ return NULL; \
+ ossl_sha3_init(ctx, pad, bitlen); \
+ SHA3_SET_MD(uname, typ) \
+ return ctx; \
+}
+
+#define SHAKE_newctx(typ, uname, name, bitlen, mdlen, pad) \
+static OSSL_FUNC_digest_newctx_fn name##_newctx; \
+static void *name##_newctx(void *provctx) \
+{ \
+ KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\
+ : NULL; \
+ \
+ if (ctx == NULL) \
+ return NULL; \
+ ossl_keccak_init(ctx, pad, bitlen, mdlen); \
+ if (mdlen == 0) \
+ ctx->md_size = SIZE_MAX; \
+ SHAKE_SET_MD(uname, typ) \
+ return ctx; \
+}
+
+#define KMAC_newctx(uname, bitlen, pad) \
+static OSSL_FUNC_digest_newctx_fn uname##_newctx; \
+static void *uname##_newctx(void *provctx) \
+{ \
+ KECCAK1600_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) \
+ : NULL; \
+ \
+ if (ctx == NULL) \
+ return NULL; \
+ ossl_keccak_init(ctx, pad, bitlen, 2 * bitlen); \
+ KMAC_SET_MD(bitlen) \
+ return ctx; \
+}
+
+#define PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags) \
+PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \
+const OSSL_DISPATCH ossl_##name##_functions[] = { \
+ { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \
+ { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))keccak_update }, \
+ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))keccak_final }, \
+ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))keccak_freectx }, \
+ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))keccak_dupctx }, \
+ { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))keccak_copyctx }, \
+ PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name)
+
+#define PROV_FUNC_SHA3_DIGEST(name, bitlen, blksize, dgstsize, flags) \
+ PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags), \
+ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init }, \
+ PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
+
+#define PROV_FUNC_SHAKE_DIGEST(name, bitlen, blksize, dgstsize, flags) \
+ PROV_FUNC_SHA3_DIGEST_COMMON(name, bitlen, blksize, dgstsize, flags), \
+ { OSSL_FUNC_DIGEST_SQUEEZE, (void (*)(void))shake_squeeze }, \
+ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))keccak_init_params }, \
+ { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))shake_set_ctx_params }, \
+ { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))shake_settable_ctx_params }, \
+ { OSSL_FUNC_DIGEST_GET_CTX_PARAMS, (void (*)(void))shake_get_ctx_params }, \
+ { OSSL_FUNC_DIGEST_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))shake_gettable_ctx_params }, \
+ PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
+
+static void keccak_freectx(void *vctx)
+{
+ KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx;
+
+ OPENSSL_clear_free(ctx, sizeof(*ctx));
+}
+
+static void keccak_copyctx(void *voutctx, void *vinctx)
+{
+ KECCAK1600_CTX *outctx = (KECCAK1600_CTX *)voutctx;
+ KECCAK1600_CTX *inctx = (KECCAK1600_CTX *)vinctx;
+
+ *outctx = *inctx;
+}
+
+static void *keccak_dupctx(void *ctx)
+{
+ KECCAK1600_CTX *in = (KECCAK1600_CTX *)ctx;
+ KECCAK1600_CTX *ret = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*ret))
+ : NULL;
+
+ if (ret != NULL)
+ *ret = *in;
+ return ret;
+}
+
+static const OSSL_PARAM *shake_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_shake_gettable_ctx_params[] = {
+ {OSSL_DIGEST_PARAM_XOFLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0},
+ {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0},
+ OSSL_PARAM_END
+ };
+ return known_shake_gettable_ctx_params;
+}
+
+static int shake_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+ KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOFLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->md_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ /* Size is an alias of xoflen */
+ p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->md_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *shake_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_shake_settable_ctx_params[] = {
+ {OSSL_DIGEST_PARAM_XOFLEN, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0},
+ {OSSL_DIGEST_PARAM_SIZE, OSSL_PARAM_UNSIGNED_INTEGER, NULL, 0, 0},
+ OSSL_PARAM_END
+ };
+
+ return known_shake_settable_ctx_params;
+}
+
+static int shake_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KECCAK1600_CTX *ctx = (KECCAK1600_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_XOFLEN);
+ if (p == NULL)
+ p = OSSL_PARAM_locate_const(params, OSSL_DIGEST_PARAM_SIZE);
+
+ if (p != NULL && !OSSL_PARAM_get_size_t(p, &ctx->md_size)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER);
+ return 0;
+ }
+ return 1;
+}
+
+#define IMPLEMENT_SHA3_functions(bitlen) \
+ SHA3_newctx(sha3, SHA3_##bitlen, sha3_##bitlen, bitlen, '\x06') \
+ PROV_FUNC_SHA3_DIGEST(sha3_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
+ SHA3_FLAGS)
+
+#define IMPLEMENT_KECCAK_functions(bitlen) \
+ SHA3_newctx(keccak, KECCAK_##bitlen, keccak_##bitlen, bitlen, '\x01') \
+ PROV_FUNC_SHA3_DIGEST(keccak_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), SHA3_MDSIZE(bitlen), \
+ SHA3_FLAGS)
+
+#define IMPLEMENT_SHAKE_functions(bitlen) \
+ SHAKE_newctx(shake, SHAKE_##bitlen, shake_##bitlen, bitlen, \
+ 0 /* no default md length */, '\x1f') \
+ PROV_FUNC_SHAKE_DIGEST(shake_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), 0, \
+ SHAKE_FLAGS)
+
+#define IMPLEMENT_KMAC_functions(bitlen) \
+ KMAC_newctx(keccak_kmac_##bitlen, bitlen, '\x04') \
+ PROV_FUNC_SHAKE_DIGEST(keccak_kmac_##bitlen, bitlen, \
+ SHA3_BLOCKSIZE(bitlen), KMAC_MDSIZE(bitlen), \
+ KMAC_FLAGS)
+
+/* ossl_sha3_224_functions */
+IMPLEMENT_SHA3_functions(224)
+/* ossl_sha3_256_functions */
+IMPLEMENT_SHA3_functions(256)
+/* ossl_sha3_384_functions */
+IMPLEMENT_SHA3_functions(384)
+/* ossl_sha3_512_functions */
+IMPLEMENT_SHA3_functions(512)
+/* ossl_keccak_224_functions */
+IMPLEMENT_KECCAK_functions(224)
+/* ossl_keccak_256_functions */
+IMPLEMENT_KECCAK_functions(256)
+/* ossl_keccak_384_functions */
+IMPLEMENT_KECCAK_functions(384)
+/* ossl_keccak_512_functions */
+IMPLEMENT_KECCAK_functions(512)
+/* ossl_shake_128_functions */
+IMPLEMENT_SHAKE_functions(128)
+/* ossl_shake_256_functions */
+IMPLEMENT_SHAKE_functions(256)
+/* ossl_keccak_kmac_128_functions */
+IMPLEMENT_KMAC_functions(128)
+/* ossl_keccak_kmac_256_functions */
+IMPLEMENT_KMAC_functions(256)
diff --git a/crypto/openssl/providers/implementations/digests/sm3_prov.c b/crypto/openssl/providers/implementations/digests/sm3_prov.c
new file mode 100644
index 000000000000..9d6de5b6ac19
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/sm3_prov.c
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include "internal/sm3.h"
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_sm3_functions */
+IMPLEMENT_digest_functions(sm3, SM3_CTX,
+ SM3_CBLOCK, SM3_DIGEST_LENGTH, 0,
+ ossl_sm3_init, ossl_sm3_update, ossl_sm3_final)
diff --git a/crypto/openssl/providers/implementations/digests/wp_prov.c b/crypto/openssl/providers/implementations/digests/wp_prov.c
new file mode 100644
index 000000000000..2af70b337281
--- /dev/null
+++ b/crypto/openssl/providers/implementations/digests/wp_prov.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Whirlpool low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/whrlpool.h>
+#include "prov/digestcommon.h"
+#include "prov/implementations.h"
+
+/* ossl_wp_functions */
+IMPLEMENT_digest_functions(wp, WHIRLPOOL_CTX,
+ WHIRLPOOL_BBLOCK / 8, WHIRLPOOL_DIGEST_LENGTH, 0,
+ WHIRLPOOL_Init, WHIRLPOOL_Update, WHIRLPOOL_Final)
diff --git a/crypto/openssl/providers/implementations/encode_decode/build.info b/crypto/openssl/providers/implementations/encode_decode/build.info
new file mode 100644
index 000000000000..a1529fdf1258
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/build.info
@@ -0,0 +1,32 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$ENCODER_GOAL=../../libdefault.a
+$DECODER_GOAL=../../libdefault.a
+
+SOURCE[$ENCODER_GOAL]=endecoder_common.c
+
+SOURCE[$DECODER_GOAL]=decode_der2key.c decode_epki2pki.c decode_pem2der.c \
+ decode_msblob2key.c decode_pvk2key.c \
+ decode_spki2typespki.c
+
+SOURCE[$ENCODER_GOAL]=encode_key2any.c encode_key2text.c encode_key2ms.c
+# encode_key2blob.c is only being included when EC is enabled, because we
+# currently only define a "blob" output type for EC public keys. This may
+# change in the future.
+IF[{- !$disabled{ec} -}]
+ SOURCE[$ENCODER_GOAL]=encode_key2blob.c
+ENDIF
+DEPEND[encode_key2any.o]=../../common/include/prov/der_rsa.h
+
+IF[{- !$disabled{'ml-dsa'} -}]
+ SOURCE[$DECODER_GOAL]=ml_dsa_codecs.c
+ENDIF
+
+IF[{- !$disabled{'ml-kem'} -}]
+ SOURCE[$DECODER_GOAL]=ml_kem_codecs.c
+ENDIF
+
+IF[{- !$disabled{'ml-dsa'} || !$disabled{'ml-kem'} -}]
+ SOURCE[$DECODER_GOAL]=ml_common_codecs.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c
new file mode 100644
index 000000000000..a3f0d0897dae
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_der2key.c
@@ -0,0 +1,1307 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/byteorder.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/pem.h> /* PEM_BUFSIZE and public PEM functions */
+#include <openssl/pkcs12.h>
+#include <openssl/provider.h>
+#include <openssl/x509.h>
+#include <openssl/proverr.h>
+#include <openssl/asn1t.h>
+#include "internal/cryptlib.h" /* ossl_assert() */
+#include "crypto/dh.h"
+#include "crypto/dsa.h"
+#include "crypto/ec.h"
+#include "crypto/evp.h"
+#include "crypto/ecx.h"
+#include "crypto/rsa.h"
+#include "crypto/ml_dsa.h"
+#include "crypto/slh_dsa.h"
+#include "crypto/x509.h"
+#include "crypto/ml_kem.h"
+#include "openssl/obj_mac.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+#include "internal/nelem.h"
+#include "ml_dsa_codecs.h"
+#include "ml_kem_codecs.h"
+
+#ifndef OPENSSL_NO_SLH_DSA
+typedef struct {
+ ASN1_OBJECT *oid;
+} BARE_ALGOR;
+
+typedef struct {
+ BARE_ALGOR algor;
+ ASN1_BIT_STRING *pubkey;
+} BARE_PUBKEY;
+
+ASN1_SEQUENCE(BARE_ALGOR) = {
+ ASN1_SIMPLE(BARE_ALGOR, oid, ASN1_OBJECT),
+} static_ASN1_SEQUENCE_END(BARE_ALGOR)
+
+ASN1_SEQUENCE(BARE_PUBKEY) = {
+ ASN1_EMBED(BARE_PUBKEY, algor, BARE_ALGOR),
+ ASN1_SIMPLE(BARE_PUBKEY, pubkey, ASN1_BIT_STRING)
+} static_ASN1_SEQUENCE_END(BARE_PUBKEY)
+#endif /* OPENSSL_NO_SLH_DSA */
+
+struct der2key_ctx_st; /* Forward declaration */
+typedef int check_key_fn(void *, struct der2key_ctx_st *ctx);
+typedef void adjust_key_fn(void *, struct der2key_ctx_st *ctx);
+typedef void free_key_fn(void *);
+typedef void *d2i_PKCS8_fn(const unsigned char **, long,
+ struct der2key_ctx_st *);
+typedef void *d2i_PUBKEY_fn(const unsigned char **, long,
+ struct der2key_ctx_st *);
+struct keytype_desc_st {
+ const char *keytype_name;
+ const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+ /* The input structure name */
+ const char *structure_name;
+
+ /*
+ * The EVP_PKEY_xxx type macro. Should be zero for type specific
+ * structures, non-zero when the outermost structure is PKCS#8 or
+ * SubjectPublicKeyInfo. This determines which of the function
+ * pointers below will be used.
+ */
+ int evp_type;
+
+ /* The selection mask for OSSL_FUNC_decoder_does_selection() */
+ int selection_mask;
+
+ /* For type specific decoders, we use the corresponding d2i */
+ d2i_of_void *d2i_private_key; /* From type-specific DER */
+ d2i_of_void *d2i_public_key; /* From type-specific DER */
+ d2i_of_void *d2i_key_params; /* From type-specific DER */
+ d2i_PKCS8_fn *d2i_PKCS8; /* Wrapped in a PrivateKeyInfo */
+ d2i_PUBKEY_fn *d2i_PUBKEY; /* Wrapped in a SubjectPublicKeyInfo */
+
+ /*
+ * For any key, we may need to check that the key meets expectations.
+ * This is useful when the same functions can decode several variants
+ * of a key.
+ */
+ check_key_fn *check_key;
+
+ /*
+ * For any key, we may need to make provider specific adjustments, such
+ * as ensure the key carries the correct library context.
+ */
+ adjust_key_fn *adjust_key;
+ /* {type}_free() */
+ free_key_fn *free_key;
+};
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct der2key_ctx_st {
+ PROV_CTX *provctx;
+ char propq[OSSL_MAX_PROPQUERY_SIZE];
+ const struct keytype_desc_st *desc;
+ /* The selection that is passed to der2key_decode() */
+ int selection;
+ /* Flag used to signal that a failure is fatal */
+ unsigned int flag_fatal : 1;
+};
+
+typedef void *key_from_pkcs8_t(const PKCS8_PRIV_KEY_INFO *p8inf,
+ OSSL_LIB_CTX *libctx, const char *propq);
+static void *der2key_decode_p8(const unsigned char **input_der,
+ long input_der_len, struct der2key_ctx_st *ctx,
+ key_from_pkcs8_t *key_from_pkcs8)
+{
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+ const X509_ALGOR *alg = NULL;
+ void *key = NULL;
+
+ if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, input_der, input_der_len)) != NULL
+ && PKCS8_pkey_get0(NULL, NULL, NULL, &alg, p8inf)
+ && (OBJ_obj2nid(alg->algorithm) == ctx->desc->evp_type
+ /* Allow decoding sm2 private key with id_ecPublicKey */
+ || (OBJ_obj2nid(alg->algorithm) == NID_X9_62_id_ecPublicKey
+ && ctx->desc->evp_type == NID_sm2)))
+ key = key_from_pkcs8(p8inf, PROV_LIBCTX_OF(ctx->provctx), ctx->propq);
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+
+ return key;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static OSSL_FUNC_decoder_freectx_fn der2key_freectx;
+static OSSL_FUNC_decoder_decode_fn der2key_decode;
+static OSSL_FUNC_decoder_export_object_fn der2key_export_object;
+static OSSL_FUNC_decoder_settable_ctx_params_fn der2key_settable_ctx_params;
+static OSSL_FUNC_decoder_set_ctx_params_fn der2key_set_ctx_params;
+
+static struct der2key_ctx_st *
+der2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+ struct der2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->provctx = provctx;
+ ctx->desc = desc;
+ }
+ return ctx;
+}
+
+static const OSSL_PARAM *der2key_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settables;
+}
+
+static int der2key_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct der2key_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str = ctx->propq;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES);
+ if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq)))
+ return 0;
+
+ return 1;
+}
+
+static void der2key_freectx(void *vctx)
+{
+ struct der2key_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static int der2key_check_selection(int selection,
+ const struct keytype_desc_st *desc)
+{
+ /*
+ * The selections are kinda sorta "levels", i.e. each selection given
+ * here is assumed to include those following.
+ */
+ int checks[] = {
+ OSSL_KEYMGMT_SELECT_PRIVATE_KEY,
+ OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+ OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+ };
+ size_t i;
+
+ /* The decoder implementations made here support guessing */
+ if (selection == 0)
+ return 1;
+
+ for (i = 0; i < OSSL_NELEM(checks); i++) {
+ int check1 = (selection & checks[i]) != 0;
+ int check2 = (desc->selection_mask & checks[i]) != 0;
+
+ /*
+ * If the caller asked for the currently checked bit(s), return
+ * whether the decoder description says it's supported.
+ */
+ if (check1)
+ return check2;
+ }
+
+ /* This should be dead code, but just to be safe... */
+ return 0;
+}
+
+static int der2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct der2key_ctx_st *ctx = vctx;
+ unsigned char *der = NULL;
+ const unsigned char *derp;
+ long der_len = 0;
+ void *key = NULL;
+ int ok = 0;
+
+ ctx->selection = selection;
+ /*
+ * The caller is allowed to specify 0 as a selection mask, to have the
+ * structure and key type guessed. For type-specific structures, this
+ * is not recommended, as some structures are very similar.
+ * Note that 0 isn't the same as OSSL_KEYMGMT_SELECT_ALL, as the latter
+ * signifies a private key structure, where everything else is assumed
+ * to be present as well.
+ */
+ if (selection == 0)
+ selection = ctx->desc->selection_mask;
+ if ((selection & ctx->desc->selection_mask) == 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ ok = ossl_read_der(ctx->provctx, cin, &der, &der_len);
+ if (!ok)
+ goto next;
+
+ ok = 0; /* Assume that we fail */
+
+ ERR_set_mark();
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ derp = der;
+ if (ctx->desc->d2i_PKCS8 != NULL) {
+ key = ctx->desc->d2i_PKCS8(&derp, der_len, ctx);
+ if (ctx->flag_fatal) {
+ ERR_clear_last_mark();
+ goto end;
+ }
+ } else if (ctx->desc->d2i_private_key != NULL) {
+ key = ctx->desc->d2i_private_key(NULL, &derp, der_len);
+ }
+ if (key == NULL && ctx->selection != 0) {
+ ERR_clear_last_mark();
+ goto next;
+ }
+ }
+ if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ derp = der;
+ if (ctx->desc->d2i_PUBKEY != NULL)
+ key = ctx->desc->d2i_PUBKEY(&derp, der_len, ctx);
+ else if (ctx->desc->d2i_public_key != NULL)
+ key = ctx->desc->d2i_public_key(NULL, &derp, der_len);
+ if (key == NULL && ctx->selection != 0) {
+ ERR_clear_last_mark();
+ goto next;
+ }
+ }
+ if (key == NULL && (selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0) {
+ derp = der;
+ if (ctx->desc->d2i_key_params != NULL)
+ key = ctx->desc->d2i_key_params(NULL, &derp, der_len);
+ if (key == NULL && ctx->selection != 0) {
+ ERR_clear_last_mark();
+ goto next;
+ }
+ }
+ if (key == NULL)
+ ERR_clear_last_mark();
+ else
+ ERR_pop_to_mark();
+
+ /*
+ * Last minute check to see if this was the correct type of key. This
+ * should never lead to a fatal error, i.e. the decoding itself was
+ * correct, it was just an unexpected key type. This is generally for
+ * classes of key types that have subtle variants, like RSA-PSS keys as
+ * opposed to plain RSA keys.
+ */
+ if (key != NULL
+ && ctx->desc->check_key != NULL
+ && !ctx->desc->check_key(key, ctx)) {
+ ctx->desc->free_key(key);
+ key = NULL;
+ }
+
+ if (key != NULL && ctx->desc->adjust_key != NULL)
+ ctx->desc->adjust_key(key, ctx);
+
+ next:
+ /*
+ * Indicated that we successfully decoded something, or not at all.
+ * Ending up "empty handed" is not an error.
+ */
+ ok = 1;
+
+ /*
+ * We free memory here so it's not held up during the callback, because
+ * we know the process is recursive and the allocated chunks of memory
+ * add up.
+ */
+ OPENSSL_free(der);
+ der = NULL;
+
+ if (key != NULL) {
+ OSSL_PARAM params[4];
+ int object_type = OSSL_OBJECT_PKEY;
+
+ params[0] =
+ OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+
+#ifndef OPENSSL_NO_SM2
+ if (strcmp(ctx->desc->keytype_name, "EC") == 0
+ && (EC_KEY_get_flags(key) & EC_FLAG_SM2_RANGE) != 0)
+ params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ "SM2", 0);
+ else
+#endif
+ params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ (char *)ctx->desc->keytype_name,
+ 0);
+ /* The address of the key becomes the octet string */
+ params[2] =
+ OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+ &key, sizeof(key));
+ params[3] = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ }
+
+ end:
+ ctx->desc->free_key(key);
+ OPENSSL_free(der);
+
+ return ok;
+}
+
+static int der2key_export_object(void *vctx,
+ const void *reference, size_t reference_sz,
+ OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+ struct der2key_ctx_st *ctx = vctx;
+ OSSL_FUNC_keymgmt_export_fn *export =
+ ossl_prov_get_keymgmt_export(ctx->desc->fns);
+ void *keydata;
+
+ if (reference_sz == sizeof(keydata) && export != NULL) {
+ int selection = ctx->selection;
+
+ if (selection == 0)
+ selection = OSSL_KEYMGMT_SELECT_ALL;
+ /* The contents of the reference is the address to our object */
+ keydata = *(void **)reference;
+
+ return export(keydata, selection, export_cb, export_cbarg);
+ }
+ return 0;
+}
+
+#define D2I_PUBKEY_NOCTX(n, f) \
+ static void * \
+ n##_d2i_PUBKEY(const unsigned char **der, long der_len, \
+ ossl_unused struct der2key_ctx_st *ctx) \
+ { \
+ return f(NULL, der, der_len); \
+ }
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DH
+# define dh_evp_type EVP_PKEY_DH
+# define dh_d2i_private_key NULL
+# define dh_d2i_public_key NULL
+# define dh_d2i_key_params (d2i_of_void *)d2i_DHparams
+# define dh_free (free_key_fn *)DH_free
+# define dh_check NULL
+
+static void *dh_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_dh_key_from_pkcs8);
+}
+
+D2I_PUBKEY_NOCTX(dh, ossl_d2i_DH_PUBKEY)
+D2I_PUBKEY_NOCTX(dhx, ossl_d2i_DHx_PUBKEY)
+
+static void dh_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+ ossl_dh_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+# define dhx_evp_type EVP_PKEY_DHX
+# define dhx_d2i_private_key NULL
+# define dhx_d2i_public_key NULL
+# define dhx_d2i_key_params (d2i_of_void *)d2i_DHxparams
+# define dhx_d2i_PKCS8 dh_d2i_PKCS8
+# define dhx_free (free_key_fn *)DH_free
+# define dhx_check NULL
+# define dhx_adjust dh_adjust
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DSA
+# define dsa_evp_type EVP_PKEY_DSA
+# define dsa_d2i_private_key (d2i_of_void *)d2i_DSAPrivateKey
+# define dsa_d2i_public_key (d2i_of_void *)d2i_DSAPublicKey
+# define dsa_d2i_key_params (d2i_of_void *)d2i_DSAparams
+# define dsa_free (free_key_fn *)DSA_free
+# define dsa_check NULL
+
+static void *dsa_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_dsa_key_from_pkcs8);
+}
+
+D2I_PUBKEY_NOCTX(dsa, ossl_d2i_DSA_PUBKEY)
+
+static void dsa_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+ ossl_dsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_EC
+# define ec_evp_type EVP_PKEY_EC
+# define ec_d2i_private_key (d2i_of_void *)d2i_ECPrivateKey
+# define ec_d2i_public_key NULL
+# define ec_d2i_key_params (d2i_of_void *)d2i_ECParameters
+# define ec_free (free_key_fn *)EC_KEY_free
+
+static void *ec_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8);
+}
+
+D2I_PUBKEY_NOCTX(ec, d2i_EC_PUBKEY)
+
+static int ec_check(void *key, struct der2key_ctx_st *ctx)
+{
+ /* We're trying to be clever by comparing two truths */
+ int ret = 0;
+ int sm2 = (EC_KEY_get_flags(key) & EC_FLAG_SM2_RANGE) != 0;
+
+ if (sm2)
+ ret = ctx->desc->evp_type == EVP_PKEY_SM2
+ || ctx->desc->evp_type == NID_X9_62_id_ecPublicKey;
+ else
+ ret = ctx->desc->evp_type != EVP_PKEY_SM2;
+
+ return ret;
+}
+
+static void ec_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+ ossl_ec_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+# ifndef OPENSSL_NO_ECX
+/*
+ * ED25519, ED448, X25519, X448 only implement PKCS#8 and SubjectPublicKeyInfo,
+ * so no d2i functions to be had.
+ */
+
+static void *ecx_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_ecx_key_from_pkcs8);
+}
+
+D2I_PUBKEY_NOCTX(ed25519, ossl_d2i_ED25519_PUBKEY)
+D2I_PUBKEY_NOCTX(ed448, ossl_d2i_ED448_PUBKEY)
+D2I_PUBKEY_NOCTX(x25519, ossl_d2i_X25519_PUBKEY)
+D2I_PUBKEY_NOCTX(x448, ossl_d2i_X448_PUBKEY)
+
+static void ecx_key_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+ ossl_ecx_key_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+# define ed25519_evp_type EVP_PKEY_ED25519
+# define ed25519_d2i_private_key NULL
+# define ed25519_d2i_public_key NULL
+# define ed25519_d2i_key_params NULL
+# define ed25519_d2i_PKCS8 ecx_d2i_PKCS8
+# define ed25519_free (free_key_fn *)ossl_ecx_key_free
+# define ed25519_check NULL
+# define ed25519_adjust ecx_key_adjust
+
+# define ed448_evp_type EVP_PKEY_ED448
+# define ed448_d2i_private_key NULL
+# define ed448_d2i_public_key NULL
+# define ed448_d2i_key_params NULL
+# define ed448_d2i_PKCS8 ecx_d2i_PKCS8
+# define ed448_free (free_key_fn *)ossl_ecx_key_free
+# define ed448_check NULL
+# define ed448_adjust ecx_key_adjust
+
+# define x25519_evp_type EVP_PKEY_X25519
+# define x25519_d2i_private_key NULL
+# define x25519_d2i_public_key NULL
+# define x25519_d2i_key_params NULL
+# define x25519_d2i_PKCS8 ecx_d2i_PKCS8
+# define x25519_free (free_key_fn *)ossl_ecx_key_free
+# define x25519_check NULL
+# define x25519_adjust ecx_key_adjust
+
+# define x448_evp_type EVP_PKEY_X448
+# define x448_d2i_private_key NULL
+# define x448_d2i_public_key NULL
+# define x448_d2i_key_params NULL
+# define x448_d2i_PKCS8 ecx_d2i_PKCS8
+# define x448_free (free_key_fn *)ossl_ecx_key_free
+# define x448_check NULL
+# define x448_adjust ecx_key_adjust
+# endif /* OPENSSL_NO_ECX */
+
+# ifndef OPENSSL_NO_SM2
+# define sm2_evp_type EVP_PKEY_SM2
+# define sm2_d2i_private_key (d2i_of_void *)d2i_ECPrivateKey
+# define sm2_d2i_public_key NULL
+# define sm2_d2i_key_params (d2i_of_void *)d2i_ECParameters
+# define sm2_d2i_PUBKEY ec_d2i_PUBKEY
+# define sm2_free (free_key_fn *)EC_KEY_free
+# define sm2_check ec_check
+# define sm2_adjust ec_adjust
+
+static void *sm2_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_ec_key_from_pkcs8);
+}
+# endif
+
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_KEM
+static void *
+ml_kem_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
+{
+ ML_KEM_KEY *key;
+
+ key = ossl_ml_kem_d2i_PKCS8(*der, der_len, ctx->desc->evp_type,
+ ctx->provctx, ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
+}
+
+static ossl_inline void *
+ml_kem_d2i_PUBKEY(const uint8_t **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ ML_KEM_KEY *key;
+
+ key = ossl_ml_kem_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type,
+ ctx->provctx, ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
+}
+
+# define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512
+# define ml_kem_512_d2i_private_key NULL
+# define ml_kem_512_d2i_public_key NULL
+# define ml_kem_512_d2i_key_params NULL
+# define ml_kem_512_d2i_PUBKEY ml_kem_d2i_PUBKEY
+# define ml_kem_512_d2i_PKCS8 ml_kem_d2i_PKCS8
+# define ml_kem_512_free (free_key_fn *)ossl_ml_kem_key_free
+# define ml_kem_512_check NULL
+# define ml_kem_512_adjust NULL
+
+# define ml_kem_768_evp_type EVP_PKEY_ML_KEM_768
+# define ml_kem_768_d2i_private_key NULL
+# define ml_kem_768_d2i_public_key NULL
+# define ml_kem_768_d2i_key_params NULL
+# define ml_kem_768_d2i_PUBKEY ml_kem_d2i_PUBKEY
+# define ml_kem_768_d2i_PKCS8 ml_kem_d2i_PKCS8
+# define ml_kem_768_free (free_key_fn *)ossl_ml_kem_key_free
+# define ml_kem_768_check NULL
+# define ml_kem_768_adjust NULL
+
+# define ml_kem_1024_evp_type EVP_PKEY_ML_KEM_1024
+# define ml_kem_1024_d2i_private_key NULL
+# define ml_kem_1024_d2i_public_key NULL
+# define ml_kem_1024_d2i_PUBKEY ml_kem_d2i_PUBKEY
+# define ml_kem_1024_d2i_PKCS8 ml_kem_d2i_PKCS8
+# define ml_kem_1024_d2i_key_params NULL
+# define ml_kem_1024_free (free_key_fn *)ossl_ml_kem_key_free
+# define ml_kem_1024_check NULL
+# define ml_kem_1024_adjust NULL
+
+#endif
+
+#ifndef OPENSSL_NO_SLH_DSA
+static void *
+slh_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
+{
+ SLH_DSA_KEY *key = NULL, *ret = NULL;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+ const unsigned char *p;
+ const X509_ALGOR *alg = NULL;
+ int plen, ptype;
+
+ if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, der, der_len)) == NULL
+ || !PKCS8_pkey_get0(NULL, &p, &plen, &alg, p8inf))
+ goto end;
+
+ /* Algorithm parameters must be absent. */
+ if ((X509_ALGOR_get0(NULL, &ptype, NULL, alg), ptype != V_ASN1_UNDEF)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS,
+ "unexpected parameters with a PKCS#8 %s private key",
+ ctx->desc->keytype_name);
+ goto end;
+ }
+ if (OBJ_obj2nid(alg->algorithm) != ctx->desc->evp_type)
+ goto end;
+ if ((key = ossl_slh_dsa_key_new(libctx, ctx->propq,
+ ctx->desc->keytype_name)) == NULL)
+ goto end;
+
+ if (!ossl_slh_dsa_set_priv(key, p, plen))
+ goto end;
+ ret = key;
+ end:
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ if (ret == NULL)
+ ossl_slh_dsa_key_free(key);
+ return ret;
+}
+
+static ossl_inline void *slh_dsa_d2i_PUBKEY(const uint8_t **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ int ok = 0;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ SLH_DSA_KEY *ret = NULL;
+ BARE_PUBKEY *spki = NULL;
+ const uint8_t *end = *der;
+ size_t len;
+
+ ret = ossl_slh_dsa_key_new(libctx, ctx->propq, ctx->desc->keytype_name);
+ if (ret == NULL)
+ return NULL;
+ len = ossl_slh_dsa_key_get_pub_len(ret);
+
+ /*-
+ * The DER ASN.1 encoding of SLH-DSA public keys prepends 18 bytes to the
+ * encoded public key (since the largest public key size is 64 bytes):
+ *
+ * - 2 byte outer sequence tag and length
+ * - 2 byte algorithm sequence tag and length
+ * - 2 byte algorithm OID tag and length
+ * - 9 byte algorithm OID
+ * - 2 byte bit string tag and length
+ * - 1 bitstring lead byte
+ *
+ * Check that we have the right OID, the bit string has no "bits left" and
+ * that we consume all the input exactly.
+ */
+ if (der_len != 18 + (long)len) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "unexpected %s public key length: %ld != %ld",
+ ctx->desc->keytype_name, der_len,
+ 18 + (long)len);
+ goto err;
+ }
+
+ if ((spki = OPENSSL_zalloc(sizeof(*spki))) == NULL)
+ goto err;
+
+ /* The spki storage is freed on error */
+ if (ASN1_item_d2i_ex((ASN1_VALUE **)&spki, &end, der_len,
+ ASN1_ITEM_rptr(BARE_PUBKEY), NULL, NULL) == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "malformed %s public key ASN.1 encoding",
+ ossl_slh_dsa_key_get_name(ret));
+ goto err;
+ }
+
+ /* The spki structure now owns some memory */
+ if ((spki->pubkey->flags & 0x7) != 0 || end != *der + der_len) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "malformed %s public key ASN.1 encoding",
+ ossl_slh_dsa_key_get_name(ret));
+ goto err;
+ }
+ if (OBJ_cmp(OBJ_nid2obj(ctx->desc->evp_type), spki->algor.oid) != 0) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "unexpected algorithm OID for an %s public key",
+ ossl_slh_dsa_key_get_name(ret));
+ goto err;
+ }
+
+ if (!ossl_slh_dsa_set_pub(ret, spki->pubkey->data, spki->pubkey->length)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "failed to parse %s public key from the input data",
+ ossl_slh_dsa_key_get_name(ret));
+ goto err;
+ }
+ ok = 1;
+ err:
+ if (spki != NULL) {
+ ASN1_OBJECT_free(spki->algor.oid);
+ ASN1_BIT_STRING_free(spki->pubkey);
+ OPENSSL_free(spki);
+ }
+ if (!ok) {
+ ossl_slh_dsa_key_free(ret);
+ ret = NULL;
+ }
+ return ret;
+}
+
+# define slh_dsa_sha2_128s_evp_type EVP_PKEY_SLH_DSA_SHA2_128S
+# define slh_dsa_sha2_128s_d2i_private_key NULL
+# define slh_dsa_sha2_128s_d2i_public_key NULL
+# define slh_dsa_sha2_128s_d2i_key_params NULL
+# define slh_dsa_sha2_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_128s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_128s_check NULL
+# define slh_dsa_sha2_128s_adjust NULL
+
+# define slh_dsa_sha2_128f_evp_type EVP_PKEY_SLH_DSA_SHA2_128F
+# define slh_dsa_sha2_128f_d2i_private_key NULL
+# define slh_dsa_sha2_128f_d2i_public_key NULL
+# define slh_dsa_sha2_128f_d2i_key_params NULL
+# define slh_dsa_sha2_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_128f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_128f_check NULL
+# define slh_dsa_sha2_128f_adjust NULL
+
+# define slh_dsa_sha2_192s_evp_type EVP_PKEY_SLH_DSA_SHA2_192S
+# define slh_dsa_sha2_192s_d2i_private_key NULL
+# define slh_dsa_sha2_192s_d2i_public_key NULL
+# define slh_dsa_sha2_192s_d2i_key_params NULL
+# define slh_dsa_sha2_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_192s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_192s_check NULL
+# define slh_dsa_sha2_192s_adjust NULL
+
+# define slh_dsa_sha2_192f_evp_type EVP_PKEY_SLH_DSA_SHA2_192F
+# define slh_dsa_sha2_192f_d2i_private_key NULL
+# define slh_dsa_sha2_192f_d2i_public_key NULL
+# define slh_dsa_sha2_192f_d2i_key_params NULL
+# define slh_dsa_sha2_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_192f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_192f_check NULL
+# define slh_dsa_sha2_192f_adjust NULL
+
+# define slh_dsa_sha2_256s_evp_type EVP_PKEY_SLH_DSA_SHA2_256S
+# define slh_dsa_sha2_256s_d2i_private_key NULL
+# define slh_dsa_sha2_256s_d2i_public_key NULL
+# define slh_dsa_sha2_256s_d2i_key_params NULL
+# define slh_dsa_sha2_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_256s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_256s_check NULL
+# define slh_dsa_sha2_256s_adjust NULL
+
+# define slh_dsa_sha2_256f_evp_type EVP_PKEY_SLH_DSA_SHA2_256F
+# define slh_dsa_sha2_256f_d2i_private_key NULL
+# define slh_dsa_sha2_256f_d2i_public_key NULL
+# define slh_dsa_sha2_256f_d2i_key_params NULL
+# define slh_dsa_sha2_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_sha2_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_sha2_256f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_sha2_256f_check NULL
+# define slh_dsa_sha2_256f_adjust NULL
+
+# define slh_dsa_shake_128s_evp_type EVP_PKEY_SLH_DSA_SHAKE_128S
+# define slh_dsa_shake_128s_d2i_private_key NULL
+# define slh_dsa_shake_128s_d2i_public_key NULL
+# define slh_dsa_shake_128s_d2i_key_params NULL
+# define slh_dsa_shake_128s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_128s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_128s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_128s_check NULL
+# define slh_dsa_shake_128s_adjust NULL
+
+# define slh_dsa_shake_128f_evp_type EVP_PKEY_SLH_DSA_SHAKE_128F
+# define slh_dsa_shake_128f_d2i_private_key NULL
+# define slh_dsa_shake_128f_d2i_public_key NULL
+# define slh_dsa_shake_128f_d2i_key_params NULL
+# define slh_dsa_shake_128f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_128f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_128f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_128f_check NULL
+# define slh_dsa_shake_128f_adjust NULL
+
+# define slh_dsa_shake_192s_evp_type EVP_PKEY_SLH_DSA_SHAKE_192S
+# define slh_dsa_shake_192s_d2i_private_key NULL
+# define slh_dsa_shake_192s_d2i_public_key NULL
+# define slh_dsa_shake_192s_d2i_key_params NULL
+# define slh_dsa_shake_192s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_192s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_192s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_192s_check NULL
+# define slh_dsa_shake_192s_adjust NULL
+
+# define slh_dsa_shake_192f_evp_type EVP_PKEY_SLH_DSA_SHAKE_192F
+# define slh_dsa_shake_192f_d2i_private_key NULL
+# define slh_dsa_shake_192f_d2i_public_key NULL
+# define slh_dsa_shake_192f_d2i_key_params NULL
+# define slh_dsa_shake_192f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_192f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_192f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_192f_check NULL
+# define slh_dsa_shake_192f_adjust NULL
+
+# define slh_dsa_shake_256s_evp_type EVP_PKEY_SLH_DSA_SHAKE_256S
+# define slh_dsa_shake_256s_d2i_private_key NULL
+# define slh_dsa_shake_256s_d2i_public_key NULL
+# define slh_dsa_shake_256s_d2i_key_params NULL
+# define slh_dsa_shake_256s_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_256s_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_256s_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_256s_check NULL
+# define slh_dsa_shake_256s_adjust NULL
+
+# define slh_dsa_shake_256f_evp_type EVP_PKEY_SLH_DSA_SHAKE_256F
+# define slh_dsa_shake_256f_d2i_private_key NULL
+# define slh_dsa_shake_256f_d2i_public_key NULL
+# define slh_dsa_shake_256f_d2i_key_params NULL
+# define slh_dsa_shake_256f_d2i_PKCS8 slh_dsa_d2i_PKCS8
+# define slh_dsa_shake_256f_d2i_PUBKEY slh_dsa_d2i_PUBKEY
+# define slh_dsa_shake_256f_free (free_key_fn *)ossl_slh_dsa_key_free
+# define slh_dsa_shake_256f_check NULL
+# define slh_dsa_shake_256f_adjust NULL
+#endif /* OPENSSL_NO_SLH_DSA */
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_evp_type EVP_PKEY_RSA
+#define rsa_d2i_private_key (d2i_of_void *)d2i_RSAPrivateKey
+#define rsa_d2i_public_key (d2i_of_void *)d2i_RSAPublicKey
+#define rsa_d2i_key_params NULL
+#define rsa_free (free_key_fn *)RSA_free
+
+static void *rsa_d2i_PKCS8(const unsigned char **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ return der2key_decode_p8(der, der_len, ctx,
+ (key_from_pkcs8_t *)ossl_rsa_key_from_pkcs8);
+}
+
+static void *
+rsa_d2i_PUBKEY(const unsigned char **der, long der_len,
+ ossl_unused struct der2key_ctx_st *ctx)
+{
+ return d2i_RSA_PUBKEY(NULL, der, der_len);
+}
+
+static int rsa_check(void *key, struct der2key_ctx_st *ctx)
+{
+ int valid;
+
+ switch (RSA_test_flags(key, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ valid = (ctx->desc->evp_type == EVP_PKEY_RSA);
+ break;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ valid = (ctx->desc->evp_type == EVP_PKEY_RSA_PSS);
+ break;
+ default:
+ /* Currently unsupported RSA key type */
+ valid = 0;
+ }
+
+ valid = (valid && ossl_rsa_check_factors(key));
+
+ return valid;
+}
+
+static void rsa_adjust(void *key, struct der2key_ctx_st *ctx)
+{
+ ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsapss_evp_type EVP_PKEY_RSA_PSS
+#define rsapss_d2i_private_key (d2i_of_void *)d2i_RSAPrivateKey
+#define rsapss_d2i_public_key (d2i_of_void *)d2i_RSAPublicKey
+#define rsapss_d2i_key_params NULL
+#define rsapss_d2i_PKCS8 rsa_d2i_PKCS8
+#define rsapss_d2i_PUBKEY rsa_d2i_PUBKEY
+#define rsapss_free (free_key_fn *)RSA_free
+#define rsapss_check rsa_check
+#define rsapss_adjust rsa_adjust
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_DSA
+static void *
+ml_dsa_d2i_PKCS8(const uint8_t **der, long der_len, struct der2key_ctx_st *ctx)
+{
+ ML_DSA_KEY *key;
+
+ key = ossl_ml_dsa_d2i_PKCS8(*der, der_len, ctx->desc->evp_type,
+ ctx->provctx, ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
+}
+
+static ossl_inline void * ml_dsa_d2i_PUBKEY(const uint8_t **der, long der_len,
+ struct der2key_ctx_st *ctx)
+{
+ ML_DSA_KEY *key;
+
+ key = ossl_ml_dsa_d2i_PUBKEY(*der, der_len, ctx->desc->evp_type,
+ ctx->provctx, ctx->propq);
+ if (key != NULL)
+ *der += der_len;
+ return key;
+}
+
+# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44
+# define ml_dsa_44_d2i_private_key NULL
+# define ml_dsa_44_d2i_public_key NULL
+# define ml_dsa_44_d2i_key_params NULL
+# define ml_dsa_44_d2i_PUBKEY ml_dsa_d2i_PUBKEY
+# define ml_dsa_44_d2i_PKCS8 ml_dsa_d2i_PKCS8
+# define ml_dsa_44_free (free_key_fn *)ossl_ml_dsa_key_free
+# define ml_dsa_44_check NULL
+# define ml_dsa_44_adjust NULL
+
+# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65
+# define ml_dsa_65_d2i_private_key NULL
+# define ml_dsa_65_d2i_public_key NULL
+# define ml_dsa_65_d2i_key_params NULL
+# define ml_dsa_65_d2i_PUBKEY ml_dsa_d2i_PUBKEY
+# define ml_dsa_65_d2i_PKCS8 ml_dsa_d2i_PKCS8
+# define ml_dsa_65_free (free_key_fn *)ossl_ml_dsa_key_free
+# define ml_dsa_65_check NULL
+# define ml_dsa_65_adjust NULL
+
+# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87
+# define ml_dsa_87_d2i_private_key NULL
+# define ml_dsa_87_d2i_public_key NULL
+# define ml_dsa_87_d2i_PUBKEY ml_dsa_d2i_PUBKEY
+# define ml_dsa_87_d2i_PKCS8 ml_dsa_d2i_PKCS8
+# define ml_dsa_87_d2i_key_params NULL
+# define ml_dsa_87_free (free_key_fn *)ossl_ml_dsa_key_free
+# define ml_dsa_87_check NULL
+# define ml_dsa_87_adjust NULL
+
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * The DO_ macros help define the selection mask and the method functions
+ * for each kind of object we want to decode.
+ */
+#define DO_type_specific_keypair(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_KEYPAIR ), \
+ keytype##_d2i_private_key, \
+ keytype##_d2i_public_key, \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_type_specific_pub(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ), \
+ NULL, \
+ keytype##_d2i_public_key, \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_type_specific_priv(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ), \
+ keytype##_d2i_private_key, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_type_specific_params(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \
+ NULL, \
+ NULL, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_type_specific(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_ALL ), \
+ keytype##_d2i_private_key, \
+ keytype##_d2i_public_key, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_type_specific_no_pub(keytype) \
+ "type-specific", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY \
+ | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \
+ keytype##_d2i_private_key, \
+ NULL, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_PrivateKeyInfo(keytype) \
+ "PrivateKeyInfo", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY ), \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_d2i_PKCS8, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_SubjectPublicKeyInfo(keytype) \
+ "SubjectPublicKeyInfo", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PUBLIC_KEY ), \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_d2i_PUBKEY, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_DH(keytype) \
+ "DH", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \
+ NULL, \
+ NULL, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_DHX(keytype) \
+ "DHX", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \
+ NULL, \
+ NULL, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_DSA(keytype) \
+ "DSA", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_ALL ), \
+ keytype##_d2i_private_key, \
+ keytype##_d2i_public_key, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_EC(keytype) \
+ "EC", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_PRIVATE_KEY \
+ | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS ), \
+ keytype##_d2i_private_key, \
+ NULL, \
+ keytype##_d2i_key_params, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+#define DO_RSA(keytype) \
+ "RSA", keytype##_evp_type, \
+ ( OSSL_KEYMGMT_SELECT_KEYPAIR ), \
+ keytype##_d2i_private_key, \
+ keytype##_d2i_public_key, \
+ NULL, \
+ NULL, \
+ NULL, \
+ keytype##_check, \
+ keytype##_adjust, \
+ keytype##_free
+
+/*
+ * MAKE_DECODER is the single driver for creating OSSL_DISPATCH tables.
+ * It takes the following arguments:
+ *
+ * keytype_name The implementation key type as a string.
+ * keytype The implementation key type. This must correspond exactly
+ * to our existing keymgmt keytype names... in other words,
+ * there must exist an ossl_##keytype##_keymgmt_functions.
+ * type The type name for the set of functions that implement the
+ * decoder for the key type. This isn't necessarily the same
+ * as keytype. For example, the key types ed25519, ed448,
+ * x25519 and x448 are all handled by the same functions with
+ * the common type name ecx.
+ * kind The kind of support to implement. This translates into
+ * the DO_##kind macros above, to populate the keytype_desc_st
+ * structure.
+ */
+#define MAKE_DECODER(keytype_name, keytype, type, kind) \
+ static const struct keytype_desc_st kind##_##keytype##_desc = \
+ { keytype_name, ossl_##keytype##_keymgmt_functions, \
+ DO_##kind(keytype) }; \
+ \
+ static OSSL_FUNC_decoder_newctx_fn kind##_der2##keytype##_newctx; \
+ \
+ static void *kind##_der2##keytype##_newctx(void *provctx) \
+ { \
+ return der2key_newctx(provctx, &kind##_##keytype##_desc); \
+ } \
+ static int kind##_der2##keytype##_does_selection(void *provctx, \
+ int selection) \
+ { \
+ return der2key_check_selection(selection, \
+ &kind##_##keytype##_desc); \
+ } \
+ const OSSL_DISPATCH \
+ ossl_##kind##_der_to_##keytype##_decoder_functions[] = { \
+ { OSSL_FUNC_DECODER_NEWCTX, \
+ (void (*)(void))kind##_der2##keytype##_newctx }, \
+ { OSSL_FUNC_DECODER_FREECTX, \
+ (void (*)(void))der2key_freectx }, \
+ { OSSL_FUNC_DECODER_DOES_SELECTION, \
+ (void (*)(void))kind##_der2##keytype##_does_selection }, \
+ { OSSL_FUNC_DECODER_DECODE, \
+ (void (*)(void))der2key_decode }, \
+ { OSSL_FUNC_DECODER_EXPORT_OBJECT, \
+ (void (*)(void))der2key_export_object }, \
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))der2key_settable_ctx_params }, \
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \
+ (void (*)(void))der2key_set_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_DH
+MAKE_DECODER("DH", dh, dh, PrivateKeyInfo);
+MAKE_DECODER("DH", dh, dh, SubjectPublicKeyInfo);
+MAKE_DECODER("DH", dh, dh, type_specific_params);
+MAKE_DECODER("DH", dh, dh, DH);
+MAKE_DECODER("DHX", dhx, dhx, PrivateKeyInfo);
+MAKE_DECODER("DHX", dhx, dhx, SubjectPublicKeyInfo);
+MAKE_DECODER("DHX", dhx, dhx, type_specific_params);
+MAKE_DECODER("DHX", dhx, dhx, DHX);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_DECODER("DSA", dsa, dsa, PrivateKeyInfo);
+MAKE_DECODER("DSA", dsa, dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("DSA", dsa, dsa, type_specific);
+MAKE_DECODER("DSA", dsa, dsa, DSA);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_DECODER("EC", ec, ec, PrivateKeyInfo);
+MAKE_DECODER("EC", ec, ec, SubjectPublicKeyInfo);
+MAKE_DECODER("EC", ec, ec, type_specific_no_pub);
+MAKE_DECODER("EC", ec, ec, EC);
+# ifndef OPENSSL_NO_ECX
+MAKE_DECODER("X25519", x25519, ecx, PrivateKeyInfo);
+MAKE_DECODER("X25519", x25519, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("X448", x448, ecx, PrivateKeyInfo);
+MAKE_DECODER("X448", x448, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("ED25519", ed25519, ecx, PrivateKeyInfo);
+MAKE_DECODER("ED25519", ed25519, ecx, SubjectPublicKeyInfo);
+MAKE_DECODER("ED448", ed448, ecx, PrivateKeyInfo);
+MAKE_DECODER("ED448", ed448, ecx, SubjectPublicKeyInfo);
+# endif
+# ifndef OPENSSL_NO_SM2
+MAKE_DECODER("SM2", sm2, ec, PrivateKeyInfo);
+MAKE_DECODER("SM2", sm2, ec, SubjectPublicKeyInfo);
+MAKE_DECODER("SM2", sm2, sm2, type_specific_no_pub);
+# endif
+#endif
+#ifndef OPENSSL_NO_ML_KEM
+MAKE_DECODER("ML-KEM-512", ml_kem_512, ml_kem_512, PrivateKeyInfo);
+MAKE_DECODER("ML-KEM-512", ml_kem_512, ml_kem_512, SubjectPublicKeyInfo);
+MAKE_DECODER("ML-KEM-768", ml_kem_768, ml_kem_768, PrivateKeyInfo);
+MAKE_DECODER("ML-KEM-768", ml_kem_768, ml_kem_768, SubjectPublicKeyInfo);
+MAKE_DECODER("ML-KEM-1024", ml_kem_1024, ml_kem_1024, PrivateKeyInfo);
+MAKE_DECODER("ML-KEM-1024", ml_kem_1024, ml_kem_1024, SubjectPublicKeyInfo);
+#endif
+#ifndef OPENSSL_NO_SLH_DSA
+MAKE_DECODER("SLH-DSA-SHA2-128s", slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-128f", slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-192s", slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-192f", slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-256s", slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-256f", slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-128s", slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-128f", slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-192s", slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-192f", slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-256s", slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-256f", slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo);
+
+MAKE_DECODER("SLH-DSA-SHA2-128s", slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-128f", slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-192s", slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-192f", slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-256s", slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHA2-256f", slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-128s", slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-128f", slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-192s", slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-192f", slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-256s", slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo);
+MAKE_DECODER("SLH-DSA-SHAKE-256f", slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo);
+#endif /* OPENSSL_NO_SLH_DSA */
+MAKE_DECODER("RSA", rsa, rsa, PrivateKeyInfo);
+MAKE_DECODER("RSA", rsa, rsa, SubjectPublicKeyInfo);
+MAKE_DECODER("RSA", rsa, rsa, type_specific_keypair);
+MAKE_DECODER("RSA", rsa, rsa, RSA);
+MAKE_DECODER("RSA-PSS", rsapss, rsapss, PrivateKeyInfo);
+MAKE_DECODER("RSA-PSS", rsapss, rsapss, SubjectPublicKeyInfo);
+
+#ifndef OPENSSL_NO_ML_DSA
+MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, PrivateKeyInfo);
+MAKE_DECODER("ML-DSA-44", ml_dsa_44, ml_dsa_44, SubjectPublicKeyInfo);
+MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, PrivateKeyInfo);
+MAKE_DECODER("ML-DSA-65", ml_dsa_65, ml_dsa_65, SubjectPublicKeyInfo);
+MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, PrivateKeyInfo);
+MAKE_DECODER("ML-DSA-87", ml_dsa_87, ml_dsa_87, SubjectPublicKeyInfo);
+#endif
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c b/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c
new file mode 100644
index 000000000000..aecf2eb4f2b4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_epki2pki.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509.h>
+#include <openssl/proverr.h>
+#include "internal/asn1.h"
+#include "internal/sizes.h"
+#include "prov/bio.h"
+#include "prov/decoders.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+static OSSL_FUNC_decoder_newctx_fn epki2pki_newctx;
+static OSSL_FUNC_decoder_freectx_fn epki2pki_freectx;
+static OSSL_FUNC_decoder_decode_fn epki2pki_decode;
+static OSSL_FUNC_decoder_settable_ctx_params_fn epki2pki_settable_ctx_params;
+static OSSL_FUNC_decoder_set_ctx_params_fn epki2pki_set_ctx_params;
+
+/*
+ * Context used for EncryptedPrivateKeyInfo to PrivateKeyInfo decoding.
+ */
+struct epki2pki_ctx_st {
+ PROV_CTX *provctx;
+ char propq[OSSL_MAX_PROPQUERY_SIZE];
+};
+
+static void *epki2pki_newctx(void *provctx)
+{
+ struct epki2pki_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void epki2pki_freectx(void *vctx)
+{
+ struct epki2pki_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *epki2pki_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settables;
+}
+
+static int epki2pki_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct epki2pki_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str = ctx->propq;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES);
+ if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq)))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * The selection parameter in epki2pki_decode() is not used by this function
+ * because it's not relevant just to decode EncryptedPrivateKeyInfo to
+ * PrivateKeyInfo.
+ */
+static int epki2pki_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct epki2pki_ctx_st *ctx = vctx;
+ BUF_MEM *mem = NULL;
+ unsigned char *der = NULL;
+ long der_len = 0;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ int ok = 0;
+
+ if (in == NULL)
+ return 0;
+
+ ok = (asn1_d2i_read_bio(in, &mem) >= 0);
+ BIO_free(in);
+
+ /* We return "empty handed". This is not an error. */
+ if (!ok)
+ return 1;
+
+ der = (unsigned char *)mem->data;
+ der_len = (long)mem->length;
+ OPENSSL_free(mem);
+
+ ok = ossl_epki2pki_der_decode(der, der_len, selection, data_cb, data_cbarg,
+ pw_cb, pw_cbarg, PROV_LIBCTX_OF(ctx->provctx),
+ ctx->propq);
+ OPENSSL_free(der);
+ return ok;
+}
+
+int ossl_epki2pki_der_decode(unsigned char *der, long der_len, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+ OSSL_LIB_CTX *libctx, const char *propq)
+{
+ const unsigned char *pder = der;
+ unsigned char *new_der = NULL;
+ X509_SIG *p8 = NULL;
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+ const X509_ALGOR *alg = NULL;
+ int ok = 1; /* Assume good */
+
+ ERR_set_mark();
+ if ((p8 = d2i_X509_SIG(NULL, &pder, der_len)) != NULL) {
+ char pbuf[1024];
+ size_t plen = 0;
+
+ ERR_clear_last_mark();
+
+ if (!pw_cb(pbuf, sizeof(pbuf), &plen, NULL, pw_cbarg)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE);
+ ok = 0;
+ } else {
+ const ASN1_OCTET_STRING *oct;
+ int new_der_len = 0;
+
+ X509_SIG_get0(p8, &alg, &oct);
+ if (!PKCS12_pbe_crypt_ex(alg, pbuf, plen,
+ oct->data, oct->length,
+ &new_der, &new_der_len, 0,
+ libctx, propq)) {
+ ok = 0;
+ } else {
+ der = new_der;
+ der_len = new_der_len;
+ }
+ alg = NULL;
+ }
+ X509_SIG_free(p8);
+ } else {
+ ERR_pop_to_mark();
+ }
+
+ ERR_set_mark();
+ pder = der;
+ p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &pder, der_len);
+ ERR_pop_to_mark();
+
+ if (p8inf != NULL && PKCS8_pkey_get0(NULL, NULL, NULL, &alg, p8inf)) {
+ /*
+ * We have something and recognised it as PrivateKeyInfo, so let's
+ * pass all the applicable data to the callback.
+ */
+ char keytype[OSSL_MAX_NAME_SIZE];
+ OSSL_PARAM params[6], *p = params;
+ int objtype = OSSL_OBJECT_PKEY;
+
+ OBJ_obj2txt(keytype, sizeof(keytype), alg->algorithm, 0);
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ keytype, 0);
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE,
+ "DER", 0);
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+ "PrivateKeyInfo", 0);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+ der, der_len);
+ *p++ = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype);
+ *p = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ }
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ OPENSSL_free(new_der);
+ return ok;
+}
+
+const OSSL_DISPATCH ossl_EncryptedPrivateKeyInfo_der_to_der_decoder_functions[] = {
+ { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))epki2pki_newctx },
+ { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))epki2pki_freectx },
+ { OSSL_FUNC_DECODER_DECODE, (void (*)(void))epki2pki_decode },
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))epki2pki_settable_ctx_params },
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS,
+ (void (*)(void))epki2pki_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c
new file mode 100644
index 000000000000..df327210f114
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_msblob2key.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/pem.h> /* For public PVK functions */
+#include <openssl/x509.h>
+#include <openssl/err.h>
+#include "internal/passphrase.h"
+#include "crypto/pem.h" /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct msblob2key_ctx_st; /* Forward declaration */
+typedef void *b2i_of_void_fn(const unsigned char **in, unsigned int bitlen,
+ int ispub);
+typedef void adjust_key_fn(void *, struct msblob2key_ctx_st *ctx);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+ int type; /* EVP key type */
+ const char *name; /* Keytype */
+ const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+ b2i_of_void_fn *read_private_key;
+ b2i_of_void_fn *read_public_key;
+ adjust_key_fn *adjust_key;
+ free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn msblob2key_freectx;
+static OSSL_FUNC_decoder_decode_fn msblob2key_decode;
+static OSSL_FUNC_decoder_export_object_fn msblob2key_export_object;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct msblob2key_ctx_st {
+ PROV_CTX *provctx;
+ const struct keytype_desc_st *desc;
+ /* The selection that is passed to msblob2key_decode() */
+ int selection;
+};
+
+static struct msblob2key_ctx_st *
+msblob2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+ struct msblob2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->provctx = provctx;
+ ctx->desc = desc;
+ }
+ return ctx;
+}
+
+static void msblob2key_freectx(void *vctx)
+{
+ struct msblob2key_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static int msblob2key_does_selection(void *provctx, int selection)
+{
+ if (selection == 0)
+ return 1;
+
+ if ((selection & (OSSL_KEYMGMT_SELECT_PRIVATE_KEY
+ | OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) != 0)
+ return 1;
+
+ return 0;
+}
+
+static int msblob2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct msblob2key_ctx_st *ctx = vctx;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ const unsigned char *p;
+ unsigned char hdr_buf[16], *buf = NULL;
+ unsigned int bitlen, magic, length;
+ int isdss = -1;
+ int ispub = -1;
+ void *key = NULL;
+ int ok = 0;
+
+ if (in == NULL)
+ return 0;
+
+ if (BIO_read(in, hdr_buf, 16) != 16) {
+ ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+ goto next;
+ }
+ ERR_set_mark();
+ p = hdr_buf;
+ ok = ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) > 0;
+ ERR_pop_to_mark();
+ if (!ok)
+ goto next;
+
+ ctx->selection = selection;
+ ok = 0; /* Assume that we fail */
+
+ if ((isdss && ctx->desc->type != EVP_PKEY_DSA)
+ || (!isdss && ctx->desc->type != EVP_PKEY_RSA))
+ goto next;
+
+ length = ossl_blob_length(bitlen, isdss, ispub);
+ if (length > BLOB_MAX_LENGTH) {
+ ERR_raise(ERR_LIB_PEM, PEM_R_HEADER_TOO_LONG);
+ goto next;
+ }
+ buf = OPENSSL_malloc(length);
+ if (buf == NULL)
+ goto end;
+ p = buf;
+ if (BIO_read(in, buf, length) != (int)length) {
+ ERR_raise(ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT);
+ goto next;
+ }
+
+ if ((selection == 0
+ || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ && !ispub
+ && ctx->desc->read_private_key != NULL) {
+ struct ossl_passphrase_data_st pwdata;
+
+ memset(&pwdata, 0, sizeof(pwdata));
+ if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+ goto end;
+ p = buf;
+ key = ctx->desc->read_private_key(&p, bitlen, ispub);
+ if (selection != 0 && key == NULL)
+ goto next;
+ }
+ if (key == NULL && (selection == 0
+ || (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ && ispub
+ && ctx->desc->read_public_key != NULL) {
+ p = buf;
+ key = ctx->desc->read_public_key(&p, bitlen, ispub);
+ if (selection != 0 && key == NULL)
+ goto next;
+ }
+
+ if (key != NULL && ctx->desc->adjust_key != NULL)
+ ctx->desc->adjust_key(key, ctx);
+
+ next:
+ /*
+ * Indicated that we successfully decoded something, or not at all.
+ * Ending up "empty handed" is not an error.
+ */
+ ok = 1;
+
+ /*
+ * We free resources here so it's not held up during the callback, because
+ * we know the process is recursive and the allocated chunks of memory
+ * add up.
+ */
+ OPENSSL_free(buf);
+ BIO_free(in);
+ buf = NULL;
+ in = NULL;
+
+ if (key != NULL) {
+ OSSL_PARAM params[4];
+ int object_type = OSSL_OBJECT_PKEY;
+
+ params[0] =
+ OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+ params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ (char *)ctx->desc->name, 0);
+ /* The address of the key becomes the octet string */
+ params[2] =
+ OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+ &key, sizeof(key));
+ params[3] = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ }
+
+ end:
+ BIO_free(in);
+ OPENSSL_free(buf);
+ ctx->desc->free_key(key);
+
+ return ok;
+}
+
+static int
+msblob2key_export_object(void *vctx,
+ const void *reference, size_t reference_sz,
+ OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+ struct msblob2key_ctx_st *ctx = vctx;
+ OSSL_FUNC_keymgmt_export_fn *export =
+ ossl_prov_get_keymgmt_export(ctx->desc->fns);
+ void *keydata;
+
+ if (reference_sz == sizeof(keydata) && export != NULL) {
+ int selection = ctx->selection;
+
+ if (selection == 0)
+ selection = OSSL_KEYMGMT_SELECT_ALL;
+ /* The contents of the reference is the address to our object */
+ keydata = *(void **)reference;
+
+ return export(keydata, selection, export_cb, export_cbarg);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_decode_private_key (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_decode_public_key (b2i_of_void_fn *)ossl_b2i_DSA_after_header
+#define dsa_adjust NULL
+#define dsa_free (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_decode_private_key (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+#define rsa_decode_public_key (b2i_of_void_fn *)ossl_b2i_RSA_after_header
+
+static void rsa_adjust(void *key, struct msblob2key_ctx_st *ctx)
+{
+ ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MSBLOB(KEYTYPE, keytype) \
+ static const struct keytype_desc_st mstype##2##keytype##_desc = { \
+ EVP_PKEY_##KEYTYPE, #KEYTYPE, \
+ ossl_##keytype##_keymgmt_functions, \
+ keytype##_decode_private_key, \
+ keytype##_decode_public_key, \
+ keytype##_adjust, \
+ keytype##_free \
+ }; \
+ static OSSL_FUNC_decoder_newctx_fn msblob2##keytype##_newctx; \
+ static void *msblob2##keytype##_newctx(void *provctx) \
+ { \
+ return msblob2key_newctx(provctx, &mstype##2##keytype##_desc); \
+ } \
+ const OSSL_DISPATCH \
+ ossl_msblob_to_##keytype##_decoder_functions[] = { \
+ { OSSL_FUNC_DECODER_NEWCTX, \
+ (void (*)(void))msblob2##keytype##_newctx }, \
+ { OSSL_FUNC_DECODER_FREECTX, \
+ (void (*)(void))msblob2key_freectx }, \
+ { OSSL_FUNC_DECODER_DOES_SELECTION, \
+ (void (*)(void))msblob2key_does_selection }, \
+ { OSSL_FUNC_DECODER_DECODE, \
+ (void (*)(void))msblob2key_decode }, \
+ { OSSL_FUNC_DECODER_EXPORT_OBJECT, \
+ (void (*)(void))msblob2key_export_object }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MSBLOB(DSA, dsa);
+#endif
+IMPLEMENT_MSBLOB(RSA, rsa);
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c b/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c
new file mode 100644
index 000000000000..a38c71883dd1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_pem2der.c
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/pem.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "prov/bio.h"
+#include "prov/decoders.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+static int read_pem(PROV_CTX *provctx, OSSL_CORE_BIO *cin,
+ char **pem_name, char **pem_header,
+ unsigned char **data, long *len)
+{
+ BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
+ int ok;
+
+ if (in == NULL)
+ return 0;
+ ok = (PEM_read_bio(in, pem_name, pem_header, data, len) > 0);
+
+ BIO_free(in);
+ return ok;
+}
+
+static OSSL_FUNC_decoder_newctx_fn pem2der_newctx;
+static OSSL_FUNC_decoder_freectx_fn pem2der_freectx;
+static OSSL_FUNC_decoder_decode_fn pem2der_decode;
+
+/*
+ * Context used for PEM to DER decoding.
+ */
+struct pem2der_ctx_st {
+ PROV_CTX *provctx;
+ char data_structure[OSSL_MAX_CODEC_STRUCT_SIZE];
+ char propq[OSSL_MAX_PROPQUERY_SIZE];
+};
+
+static void *pem2der_newctx(void *provctx)
+{
+ struct pem2der_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void pem2der_freectx(void *vctx)
+{
+ struct pem2der_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *pem2der_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settables;
+}
+
+static int pem2der_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct pem2der_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES);
+ str = ctx->propq;
+ if (p != NULL
+ && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq)))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE);
+ str = ctx->data_structure;
+ if (p != NULL
+ && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->data_structure)))
+ return 0;
+
+ return 1;
+}
+
+/* pem_password_cb compatible function */
+struct pem2der_pass_data_st {
+ OSSL_PASSPHRASE_CALLBACK *cb;
+ void *cbarg;
+};
+
+static int pem2der_pass_helper(char *buf, int num, int w, void *data)
+{
+ struct pem2der_pass_data_st *pass_data = data;
+ size_t plen;
+
+ if (pass_data == NULL
+ || pass_data->cb == NULL
+ || !pass_data->cb(buf, num, &plen, NULL, pass_data->cbarg))
+ return -1;
+ return (int)plen;
+}
+
+static int pem2der_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ /*
+ * PEM names we recognise. Other PEM names should be recognised by
+ * other decoder implementations.
+ */
+ static struct pem_name_map_st {
+ const char *pem_name;
+ int object_type;
+ const char *data_type;
+ const char *data_structure;
+ } pem_name_map[] = {
+ /* PKCS#8 and SubjectPublicKeyInfo */
+ { PEM_STRING_PKCS8, OSSL_OBJECT_PKEY, NULL, "EncryptedPrivateKeyInfo" },
+ { PEM_STRING_PKCS8INF, OSSL_OBJECT_PKEY, NULL, "PrivateKeyInfo" },
+#define PKCS8_LAST_IDX 1
+ { PEM_STRING_PUBLIC, OSSL_OBJECT_PKEY, NULL, "SubjectPublicKeyInfo" },
+#define SPKI_LAST_IDX 2
+ /* Our set of type specific PEM types */
+ { PEM_STRING_DHPARAMS, OSSL_OBJECT_PKEY, "DH", "type-specific" },
+ { PEM_STRING_DHXPARAMS, OSSL_OBJECT_PKEY, "X9.42 DH", "type-specific" },
+ { PEM_STRING_DSA, OSSL_OBJECT_PKEY, "DSA", "type-specific" },
+ { PEM_STRING_DSA_PUBLIC, OSSL_OBJECT_PKEY, "DSA", "type-specific" },
+ { PEM_STRING_DSAPARAMS, OSSL_OBJECT_PKEY, "DSA", "type-specific" },
+ { PEM_STRING_ECPRIVATEKEY, OSSL_OBJECT_PKEY, "EC", "type-specific" },
+ { PEM_STRING_ECPARAMETERS, OSSL_OBJECT_PKEY, "EC", "type-specific" },
+ { PEM_STRING_SM2PRIVATEKEY, OSSL_OBJECT_PKEY, "SM2", "type-specific" },
+ { PEM_STRING_SM2PARAMETERS, OSSL_OBJECT_PKEY, "SM2", "type-specific" },
+ { PEM_STRING_RSA, OSSL_OBJECT_PKEY, "RSA", "type-specific" },
+ { PEM_STRING_RSA_PUBLIC, OSSL_OBJECT_PKEY, "RSA", "type-specific" },
+
+ /*
+ * A few others that there is at least have an object type for, even
+ * though there is no provider interface to handle such objects, yet.
+ * However, this is beneficial for the OSSL_STORE result handler.
+ */
+ { PEM_STRING_X509, OSSL_OBJECT_CERT, NULL, "Certificate" },
+ { PEM_STRING_X509_TRUSTED, OSSL_OBJECT_CERT, NULL, "Certificate" },
+ { PEM_STRING_X509_OLD, OSSL_OBJECT_CERT, NULL, "Certificate" },
+ { PEM_STRING_X509_CRL, OSSL_OBJECT_CRL, NULL, "CertificateList" }
+ };
+ struct pem2der_ctx_st *ctx = vctx;
+ char *pem_name = NULL, *pem_header = NULL;
+ size_t i;
+ unsigned char *der = NULL;
+ long der_len = 0;
+ int ok = 0;
+ int objtype = OSSL_OBJECT_UNKNOWN;
+
+ ok = read_pem(ctx->provctx, cin, &pem_name, &pem_header,
+ &der, &der_len) > 0;
+ /* We return "empty handed". This is not an error. */
+ if (!ok)
+ return 1;
+
+ /*
+ * 10 is the number of characters in "Proc-Type:", which
+ * PEM_get_EVP_CIPHER_INFO() requires to be present.
+ * If the PEM header has less characters than that, it's
+ * not worth spending cycles on it.
+ */
+ if (strlen(pem_header) > 10) {
+ EVP_CIPHER_INFO cipher;
+ struct pem2der_pass_data_st pass_data;
+
+ ok = 0; /* Assume that we fail */
+ pass_data.cb = pw_cb;
+ pass_data.cbarg = pw_cbarg;
+ if (!PEM_get_EVP_CIPHER_INFO(pem_header, &cipher)
+ || !PEM_do_header(&cipher, der, &der_len,
+ pem2der_pass_helper, &pass_data))
+ goto end;
+ }
+
+ /*
+ * Indicated that we successfully decoded something, or not at all.
+ * Ending up "empty handed" is not an error.
+ */
+ ok = 1;
+
+ /* Have a look to see if we recognise anything */
+ for (i = 0; i < OSSL_NELEM(pem_name_map); i++)
+ if (strcmp(pem_name, pem_name_map[i].pem_name) == 0)
+ break;
+
+ if (i < OSSL_NELEM(pem_name_map)) {
+ OSSL_PARAM params[5], *p = params;
+ /* We expect these to be read only so casting away the const is ok */
+ char *data_type = (char *)pem_name_map[i].data_type;
+ char *data_structure = (char *)pem_name_map[i].data_structure;
+
+ /*
+ * Since this may perform decryption, we need to check the selection to
+ * avoid password prompts for objects of no interest.
+ */
+ if (i <= PKCS8_LAST_IDX
+ && ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)
+ || OPENSSL_strcasecmp(ctx->data_structure, "EncryptedPrivateKeyInfo") == 0
+ || OPENSSL_strcasecmp(ctx->data_structure, "PrivateKeyInfo") == 0)) {
+ ok = ossl_epki2pki_der_decode(der, der_len, selection, data_cb,
+ data_cbarg, pw_cb, pw_cbarg,
+ PROV_LIBCTX_OF(ctx->provctx),
+ ctx->propq);
+ goto end;
+ }
+
+ if (i <= SPKI_LAST_IDX
+ && ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+ || OPENSSL_strcasecmp(ctx->data_structure, "SubjectPublicKeyInfo") == 0)) {
+ ok = ossl_spki2typespki_der_decode(der, der_len, selection, data_cb,
+ data_cbarg, pw_cb, pw_cbarg,
+ PROV_LIBCTX_OF(ctx->provctx),
+ ctx->propq);
+ goto end;
+ }
+
+ objtype = pem_name_map[i].object_type;
+ if (data_type != NULL)
+ *p++ =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ data_type, 0);
+
+ /* We expect this to be read only so casting away the const is ok */
+ if (data_structure != NULL)
+ *p++ =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+ data_structure, 0);
+ *p++ =
+ OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+ der, der_len);
+ *p++ =
+ OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype);
+
+ *p = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ }
+
+ end:
+ OPENSSL_free(pem_name);
+ OPENSSL_free(pem_header);
+ OPENSSL_free(der);
+ return ok;
+}
+
+const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[] = {
+ { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))pem2der_newctx },
+ { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))pem2der_freectx },
+ { OSSL_FUNC_DECODER_DECODE, (void (*)(void))pem2der_decode },
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))pem2der_settable_ctx_params },
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS,
+ (void (*)(void))pem2der_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c b/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c
new file mode 100644
index 000000000000..ea4585d93c5c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_pvk2key.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/pem.h> /* For public PVK functions */
+#include <openssl/x509.h>
+#include "internal/passphrase.h"
+#include "internal/sizes.h"
+#include "crypto/pem.h" /* For internal PVK and "blob" headers */
+#include "crypto/rsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+struct pvk2key_ctx_st; /* Forward declaration */
+typedef int check_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void adjust_key_fn(void *, struct pvk2key_ctx_st *ctx);
+typedef void *b2i_PVK_of_bio_pw_fn(BIO *in, pem_password_cb *cb, void *u,
+ OSSL_LIB_CTX *libctx, const char *propq);
+typedef void free_key_fn(void *);
+struct keytype_desc_st {
+ int type; /* EVP key type */
+ const char *name; /* Keytype */
+ const OSSL_DISPATCH *fns; /* Keymgmt (to pilfer functions from) */
+
+ b2i_PVK_of_bio_pw_fn *read_private_key;
+ adjust_key_fn *adjust_key;
+ free_key_fn *free_key;
+};
+
+static OSSL_FUNC_decoder_freectx_fn pvk2key_freectx;
+static OSSL_FUNC_decoder_decode_fn pvk2key_decode;
+static OSSL_FUNC_decoder_export_object_fn pvk2key_export_object;
+static OSSL_FUNC_decoder_settable_ctx_params_fn pvk2key_settable_ctx_params;
+static OSSL_FUNC_decoder_set_ctx_params_fn pvk2key_set_ctx_params;
+
+/*
+ * Context used for DER to key decoding.
+ */
+struct pvk2key_ctx_st {
+ PROV_CTX *provctx;
+ char propq[OSSL_MAX_PROPQUERY_SIZE];
+ const struct keytype_desc_st *desc;
+ /* The selection that is passed to der2key_decode() */
+ int selection;
+};
+
+static struct pvk2key_ctx_st *
+pvk2key_newctx(void *provctx, const struct keytype_desc_st *desc)
+{
+ struct pvk2key_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->provctx = provctx;
+ ctx->desc = desc;
+ }
+ return ctx;
+}
+
+static void pvk2key_freectx(void *vctx)
+{
+ struct pvk2key_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *pvk2key_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END,
+ };
+ return settables;
+}
+
+static int pvk2key_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct pvk2key_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str = ctx->propq;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES);
+ if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq)))
+ return 0;
+
+ return 1;
+}
+
+static int pvk2key_does_selection(void *provctx, int selection)
+{
+ if (selection == 0)
+ return 1;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ return 1;
+
+ return 0;
+}
+
+static int pvk2key_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct pvk2key_ctx_st *ctx = vctx;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ void *key = NULL;
+ int ok = 0;
+
+ if (in == NULL)
+ return 0;
+
+ ctx->selection = selection;
+
+ if ((selection == 0
+ || (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ && ctx->desc->read_private_key != NULL) {
+ struct ossl_passphrase_data_st pwdata;
+ int err, lib, reason;
+
+ memset(&pwdata, 0, sizeof(pwdata));
+ if (!ossl_pw_set_ossl_passphrase_cb(&pwdata, pw_cb, pw_cbarg))
+ goto end;
+
+ key = ctx->desc->read_private_key(in, ossl_pw_pvk_password, &pwdata,
+ PROV_LIBCTX_OF(ctx->provctx),
+ ctx->propq);
+
+ /*
+ * Because the PVK API doesn't have a separate decrypt call, we need
+ * to check the error queue for certain well known errors that are
+ * considered fatal and which we pass through, while the rest gets
+ * thrown away.
+ */
+ err = ERR_peek_last_error();
+ lib = ERR_GET_LIB(err);
+ reason = ERR_GET_REASON(err);
+ if (lib == ERR_LIB_PEM
+ && (reason == PEM_R_BAD_PASSWORD_READ
+ || reason == PEM_R_BAD_DECRYPT)) {
+ ERR_clear_last_mark();
+ goto end;
+ }
+
+ if (selection != 0 && key == NULL)
+ goto next;
+ }
+
+ if (key != NULL && ctx->desc->adjust_key != NULL)
+ ctx->desc->adjust_key(key, ctx);
+
+ next:
+ /*
+ * Indicated that we successfully decoded something, or not at all.
+ * Ending up "empty handed" is not an error.
+ */
+ ok = 1;
+
+ /*
+ * We free resources here so it's not held up during the callback, because
+ * we know the process is recursive and the allocated chunks of memory
+ * add up.
+ */
+ BIO_free(in);
+ in = NULL;
+
+ if (key != NULL) {
+ OSSL_PARAM params[4];
+ int object_type = OSSL_OBJECT_PKEY;
+
+ params[0] =
+ OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &object_type);
+ params[1] =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ (char *)ctx->desc->name, 0);
+ /* The address of the key becomes the octet string */
+ params[2] =
+ OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,
+ &key, sizeof(key));
+ params[3] = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ }
+
+ end:
+ BIO_free(in);
+ ctx->desc->free_key(key);
+
+ return ok;
+}
+
+static int pvk2key_export_object(void *vctx,
+ const void *reference, size_t reference_sz,
+ OSSL_CALLBACK *export_cb, void *export_cbarg)
+{
+ struct pvk2key_ctx_st *ctx = vctx;
+ OSSL_FUNC_keymgmt_export_fn *export =
+ ossl_prov_get_keymgmt_export(ctx->desc->fns);
+ void *keydata;
+
+ if (reference_sz == sizeof(keydata) && export != NULL) {
+ int selection = ctx->selection;
+
+ if (selection == 0)
+ selection = OSSL_KEYMGMT_SELECT_ALL;
+ /* The contents of the reference is the address to our object */
+ keydata = *(void **)reference;
+
+ return export(keydata, selection, export_cb, export_cbarg);
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define dsa_private_key_bio (b2i_PVK_of_bio_pw_fn *)b2i_DSA_PVK_bio_ex
+#define dsa_adjust NULL
+#define dsa_free (void (*)(void *))DSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define rsa_private_key_bio (b2i_PVK_of_bio_pw_fn *)b2i_RSA_PVK_bio_ex
+
+static void rsa_adjust(void *key, struct pvk2key_ctx_st *ctx)
+{
+ ossl_rsa_set0_libctx(key, PROV_LIBCTX_OF(ctx->provctx));
+}
+
+#define rsa_free (void (*)(void *))RSA_free
+
+/* ---------------------------------------------------------------------- */
+
+#define IMPLEMENT_MS(KEYTYPE, keytype) \
+ static const struct keytype_desc_st \
+ pvk2##keytype##_desc = { \
+ EVP_PKEY_##KEYTYPE, #KEYTYPE, \
+ ossl_##keytype##_keymgmt_functions, \
+ keytype##_private_key_bio, \
+ keytype##_adjust, \
+ keytype##_free \
+ }; \
+ static OSSL_FUNC_decoder_newctx_fn pvk2##keytype##_newctx; \
+ static void *pvk2##keytype##_newctx(void *provctx) \
+ { \
+ return pvk2key_newctx(provctx, &pvk2##keytype##_desc); \
+ } \
+ const OSSL_DISPATCH \
+ ossl_##pvk_to_##keytype##_decoder_functions[] = { \
+ { OSSL_FUNC_DECODER_NEWCTX, \
+ (void (*)(void))pvk2##keytype##_newctx }, \
+ { OSSL_FUNC_DECODER_FREECTX, \
+ (void (*)(void))pvk2key_freectx }, \
+ { OSSL_FUNC_DECODER_DOES_SELECTION, \
+ (void (*)(void))pvk2key_does_selection }, \
+ { OSSL_FUNC_DECODER_DECODE, \
+ (void (*)(void))pvk2key_decode }, \
+ { OSSL_FUNC_DECODER_EXPORT_OBJECT, \
+ (void (*)(void))pvk2key_export_object }, \
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))pvk2key_settable_ctx_params }, \
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \
+ (void (*)(void))pvk2key_set_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_DSA
+IMPLEMENT_MS(DSA, dsa);
+#endif
+IMPLEMENT_MS(RSA, rsa);
diff --git a/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c b/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c
new file mode 100644
index 000000000000..8cf6d7d41ce3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/decode_spki2typespki.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/asn1t.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/params.h>
+#include <openssl/x509.h>
+#include "internal/sizes.h"
+#include "crypto/x509.h"
+#include "crypto/ec.h"
+#include "prov/bio.h"
+#include "prov/decoders.h"
+#include "prov/implementations.h"
+#include "endecoder_local.h"
+
+static OSSL_FUNC_decoder_newctx_fn spki2typespki_newctx;
+static OSSL_FUNC_decoder_freectx_fn spki2typespki_freectx;
+static OSSL_FUNC_decoder_decode_fn spki2typespki_decode;
+static OSSL_FUNC_decoder_settable_ctx_params_fn spki2typespki_settable_ctx_params;
+static OSSL_FUNC_decoder_set_ctx_params_fn spki2typespki_set_ctx_params;
+
+/*
+ * Context used for SubjectPublicKeyInfo to Type specific SubjectPublicKeyInfo
+ * decoding.
+ */
+struct spki2typespki_ctx_st {
+ PROV_CTX *provctx;
+ char propq[OSSL_MAX_PROPQUERY_SIZE];
+};
+
+static void *spki2typespki_newctx(void *provctx)
+{
+ struct spki2typespki_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void spki2typespki_freectx(void *vctx)
+{
+ struct spki2typespki_ctx_st *ctx = vctx;
+
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *spki2typespki_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_DECODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settables;
+}
+
+static int spki2typespki_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct spki2typespki_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str = ctx->propq;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DECODER_PARAM_PROPERTIES);
+ if (p != NULL && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->propq)))
+ return 0;
+
+ return 1;
+}
+
+static int spki2typespki_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct spki2typespki_ctx_st *ctx = vctx;
+ unsigned char *der;
+ long len;
+ int ok = 0;
+
+ if (!ossl_read_der(ctx->provctx, cin, &der, &len))
+ return 1;
+
+ ok = ossl_spki2typespki_der_decode(der, len, selection, data_cb, data_cbarg,
+ pw_cb, pw_cbarg,
+ PROV_LIBCTX_OF(ctx->provctx), ctx->propq);
+ OPENSSL_free(der);
+ return ok;
+}
+
+int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+ OSSL_LIB_CTX *libctx, const char *propq)
+{
+ const unsigned char *derp = der;
+ X509_PUBKEY *xpub = NULL;
+ X509_ALGOR *algor = NULL;
+ const ASN1_OBJECT *oid = NULL;
+ char dataname[OSSL_MAX_NAME_SIZE];
+ OSSL_PARAM params[6], *p = params;
+ int objtype = OSSL_OBJECT_PKEY;
+ int ok = 0;
+
+ xpub = ossl_d2i_X509_PUBKEY_INTERNAL(&derp, len, libctx, propq);
+
+ if (xpub == NULL) {
+ /* We return "empty handed". This is not an error. */
+ ok = 1;
+ goto end;
+ }
+
+ if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &algor, xpub))
+ goto end;
+ X509_ALGOR_get0(&oid, NULL, NULL, algor);
+
+#ifndef OPENSSL_NO_EC
+ /* SM2 abuses the EC oid, so this could actually be SM2 */
+ if (OBJ_obj2nid(oid) == NID_X9_62_id_ecPublicKey
+ && ossl_x509_algor_is_sm2(algor))
+ strcpy(dataname, "SM2");
+ else
+#endif
+ if (OBJ_obj2txt(dataname, sizeof(dataname), oid, 0) <= 0)
+ goto end;
+
+ ossl_X509_PUBKEY_INTERNAL_free(xpub);
+ xpub = NULL;
+
+ *p++ =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ dataname, 0);
+
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE,
+ "DER", 0);
+
+ *p++ =
+ OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+ "SubjectPublicKeyInfo",
+ 0);
+ *p++ =
+ OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA, der, len);
+ *p++ =
+ OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype);
+
+ *p = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+
+ end:
+ ossl_X509_PUBKEY_INTERNAL_free(xpub);
+ return ok;
+}
+
+const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_der_decoder_functions[] = {
+ { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))spki2typespki_newctx },
+ { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))spki2typespki_freectx },
+ { OSSL_FUNC_DECODER_DECODE, (void (*)(void))spki2typespki_decode },
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS,
+ (void (*)(void))spki2typespki_settable_ctx_params },
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS,
+ (void (*)(void))spki2typespki_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c
new file mode 100644
index 000000000000..67a11590034a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2any.c
@@ -0,0 +1,1764 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Low level APIs are deprecated for public use, but still ok for internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/byteorder.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/params.h>
+#include <openssl/asn1.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/x509.h>
+#include <openssl/pkcs12.h> /* PKCS8_encrypt() */
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/proverr.h>
+#include "internal/passphrase.h"
+#include "internal/cryptlib.h"
+#include "crypto/ecx.h"
+#include "crypto/ml_kem.h"
+#include "crypto/rsa.h"
+#include "crypto/ml_dsa.h"
+#include "crypto/slh_dsa.h"
+#include "prov/implementations.h"
+#include "prov/bio.h"
+#include "prov/provider_ctx.h"
+#include "prov/der_rsa.h"
+#include "endecoder_local.h"
+#include "ml_dsa_codecs.h"
+#include "ml_kem_codecs.h"
+
+#if defined(OPENSSL_NO_DH) && defined(OPENSSL_NO_DSA) && defined(OPENSSL_NO_EC)
+# define OPENSSL_NO_KEYPARAMS
+#endif
+
+typedef struct key2any_ctx_st {
+ PROV_CTX *provctx;
+
+ /* Set to 0 if parameters should not be saved (dsa only) */
+ int save_parameters;
+
+ /* Set to 1 if intending to encrypt/decrypt, otherwise 0 */
+ int cipher_intent;
+
+ EVP_CIPHER *cipher;
+
+ struct ossl_passphrase_data_st pwdata;
+} KEY2ANY_CTX;
+
+typedef int check_key_type_fn(const void *key, int nid);
+typedef int key_to_paramstring_fn(const void *key, int nid, int save,
+ void **str, int *strtype);
+typedef int key_to_der_fn(BIO *out, const void *key,
+ int key_nid, const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d, KEY2ANY_CTX *ctx);
+typedef int write_bio_of_void_fn(BIO *bp, const void *x);
+
+
+/* Free the blob allocated during key_to_paramstring_fn */
+static void free_asn1_data(int type, void *data)
+{
+ switch (type) {
+ case V_ASN1_OBJECT:
+ ASN1_OBJECT_free(data);
+ break;
+ case V_ASN1_SEQUENCE:
+ ASN1_STRING_free(data);
+ break;
+ }
+}
+
+static PKCS8_PRIV_KEY_INFO *key_to_p8info(const void *key, int key_nid,
+ void *params, int params_type,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ /* der, derlen store the key DER output and its length */
+ unsigned char *der = NULL;
+ int derlen;
+ /* The final PKCS#8 info */
+ PKCS8_PRIV_KEY_INFO *p8info = NULL;
+
+ if ((p8info = PKCS8_PRIV_KEY_INFO_new()) == NULL
+ || (derlen = k2d(key, &der, (void *)ctx)) <= 0
+ || !PKCS8_pkey_set0(p8info, OBJ_nid2obj(key_nid), 0,
+ params_type, params, der, derlen)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ PKCS8_PRIV_KEY_INFO_free(p8info);
+ OPENSSL_free(der);
+ p8info = NULL;
+ }
+
+ return p8info;
+}
+
+static X509_SIG *p8info_to_encp8(PKCS8_PRIV_KEY_INFO *p8info,
+ KEY2ANY_CTX *ctx)
+{
+ X509_SIG *p8 = NULL;
+ char kstr[PEM_BUFSIZE];
+ size_t klen = 0;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ctx->cipher == NULL)
+ return NULL;
+
+ if (!ossl_pw_get_passphrase(kstr, sizeof(kstr), &klen, NULL, 1,
+ &ctx->pwdata)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PASSPHRASE);
+ return NULL;
+ }
+ /* First argument == -1 means "standard" */
+ p8 = PKCS8_encrypt_ex(-1, ctx->cipher, kstr, klen, NULL, 0, 0, p8info, libctx, NULL);
+ OPENSSL_cleanse(kstr, klen);
+ return p8;
+}
+
+static X509_SIG *key_to_encp8(const void *key, int key_nid,
+ void *params, int params_type,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ PKCS8_PRIV_KEY_INFO *p8info =
+ key_to_p8info(key, key_nid, params, params_type, k2d, ctx);
+ X509_SIG *p8 = NULL;
+
+ if (p8info == NULL) {
+ free_asn1_data(params_type, params);
+ } else {
+ p8 = p8info_to_encp8(p8info, ctx);
+ PKCS8_PRIV_KEY_INFO_free(p8info);
+ }
+ return p8;
+}
+
+static X509_PUBKEY *key_to_pubkey(const void *key, int key_nid,
+ void *params, int params_type,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ /* der, derlen store the key DER output and its length */
+ unsigned char *der = NULL;
+ int derlen;
+ /* The final X509_PUBKEY */
+ X509_PUBKEY *xpk = NULL;
+
+
+ if ((xpk = X509_PUBKEY_new()) == NULL
+ || (derlen = k2d(key, &der, (void *)ctx)) <= 0
+ || !X509_PUBKEY_set0_param(xpk, OBJ_nid2obj(key_nid),
+ params_type, params, der, derlen)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_X509_LIB);
+ X509_PUBKEY_free(xpk);
+ OPENSSL_free(der);
+ xpk = NULL;
+ }
+
+ return xpk;
+}
+
+/*
+ * key_to_epki_* produce encoded output with the private key data in a
+ * EncryptedPrivateKeyInfo structure (defined by PKCS#8). They require
+ * that there's an intent to encrypt, anything else is an error.
+ *
+ * key_to_pki_* primarily produce encoded output with the private key data
+ * in a PrivateKeyInfo structure (also defined by PKCS#8). However, if
+ * there is an intent to encrypt the data, the corresponding key_to_epki_*
+ * function is used instead.
+ *
+ * key_to_spki_* produce encoded output with the public key data in an
+ * X.509 SubjectPublicKeyInfo.
+ *
+ * Key parameters don't have any defined envelopment of this kind, but are
+ * included in some manner in the output from the functions described above,
+ * either in the AlgorithmIdentifier's parameter field, or as part of the
+ * key data itself.
+ */
+
+static int key_to_epki_der_priv_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ X509_SIG *p8;
+
+ if (!ctx->cipher_intent)
+ return 0;
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ p8 = key_to_encp8(key, key_nid, str, strtype, k2d, ctx);
+ if (p8 != NULL)
+ ret = i2d_PKCS8_bio(out, p8);
+
+ X509_SIG_free(p8);
+
+ return ret;
+}
+
+static int key_to_epki_pem_priv_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ X509_SIG *p8;
+
+ if (!ctx->cipher_intent)
+ return 0;
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ p8 = key_to_encp8(key, key_nid, str, strtype, k2d, ctx);
+ if (p8 != NULL)
+ ret = PEM_write_bio_PKCS8(out, p8);
+
+ X509_SIG_free(p8);
+
+ return ret;
+}
+
+static int key_to_pki_der_priv_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ PKCS8_PRIV_KEY_INFO *p8info;
+
+ if (ctx->cipher_intent)
+ return key_to_epki_der_priv_bio(out, key, key_nid, pemname,
+ p2s, k2d, ctx);
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ p8info = key_to_p8info(key, key_nid, str, strtype, k2d, ctx);
+
+ if (p8info != NULL)
+ ret = i2d_PKCS8_PRIV_KEY_INFO_bio(out, p8info);
+ else
+ free_asn1_data(strtype, str);
+
+ PKCS8_PRIV_KEY_INFO_free(p8info);
+
+ return ret;
+}
+
+static int key_to_pki_pem_priv_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ PKCS8_PRIV_KEY_INFO *p8info;
+
+ if (ctx->cipher_intent)
+ return key_to_epki_pem_priv_bio(out, key, key_nid, pemname,
+ p2s, k2d, ctx);
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ p8info = key_to_p8info(key, key_nid, str, strtype, k2d, ctx);
+
+ if (p8info != NULL)
+ ret = PEM_write_bio_PKCS8_PRIV_KEY_INFO(out, p8info);
+ else
+ free_asn1_data(strtype, str);
+
+ PKCS8_PRIV_KEY_INFO_free(p8info);
+
+ return ret;
+}
+
+static int key_to_spki_der_pub_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ X509_PUBKEY *xpk = NULL;
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ xpk = key_to_pubkey(key, key_nid, str, strtype, k2d, ctx);
+
+ if (xpk != NULL)
+ ret = i2d_X509_PUBKEY_bio(out, xpk);
+
+ /* Also frees |str| */
+ X509_PUBKEY_free(xpk);
+ return ret;
+}
+
+static int key_to_spki_pem_pub_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ int ret = 0;
+ void *str = NULL;
+ int strtype = V_ASN1_UNDEF;
+ X509_PUBKEY *xpk = NULL;
+
+ if (p2s != NULL && !p2s(key, key_nid, ctx->save_parameters,
+ &str, &strtype))
+ return 0;
+
+ xpk = key_to_pubkey(key, key_nid, str, strtype, k2d, ctx);
+
+ if (xpk != NULL)
+ ret = PEM_write_bio_X509_PUBKEY(out, xpk);
+ else
+ free_asn1_data(strtype, str);
+
+ /* Also frees |str| */
+ X509_PUBKEY_free(xpk);
+ return ret;
+}
+
+/*
+ * key_to_type_specific_* produce encoded output with type specific key data,
+ * no envelopment; the same kind of output as the type specific i2d_ and
+ * PEM_write_ functions, which is often a simple SEQUENCE of INTEGER.
+ *
+ * OpenSSL tries to discourage production of new keys in this form, because
+ * of the ambiguity when trying to recognise them, but can't deny that PKCS#1
+ * et al still are live standards.
+ *
+ * Note that these functions completely ignore p2s, and rather rely entirely
+ * on k2d to do the complete work.
+ */
+static int key_to_type_specific_der_bio(BIO *out, const void *key,
+ int key_nid,
+ ossl_unused const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ unsigned char *der = NULL;
+ int derlen;
+ int ret;
+
+ if ((derlen = k2d(key, &der, (void *)ctx)) <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);
+ return 0;
+ }
+
+ ret = BIO_write(out, der, derlen);
+ OPENSSL_free(der);
+ return ret > 0;
+}
+#define key_to_type_specific_der_priv_bio key_to_type_specific_der_bio
+#define key_to_type_specific_der_pub_bio key_to_type_specific_der_bio
+#define key_to_type_specific_der_param_bio key_to_type_specific_der_bio
+
+static int key_to_type_specific_pem_bio_cb(BIO *out, const void *key,
+ int key_nid, const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx,
+ pem_password_cb *cb, void *cbarg)
+{
+ return PEM_ASN1_write_bio_ctx(k2d, (void *)ctx, pemname, out, key,
+ ctx->cipher, NULL, 0, cb, cbarg) > 0;
+}
+
+static int key_to_type_specific_pem_priv_bio(BIO *out, const void *key,
+ int key_nid, const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+ p2s, k2d, ctx,
+ ossl_pw_pem_password, &ctx->pwdata);
+}
+
+static int key_to_type_specific_pem_pub_bio(BIO *out, const void *key,
+ int key_nid, const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+ p2s, k2d, ctx, NULL, NULL);
+}
+
+#ifndef OPENSSL_NO_KEYPARAMS
+static int key_to_type_specific_pem_param_bio(BIO *out, const void *key,
+ int key_nid, const char *pemname,
+ key_to_paramstring_fn *p2s,
+ OSSL_i2d_of_void_ctx *k2d,
+ KEY2ANY_CTX *ctx)
+{
+ return key_to_type_specific_pem_bio_cb(out, key, key_nid, pemname,
+ p2s, k2d, ctx, NULL, NULL);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define k2d_NOCTX(n, f) \
+ static int \
+ n##_k2d(const void *key, unsigned char **pder, \
+ ossl_unused void *ctx) \
+ { \
+ return f(key, pder); \
+ }
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DH
+static int prepare_dh_params(const void *dh, int nid, int save,
+ void **pstr, int *pstrtype)
+{
+ ASN1_STRING *params = ASN1_STRING_new();
+
+ if (params == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ if (nid == EVP_PKEY_DHX)
+ params->length = i2d_DHxparams(dh, &params->data);
+ else
+ params->length = i2d_DHparams(dh, &params->data);
+
+ if (params->length <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ ASN1_STRING_free(params);
+ return 0;
+ }
+ params->type = V_ASN1_SEQUENCE;
+
+ *pstr = params;
+ *pstrtype = V_ASN1_SEQUENCE;
+ return 1;
+}
+
+static int dh_spki_pub_to_der(const void *dh, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const BIGNUM *bn = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+ int ret;
+
+ if ((bn = DH_get0_pub_key(dh)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ if ((pub_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+ return 0;
+ }
+
+ ret = i2d_ASN1_INTEGER(pub_key, pder);
+
+ ASN1_STRING_clear_free(pub_key);
+ return ret;
+}
+
+static int dh_pki_priv_to_der(const void *dh, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const BIGNUM *bn = NULL;
+ ASN1_INTEGER *priv_key = NULL;
+ int ret;
+
+ if ((bn = DH_get0_priv_key(dh)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+ if ((priv_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+ return 0;
+ }
+
+ ret = i2d_ASN1_INTEGER(priv_key, pder);
+
+ ASN1_STRING_clear_free(priv_key);
+ return ret;
+}
+
+# define dh_epki_priv_to_der dh_pki_priv_to_der
+
+static int
+dh_type_specific_params_to_der(const void *dh, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ if (DH_test_flags(dh, DH_FLAG_TYPE_DHX))
+ return i2d_DHxparams(dh, pder);
+ return i2d_DHparams(dh, pder);
+}
+
+/*
+ * DH doesn't have i2d_DHPrivateKey or i2d_DHPublicKey, so we can't make
+ * corresponding functions here.
+ */
+# define dh_type_specific_priv_to_der NULL
+# define dh_type_specific_pub_to_der NULL
+
+static int dh_check_key_type(const void *dh, int expected_type)
+{
+ int type =
+ DH_test_flags(dh, DH_FLAG_TYPE_DHX) ? EVP_PKEY_DHX : EVP_PKEY_DH;
+
+ return type == expected_type;
+}
+
+# define dh_evp_type EVP_PKEY_DH
+# define dhx_evp_type EVP_PKEY_DHX
+# define dh_pem_type "DH"
+# define dhx_pem_type "X9.42 DH"
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DSA
+static int encode_dsa_params(const void *dsa, int nid,
+ void **pstr, int *pstrtype)
+{
+ ASN1_STRING *params = ASN1_STRING_new();
+
+ if (params == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ params->length = i2d_DSAparams(dsa, &params->data);
+
+ if (params->length <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ ASN1_STRING_free(params);
+ return 0;
+ }
+
+ *pstrtype = V_ASN1_SEQUENCE;
+ *pstr = params;
+ return 1;
+}
+
+static int prepare_dsa_params(const void *dsa, int nid, int save,
+ void **pstr, int *pstrtype)
+{
+ const BIGNUM *p = DSA_get0_p(dsa);
+ const BIGNUM *q = DSA_get0_q(dsa);
+ const BIGNUM *g = DSA_get0_g(dsa);
+
+ if (save && p != NULL && q != NULL && g != NULL)
+ return encode_dsa_params(dsa, nid, pstr, pstrtype);
+
+ *pstr = NULL;
+ *pstrtype = V_ASN1_UNDEF;
+ return 1;
+}
+
+static int dsa_spki_pub_to_der(const void *dsa, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const BIGNUM *bn = NULL;
+ ASN1_INTEGER *pub_key = NULL;
+ int ret;
+
+ if ((bn = DSA_get0_pub_key(dsa)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ if ((pub_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+ return 0;
+ }
+
+ ret = i2d_ASN1_INTEGER(pub_key, pder);
+
+ ASN1_STRING_clear_free(pub_key);
+ return ret;
+}
+
+static int dsa_pki_priv_to_der(const void *dsa, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const BIGNUM *bn = NULL;
+ ASN1_INTEGER *priv_key = NULL;
+ int ret;
+
+ if ((bn = DSA_get0_priv_key(dsa)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+ if ((priv_key = BN_to_ASN1_INTEGER(bn, NULL)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BN_ERROR);
+ return 0;
+ }
+
+ ret = i2d_ASN1_INTEGER(priv_key, pder);
+
+ ASN1_STRING_clear_free(priv_key);
+ return ret;
+}
+
+k2d_NOCTX(dsa_prv, i2d_DSAPrivateKey)
+k2d_NOCTX(dsa_pub, i2d_DSAPublicKey)
+k2d_NOCTX(dsa_param, i2d_DSAparams)
+
+# define dsa_epki_priv_to_der dsa_pki_priv_to_der
+
+# define dsa_type_specific_priv_to_der dsa_prv_k2d
+# define dsa_type_specific_pub_to_der dsa_pub_k2d
+# define dsa_type_specific_params_to_der dsa_param_k2d
+
+# define dsa_check_key_type NULL
+# define dsa_evp_type EVP_PKEY_DSA
+# define dsa_pem_type "DSA"
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_EC
+static int prepare_ec_explicit_params(const void *eckey,
+ void **pstr, int *pstrtype)
+{
+ ASN1_STRING *params = ASN1_STRING_new();
+
+ if (params == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ params->length = i2d_ECParameters(eckey, &params->data);
+ if (params->length <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ ASN1_STRING_free(params);
+ return 0;
+ }
+
+ *pstrtype = V_ASN1_SEQUENCE;
+ *pstr = params;
+ return 1;
+}
+
+/*
+ * This implements EcpkParameters, where the CHOICE is based on whether there
+ * is a curve name (curve nid) to be found or not. See RFC 3279 for details.
+ */
+static int prepare_ec_params(const void *eckey, int nid, int save,
+ void **pstr, int *pstrtype)
+{
+ int curve_nid;
+ const EC_GROUP *group = EC_KEY_get0_group(eckey);
+ ASN1_OBJECT *params = NULL;
+
+ if (group == NULL)
+ return 0;
+ curve_nid = EC_GROUP_get_curve_name(group);
+ if (curve_nid != NID_undef) {
+ params = OBJ_nid2obj(curve_nid);
+ if (params == NULL)
+ return 0;
+ }
+
+ if (curve_nid != NID_undef
+ && (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE)) {
+ /* The CHOICE came to namedCurve */
+ if (OBJ_length(params) == 0) {
+ /* Some curves might not have an associated OID */
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_OID);
+ ASN1_OBJECT_free(params);
+ return 0;
+ }
+ *pstr = params;
+ *pstrtype = V_ASN1_OBJECT;
+ return 1;
+ } else {
+ /* The CHOICE came to ecParameters */
+ return prepare_ec_explicit_params(eckey, pstr, pstrtype);
+ }
+}
+
+static int ec_spki_pub_to_der(const void *eckey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ if (EC_KEY_get0_public_key(eckey) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ return i2o_ECPublicKey(eckey, pder);
+}
+
+static int ec_pki_priv_to_der(const void *veckey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ EC_KEY *eckey = (EC_KEY *)veckey;
+ unsigned int old_flags;
+ int ret = 0;
+
+ /*
+ * For PKCS8 the curve name appears in the PKCS8_PRIV_KEY_INFO object
+ * as the pkeyalg->parameter field. (For a named curve this is an OID)
+ * The pkey field is an octet string that holds the encoded
+ * ECPrivateKey SEQUENCE with the optional parameters field omitted.
+ * We omit this by setting the EC_PKEY_NO_PARAMETERS flag.
+ */
+ old_flags = EC_KEY_get_enc_flags(eckey); /* save old flags */
+ EC_KEY_set_enc_flags(eckey, old_flags | EC_PKEY_NO_PARAMETERS);
+ ret = i2d_ECPrivateKey(eckey, pder);
+ EC_KEY_set_enc_flags(eckey, old_flags); /* restore old flags */
+ return ret; /* return the length of the der encoded data */
+}
+
+k2d_NOCTX(ec_param, i2d_ECParameters)
+k2d_NOCTX(ec_prv, i2d_ECPrivateKey)
+
+# define ec_epki_priv_to_der ec_pki_priv_to_der
+
+# define ec_type_specific_params_to_der ec_param_k2d
+/* No ec_type_specific_pub_to_der, there simply is no such thing */
+# define ec_type_specific_priv_to_der ec_prv_k2d
+
+# define ec_check_key_type NULL
+# define ec_evp_type EVP_PKEY_EC
+# define ec_pem_type "EC"
+
+# ifndef OPENSSL_NO_SM2
+/*
+ * Albeit SM2 is a slightly different algorithm than ECDSA, the key type
+ * encoding (in all places where an AlgorithmIdentifier is produced, such
+ * as PrivateKeyInfo and SubjectPublicKeyInfo) is the same as for ECC keys
+ * according to the example in GM/T 0015-2012, appendix D.2.
+ * This leaves the distinction of SM2 keys to the EC group (which is found
+ * in AlgorithmIdentified.params).
+ */
+# define sm2_evp_type ec_evp_type
+# define sm2_pem_type "SM2"
+# endif
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ECX
+# define prepare_ecx_params NULL
+
+static int ecx_spki_pub_to_der(const void *vecxkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const ECX_KEY *ecxkey = vecxkey;
+ unsigned char *keyblob;
+
+ if (ecxkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ keyblob = OPENSSL_memdup(ecxkey->pubkey, ecxkey->keylen);
+ if (keyblob == NULL)
+ return 0;
+
+ *pder = keyblob;
+ return ecxkey->keylen;
+}
+
+static int ecx_pki_priv_to_der(const void *vecxkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const ECX_KEY *ecxkey = vecxkey;
+ ASN1_OCTET_STRING oct;
+ int keybloblen;
+
+ if (ecxkey == NULL || ecxkey->privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ oct.data = ecxkey->privkey;
+ oct.length = ecxkey->keylen;
+ oct.flags = 0;
+
+ keybloblen = i2d_ASN1_OCTET_STRING(&oct, pder);
+ if (keybloblen < 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_ASN1_LIB);
+ return 0;
+ }
+
+ return keybloblen;
+}
+
+# define ecx_epki_priv_to_der ecx_pki_priv_to_der
+
+/*
+ * ED25519, ED448, X25519 and X448 only has PKCS#8 / SubjectPublicKeyInfo
+ * representation, so we don't define ecx_type_specific_[priv,pub,params]_to_der.
+ */
+
+# define ecx_check_key_type NULL
+
+# define ed25519_evp_type EVP_PKEY_ED25519
+# define ed448_evp_type EVP_PKEY_ED448
+# define x25519_evp_type EVP_PKEY_X25519
+# define x448_evp_type EVP_PKEY_X448
+# define ed25519_pem_type "ED25519"
+# define ed448_pem_type "ED448"
+# define x25519_pem_type "X25519"
+# define x448_pem_type "X448"
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_DSA
+static int ml_dsa_spki_pub_to_der(const void *vkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ return ossl_ml_dsa_i2d_pubkey(vkey, pder);
+}
+
+static int ml_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder,
+ void *vctx)
+{
+ KEY2ANY_CTX *ctx = vctx;
+
+ return ossl_ml_dsa_i2d_prvkey(vkey, pder, ctx->provctx);
+}
+
+# define ml_dsa_epki_priv_to_der ml_dsa_pki_priv_to_der
+# define prepare_ml_dsa_params NULL
+# define ml_dsa_check_key_type NULL
+
+# define ml_dsa_44_evp_type EVP_PKEY_ML_DSA_44
+# define ml_dsa_44_pem_type "ML-DSA-44"
+# define ml_dsa_65_evp_type EVP_PKEY_ML_DSA_65
+# define ml_dsa_65_pem_type "ML-DSA-65"
+# define ml_dsa_87_evp_type EVP_PKEY_ML_DSA_87
+# define ml_dsa_87_pem_type "ML-DSA-87"
+#endif /* OPENSSL_NO_ML_DSA */
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_KEM
+
+static int ml_kem_spki_pub_to_der(const void *vkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ return ossl_ml_kem_i2d_pubkey(vkey, pder);
+}
+
+static int ml_kem_pki_priv_to_der(const void *vkey, unsigned char **pder,
+ void *vctx)
+{
+ KEY2ANY_CTX *ctx = vctx;
+
+ return ossl_ml_kem_i2d_prvkey(vkey, pder, ctx->provctx);
+}
+
+# define ml_kem_epki_priv_to_der ml_kem_pki_priv_to_der
+# define prepare_ml_kem_params NULL
+# define ml_kem_check_key_type NULL
+
+# define ml_kem_512_evp_type EVP_PKEY_ML_KEM_512
+# define ml_kem_512_pem_type "ML-KEM-512"
+# define ml_kem_768_evp_type EVP_PKEY_ML_KEM_768
+# define ml_kem_768_pem_type "ML-KEM-768"
+# define ml_kem_1024_evp_type EVP_PKEY_ML_KEM_1024
+# define ml_kem_1024_pem_type "ML-KEM-1024"
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Helper functions to prepare RSA-PSS params for encoding. We would
+ * have simply written the whole AlgorithmIdentifier, but existing libcrypto
+ * functionality doesn't allow that.
+ */
+
+static int prepare_rsa_params(const void *rsa, int nid, int save,
+ void **pstr, int *pstrtype)
+{
+ const RSA_PSS_PARAMS_30 *pss = ossl_rsa_get0_pss_params_30((RSA *)rsa);
+
+ *pstr = NULL;
+
+ switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ /* If plain RSA, the parameters shall be NULL */
+ *pstrtype = V_ASN1_NULL;
+ return 1;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ if (ossl_rsa_pss_params_30_is_unrestricted(pss)) {
+ *pstrtype = V_ASN1_UNDEF;
+ return 1;
+ } else {
+ ASN1_STRING *astr = NULL;
+ WPACKET pkt;
+ unsigned char *str = NULL;
+ size_t str_sz = 0;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ switch (i) {
+ case 0:
+ if (!WPACKET_init_null_der(&pkt))
+ goto err;
+ break;
+ case 1:
+ if ((str = OPENSSL_malloc(str_sz)) == NULL
+ || !WPACKET_init_der(&pkt, str, str_sz)) {
+ WPACKET_cleanup(&pkt);
+ goto err;
+ }
+ break;
+ }
+ if (!ossl_DER_w_RSASSA_PSS_params(&pkt, -1, pss)
+ || !WPACKET_finish(&pkt)
+ || !WPACKET_get_total_written(&pkt, &str_sz)) {
+ WPACKET_cleanup(&pkt);
+ goto err;
+ }
+ WPACKET_cleanup(&pkt);
+
+ /*
+ * If no PSS parameters are going to be written, there's no
+ * point going for another iteration.
+ * This saves us from getting |str| allocated just to have it
+ * immediately de-allocated.
+ */
+ if (str_sz == 0)
+ break;
+ }
+
+ if ((astr = ASN1_STRING_new()) == NULL)
+ goto err;
+ *pstrtype = V_ASN1_SEQUENCE;
+ ASN1_STRING_set0(astr, str, (int)str_sz);
+ *pstr = astr;
+
+ return 1;
+ err:
+ OPENSSL_free(str);
+ return 0;
+ }
+ }
+
+ /* Currently unsupported RSA key type */
+ return 0;
+}
+
+k2d_NOCTX(rsa_prv, i2d_RSAPrivateKey)
+k2d_NOCTX(rsa_pub, i2d_RSAPublicKey)
+
+/*
+ * RSA is extremely simple, as PKCS#1 is used for the PKCS#8 |privateKey|
+ * field as well as the SubjectPublicKeyInfo |subjectPublicKey| field.
+ */
+#define rsa_pki_priv_to_der rsa_type_specific_priv_to_der
+#define rsa_epki_priv_to_der rsa_type_specific_priv_to_der
+#define rsa_spki_pub_to_der rsa_type_specific_pub_to_der
+#define rsa_type_specific_priv_to_der rsa_prv_k2d
+#define rsa_type_specific_pub_to_der rsa_pub_k2d
+#define rsa_type_specific_params_to_der NULL
+
+static int rsa_check_key_type(const void *rsa, int expected_type)
+{
+ switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ return expected_type == EVP_PKEY_RSA;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ return expected_type == EVP_PKEY_RSA_PSS;
+ }
+
+ /* Currently unsupported RSA key type */
+ return EVP_PKEY_NONE;
+}
+
+#define rsa_evp_type EVP_PKEY_RSA
+#define rsapss_evp_type EVP_PKEY_RSA_PSS
+#define rsa_pem_type "RSA"
+#define rsapss_pem_type "RSA-PSS"
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_SLH_DSA
+# define prepare_slh_dsa_params NULL
+
+static int slh_dsa_spki_pub_to_der(const void *vkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const SLH_DSA_KEY *key = vkey;
+ uint8_t *key_blob;
+ size_t key_len;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ key_len = ossl_slh_dsa_key_get_pub_len(key);
+ key_blob = OPENSSL_memdup(ossl_slh_dsa_key_get_pub(key), key_len);
+ if (key_blob == NULL)
+ return 0;
+
+ *pder = key_blob;
+ return key_len;
+}
+
+static int slh_dsa_pki_priv_to_der(const void *vkey, unsigned char **pder,
+ ossl_unused void *ctx)
+{
+ const SLH_DSA_KEY *key = vkey;
+ size_t len;
+
+ if (ossl_slh_dsa_key_get_priv(key) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ len = ossl_slh_dsa_key_get_priv_len(key);
+
+ if (pder != NULL
+ && ((*pder = OPENSSL_memdup(ossl_slh_dsa_key_get_priv(key), len)) == NULL))
+ return 0;
+
+ return len;
+}
+# define slh_dsa_epki_priv_to_der slh_dsa_pki_priv_to_der
+
+/* SLH_DSA only has PKCS#8 / SubjectPublicKeyInfo representations. */
+
+# define slh_dsa_check_key_type NULL
+# define slh_dsa_sha2_128s_evp_type EVP_PKEY_SLH_DSA_SHA2_128S
+# define slh_dsa_sha2_128f_evp_type EVP_PKEY_SLH_DSA_SHA2_128F
+# define slh_dsa_sha2_192s_evp_type EVP_PKEY_SLH_DSA_SHA2_192S
+# define slh_dsa_sha2_192f_evp_type EVP_PKEY_SLH_DSA_SHA2_192F
+# define slh_dsa_sha2_256s_evp_type EVP_PKEY_SLH_DSA_SHA2_256S
+# define slh_dsa_sha2_256f_evp_type EVP_PKEY_SLH_DSA_SHA2_256F
+# define slh_dsa_shake_128s_evp_type EVP_PKEY_SLH_DSA_SHAKE_128S
+# define slh_dsa_shake_128f_evp_type EVP_PKEY_SLH_DSA_SHAKE_128F
+# define slh_dsa_shake_192s_evp_type EVP_PKEY_SLH_DSA_SHAKE_192S
+# define slh_dsa_shake_192f_evp_type EVP_PKEY_SLH_DSA_SHAKE_192F
+# define slh_dsa_shake_256s_evp_type EVP_PKEY_SLH_DSA_SHAKE_256S
+# define slh_dsa_shake_256f_evp_type EVP_PKEY_SLH_DSA_SHAKE_256F
+# define slh_dsa_sha2_128s_input_type "SLH-DSA-SHA2-128s"
+# define slh_dsa_sha2_128f_input_type "SLH-DSA-SHA2-128f"
+# define slh_dsa_sha2_192s_input_type "SLH-DSA-SHA2-192s"
+# define slh_dsa_sha2_192f_input_type "SLH-DSA-SHA2-192f"
+# define slh_dsa_sha2_256s_input_type "SLH-DSA-SHA2-256s"
+# define slh_dsa_sha2_256f_input_type "SLH-DSA-SHA2-256f"
+# define slh_dsa_shake_128s_input_type "SLH-DSA-SHAKE-128s"
+# define slh_dsa_shake_128f_input_type "SLH-DSA-SHAKE-128f"
+# define slh_dsa_shake_192s_input_type "SLH-DSA-SHAKE-192s"
+# define slh_dsa_shake_192f_input_type "SLH-DSA-SHAKE-192f"
+# define slh_dsa_shake_256s_input_type "SLH-DSA-SHAKE-256s"
+# define slh_dsa_shake_256f_input_type "SLH-DSA-SHAKE-256f"
+# define slh_dsa_sha2_128s_pem_type "SLH-DSA-SHA2-128s"
+# define slh_dsa_sha2_128f_pem_type "SLH-DSA-SHA2-128f"
+# define slh_dsa_sha2_192s_pem_type "SLH-DSA-SHA2-192s"
+# define slh_dsa_sha2_192f_pem_type "SLH-DSA-SHA2-192f"
+# define slh_dsa_sha2_256s_pem_type "SLH-DSA-SHA2-256s"
+# define slh_dsa_sha2_256f_pem_type "SLH-DSA-SHA2-256f"
+# define slh_dsa_shake_128s_pem_type "SLH-DSA-SHAKE-128s"
+# define slh_dsa_shake_128f_pem_type "SLH-DSA-SHAKE-128f"
+# define slh_dsa_shake_192s_pem_type "SLH-DSA-SHAKE-192s"
+# define slh_dsa_shake_192f_pem_type "SLH-DSA-SHAKE-192f"
+# define slh_dsa_shake_256s_pem_type "SLH-DSA-SHAKE-256s"
+# define slh_dsa_shake_256f_pem_type "SLH-DSA-SHAKE-256f"
+#endif /* OPENSSL_NO_SLH_DSA */
+
+/* ---------------------------------------------------------------------- */
+
+static OSSL_FUNC_decoder_newctx_fn key2any_newctx;
+static OSSL_FUNC_decoder_freectx_fn key2any_freectx;
+
+static void *key2any_newctx(void *provctx)
+{
+ KEY2ANY_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->provctx = provctx;
+ ctx->save_parameters = 1;
+ }
+
+ return ctx;
+}
+
+static void key2any_freectx(void *vctx)
+{
+ KEY2ANY_CTX *ctx = vctx;
+
+ ossl_pw_clear_passphrase_data(&ctx->pwdata);
+ EVP_CIPHER_free(ctx->cipher);
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *key2any_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_ENCODER_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END,
+ };
+
+ return settables;
+}
+
+static int key2any_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ KEY2ANY_CTX *ctx = vctx;
+ OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);
+ const OSSL_PARAM *cipherp =
+ OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_CIPHER);
+ const OSSL_PARAM *propsp =
+ OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_PROPERTIES);
+ const OSSL_PARAM *save_paramsp =
+ OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_SAVE_PARAMETERS);
+
+ if (cipherp != NULL) {
+ const char *ciphername = NULL;
+ const char *props = NULL;
+
+ if (!OSSL_PARAM_get_utf8_string_ptr(cipherp, &ciphername))
+ return 0;
+ if (propsp != NULL && !OSSL_PARAM_get_utf8_string_ptr(propsp, &props))
+ return 0;
+
+ EVP_CIPHER_free(ctx->cipher);
+ ctx->cipher = NULL;
+ ctx->cipher_intent = ciphername != NULL;
+ if (ciphername != NULL
+ && ((ctx->cipher =
+ EVP_CIPHER_fetch(libctx, ciphername, props)) == NULL))
+ return 0;
+ }
+
+ if (save_paramsp != NULL) {
+ if (!OSSL_PARAM_get_int(save_paramsp, &ctx->save_parameters))
+ return 0;
+ }
+ return 1;
+}
+
+static int key2any_check_selection(int selection, int selection_mask)
+{
+ /*
+ * The selections are kinda sorta "levels", i.e. each selection given
+ * here is assumed to include those following.
+ */
+ int checks[] = {
+ OSSL_KEYMGMT_SELECT_PRIVATE_KEY,
+ OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+ OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+ };
+ size_t i;
+
+ /* The decoder implementations made here support guessing */
+ if (selection == 0)
+ return 1;
+
+ for (i = 0; i < OSSL_NELEM(checks); i++) {
+ int check1 = (selection & checks[i]) != 0;
+ int check2 = (selection_mask & checks[i]) != 0;
+
+ /*
+ * If the caller asked for the currently checked bit(s), return
+ * whether the decoder description says it's supported.
+ */
+ if (check1)
+ return check2;
+ }
+
+ /* This should be dead code, but just to be safe... */
+ return 0;
+}
+
+static int key2any_encode(KEY2ANY_CTX *ctx, OSSL_CORE_BIO *cout,
+ const void *key, int type, const char *pemname,
+ check_key_type_fn *checker,
+ key_to_der_fn *writer,
+ OSSL_PASSPHRASE_CALLBACK *pwcb, void *pwcbarg,
+ key_to_paramstring_fn *key2paramstring,
+ OSSL_i2d_of_void_ctx *key2der)
+{
+ int ret = 0;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ } else if (writer != NULL
+ && (checker == NULL || checker(key, type))) {
+ BIO *out = ossl_bio_new_from_core_bio(ctx->provctx, cout);
+
+ if (out != NULL
+ && (pwcb == NULL
+ || ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, pwcb, pwcbarg)))
+ ret =
+ writer(out, key, type, pemname, key2paramstring, key2der, ctx);
+
+ BIO_free(out);
+ } else {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ }
+ return ret;
+}
+
+#define DO_PRIVATE_KEY_selection_mask OSSL_KEYMGMT_SELECT_PRIVATE_KEY
+#define DO_PRIVATE_KEY(impl, type, kind, output) \
+ if ((selection & DO_PRIVATE_KEY_selection_mask) != 0) \
+ return key2any_encode(ctx, cout, key, impl##_evp_type, \
+ impl##_pem_type " PRIVATE KEY", \
+ type##_check_key_type, \
+ key_to_##kind##_##output##_priv_bio, \
+ cb, cbarg, prepare_##type##_params, \
+ type##_##kind##_priv_to_der);
+
+#define DO_PUBLIC_KEY_selection_mask OSSL_KEYMGMT_SELECT_PUBLIC_KEY
+#define DO_PUBLIC_KEY(impl, type, kind, output) \
+ if ((selection & DO_PUBLIC_KEY_selection_mask) != 0) \
+ return key2any_encode(ctx, cout, key, impl##_evp_type, \
+ impl##_pem_type " PUBLIC KEY", \
+ type##_check_key_type, \
+ key_to_##kind##_##output##_pub_bio, \
+ cb, cbarg, prepare_##type##_params, \
+ type##_##kind##_pub_to_der);
+
+#define DO_PARAMETERS_selection_mask OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+#define DO_PARAMETERS(impl, type, kind, output) \
+ if ((selection & DO_PARAMETERS_selection_mask) != 0) \
+ return key2any_encode(ctx, cout, key, impl##_evp_type, \
+ impl##_pem_type " PARAMETERS", \
+ type##_check_key_type, \
+ key_to_##kind##_##output##_param_bio, \
+ NULL, NULL, NULL, \
+ type##_##kind##_params_to_der);
+
+/*-
+ * Implement the kinds of output structure that can be produced. They are
+ * referred to by name, and for each name, the following macros are defined
+ * (braces not included):
+ *
+ * DO_{kind}_selection_mask
+ *
+ * A mask of selection bits that must not be zero. This is used as a
+ * selection criterion for each implementation.
+ * This mask must never be zero.
+ *
+ * DO_{kind}
+ *
+ * The performing macro. It must use the DO_ macros defined above,
+ * always in this order:
+ *
+ * - DO_PRIVATE_KEY
+ * - DO_PUBLIC_KEY
+ * - DO_PARAMETERS
+ *
+ * Any of those may be omitted, but the relative order must still be
+ * the same.
+ */
+
+/*
+ * PKCS#8 defines two structures for private keys only:
+ * - PrivateKeyInfo (raw unencrypted form)
+ * - EncryptedPrivateKeyInfo (encrypted wrapping)
+ *
+ * To allow a certain amount of flexibility, we allow the routines
+ * for PrivateKeyInfo to also produce EncryptedPrivateKeyInfo if a
+ * passphrase callback has been passed to them.
+ */
+#define DO_PrivateKeyInfo_selection_mask DO_PRIVATE_KEY_selection_mask
+#define DO_PrivateKeyInfo(impl, type, output) \
+ DO_PRIVATE_KEY(impl, type, pki, output)
+
+#define DO_EncryptedPrivateKeyInfo_selection_mask DO_PRIVATE_KEY_selection_mask
+#define DO_EncryptedPrivateKeyInfo(impl, type, output) \
+ DO_PRIVATE_KEY(impl, type, epki, output)
+
+/* SubjectPublicKeyInfo is a structure for public keys only */
+#define DO_SubjectPublicKeyInfo_selection_mask DO_PUBLIC_KEY_selection_mask
+#define DO_SubjectPublicKeyInfo(impl, type, output) \
+ DO_PUBLIC_KEY(impl, type, spki, output)
+
+/*
+ * "type-specific" is a uniform name for key type specific output for private
+ * and public keys as well as key parameters. This is used internally in
+ * libcrypto so it doesn't have to have special knowledge about select key
+ * types, but also when no better name has been found. If there are more
+ * expressive DO_ names above, those are preferred.
+ *
+ * Three forms exist:
+ *
+ * - type_specific_keypair Only supports private and public key
+ * - type_specific_params Only supports parameters
+ * - type_specific Supports all parts of an EVP_PKEY
+ * - type_specific_no_pub Supports all parts of an EVP_PKEY
+ * except public key
+ */
+#define DO_type_specific_params_selection_mask DO_PARAMETERS_selection_mask
+#define DO_type_specific_params(impl, type, output) \
+ DO_PARAMETERS(impl, type, type_specific, output)
+#define DO_type_specific_keypair_selection_mask \
+ ( DO_PRIVATE_KEY_selection_mask | DO_PUBLIC_KEY_selection_mask )
+#define DO_type_specific_keypair(impl, type, output) \
+ DO_PRIVATE_KEY(impl, type, type_specific, output) \
+ DO_PUBLIC_KEY(impl, type, type_specific, output)
+#define DO_type_specific_selection_mask \
+ ( DO_type_specific_keypair_selection_mask \
+ | DO_type_specific_params_selection_mask )
+#define DO_type_specific(impl, type, output) \
+ DO_type_specific_keypair(impl, type, output) \
+ DO_type_specific_params(impl, type, output)
+#define DO_type_specific_no_pub_selection_mask \
+ ( DO_PRIVATE_KEY_selection_mask | DO_PARAMETERS_selection_mask)
+#define DO_type_specific_no_pub(impl, type, output) \
+ DO_PRIVATE_KEY(impl, type, type_specific, output) \
+ DO_type_specific_params(impl, type, output)
+
+/*
+ * Type specific aliases for the cases where we need to refer to them by
+ * type name.
+ * This only covers key types that are represented with i2d_{TYPE}PrivateKey,
+ * i2d_{TYPE}PublicKey and i2d_{TYPE}params / i2d_{TYPE}Parameters.
+ */
+#define DO_RSA_selection_mask DO_type_specific_keypair_selection_mask
+#define DO_RSA(impl, type, output) DO_type_specific_keypair(impl, type, output)
+
+#define DO_DH_selection_mask DO_type_specific_params_selection_mask
+#define DO_DH(impl, type, output) DO_type_specific_params(impl, type, output)
+
+#define DO_DHX_selection_mask DO_type_specific_params_selection_mask
+#define DO_DHX(impl, type, output) DO_type_specific_params(impl, type, output)
+
+#define DO_DSA_selection_mask DO_type_specific_selection_mask
+#define DO_DSA(impl, type, output) DO_type_specific(impl, type, output)
+
+#define DO_EC_selection_mask DO_type_specific_no_pub_selection_mask
+#define DO_EC(impl, type, output) DO_type_specific_no_pub(impl, type, output)
+
+#define DO_SM2_selection_mask DO_type_specific_no_pub_selection_mask
+#define DO_SM2(impl, type, output) DO_type_specific_no_pub(impl, type, output)
+
+/* PKCS#1 defines a structure for RSA private and public keys */
+#define DO_PKCS1_selection_mask DO_RSA_selection_mask
+#define DO_PKCS1(impl, type, output) DO_RSA(impl, type, output)
+
+/* PKCS#3 defines a structure for DH parameters */
+#define DO_PKCS3_selection_mask DO_DH_selection_mask
+#define DO_PKCS3(impl, type, output) DO_DH(impl, type, output)
+/* X9.42 defines a structure for DHx parameters */
+#define DO_X9_42_selection_mask DO_DHX_selection_mask
+#define DO_X9_42(impl, type, output) DO_DHX(impl, type, output)
+
+/* X9.62 defines a structure for EC keys and parameters */
+#define DO_X9_62_selection_mask DO_EC_selection_mask
+#define DO_X9_62(impl, type, output) DO_EC(impl, type, output)
+
+/*
+ * MAKE_ENCODER is the single driver for creating OSSL_DISPATCH tables.
+ * It takes the following arguments:
+ *
+ * impl This is the key type name that's being implemented.
+ * type This is the type name for the set of functions that implement
+ * the key type. For example, ed25519, ed448, x25519 and x448
+ * are all implemented with the exact same set of functions.
+ * kind What kind of support to implement. These translate into
+ * the DO_##kind macros above.
+ * output The output type to implement. may be der or pem.
+ *
+ * The resulting OSSL_DISPATCH array gets the following name (expressed in
+ * C preprocessor terms) from those arguments:
+ *
+ * ossl_##impl##_to_##kind##_##output##_encoder_functions
+ */
+#define MAKE_ENCODER(impl, type, kind, output) \
+ static OSSL_FUNC_encoder_import_object_fn \
+ impl##_to_##kind##_##output##_import_object; \
+ static OSSL_FUNC_encoder_free_object_fn \
+ impl##_to_##kind##_##output##_free_object; \
+ static OSSL_FUNC_encoder_encode_fn \
+ impl##_to_##kind##_##output##_encode; \
+ \
+ static void * \
+ impl##_to_##kind##_##output##_import_object(void *vctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ KEY2ANY_CTX *ctx = vctx; \
+ \
+ return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \
+ ctx->provctx, selection, params); \
+ } \
+ static void impl##_to_##kind##_##output##_free_object(void *key) \
+ { \
+ ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \
+ } \
+ static int impl##_to_##kind##_##output##_does_selection(void *ctx, \
+ int selection) \
+ { \
+ return key2any_check_selection(selection, \
+ DO_##kind##_selection_mask); \
+ } \
+ static int \
+ impl##_to_##kind##_##output##_encode(void *ctx, OSSL_CORE_BIO *cout, \
+ const void *key, \
+ const OSSL_PARAM key_abstract[], \
+ int selection, \
+ OSSL_PASSPHRASE_CALLBACK *cb, \
+ void *cbarg) \
+ { \
+ /* We don't deal with abstract objects */ \
+ if (key_abstract != NULL) { \
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \
+ return 0; \
+ } \
+ DO_##kind(impl, type, output) \
+ \
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \
+ return 0; \
+ } \
+ const OSSL_DISPATCH \
+ ossl_##impl##_to_##kind##_##output##_encoder_functions[] = { \
+ { OSSL_FUNC_ENCODER_NEWCTX, \
+ (void (*)(void))key2any_newctx }, \
+ { OSSL_FUNC_ENCODER_FREECTX, \
+ (void (*)(void))key2any_freectx }, \
+ { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))key2any_settable_ctx_params }, \
+ { OSSL_FUNC_ENCODER_SET_CTX_PARAMS, \
+ (void (*)(void))key2any_set_ctx_params }, \
+ { OSSL_FUNC_ENCODER_DOES_SELECTION, \
+ (void (*)(void))impl##_to_##kind##_##output##_does_selection }, \
+ { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \
+ (void (*)(void))impl##_to_##kind##_##output##_import_object }, \
+ { OSSL_FUNC_ENCODER_FREE_OBJECT, \
+ (void (*)(void))impl##_to_##kind##_##output##_free_object }, \
+ { OSSL_FUNC_ENCODER_ENCODE, \
+ (void (*)(void))impl##_to_##kind##_##output##_encode }, \
+ OSSL_DISPATCH_END \
+ }
+
+/*
+ * Replacements for i2d_{TYPE}PrivateKey, i2d_{TYPE}PublicKey,
+ * i2d_{TYPE}params, as they exist.
+ */
+MAKE_ENCODER(rsa, rsa, type_specific_keypair, der);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, type_specific_params, der);
+MAKE_ENCODER(dhx, dh, type_specific_params, der);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, type_specific, der);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, type_specific_no_pub, der);
+# ifndef OPENSSL_NO_SM2
+MAKE_ENCODER(sm2, ec, type_specific_no_pub, der);
+# endif
+#endif
+
+/*
+ * Replacements for PEM_write_bio_{TYPE}PrivateKey,
+ * PEM_write_bio_{TYPE}PublicKey, PEM_write_bio_{TYPE}params, as they exist.
+ */
+MAKE_ENCODER(rsa, rsa, type_specific_keypair, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, type_specific_params, pem);
+MAKE_ENCODER(dhx, dh, type_specific_params, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, type_specific, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, type_specific_no_pub, pem);
+# ifndef OPENSSL_NO_SM2
+MAKE_ENCODER(sm2, ec, type_specific_no_pub, pem);
+# endif
+#endif
+
+/*
+ * PKCS#8 and SubjectPublicKeyInfo support. This may duplicate some of the
+ * implementations specified above, but are more specific.
+ * The SubjectPublicKeyInfo implementations also replace the
+ * PEM_write_bio_{TYPE}_PUBKEY functions.
+ * For PEM, these are expected to be used by PEM_write_bio_PrivateKey(),
+ * PEM_write_bio_PUBKEY() and PEM_write_bio_Parameters().
+ */
+MAKE_ENCODER(rsa, rsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(rsa, rsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(rsa, rsa, PrivateKeyInfo, der);
+MAKE_ENCODER(rsa, rsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(rsa, rsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(rsa, rsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(rsapss, rsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(rsapss, rsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(rsapss, rsa, PrivateKeyInfo, der);
+MAKE_ENCODER(rsapss, rsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(rsapss, rsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(rsapss, rsa, SubjectPublicKeyInfo, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(dh, dh, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(dh, dh, PrivateKeyInfo, der);
+MAKE_ENCODER(dh, dh, PrivateKeyInfo, pem);
+MAKE_ENCODER(dh, dh, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dh, dh, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(dhx, dh, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(dhx, dh, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(dhx, dh, PrivateKeyInfo, der);
+MAKE_ENCODER(dhx, dh, PrivateKeyInfo, pem);
+MAKE_ENCODER(dhx, dh, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dhx, dh, SubjectPublicKeyInfo, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(dsa, dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(dsa, dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(dsa, dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(dsa, dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(dsa, dsa, SubjectPublicKeyInfo, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ec, ec, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ec, ec, PrivateKeyInfo, der);
+MAKE_ENCODER(ec, ec, PrivateKeyInfo, pem);
+MAKE_ENCODER(ec, ec, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ec, ec, SubjectPublicKeyInfo, pem);
+# ifndef OPENSSL_NO_SM2
+MAKE_ENCODER(sm2, ec, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(sm2, ec, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(sm2, ec, PrivateKeyInfo, der);
+MAKE_ENCODER(sm2, ec, PrivateKeyInfo, pem);
+MAKE_ENCODER(sm2, ec, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(sm2, ec, SubjectPublicKeyInfo, pem);
+# endif
+# ifndef OPENSSL_NO_ECX
+MAKE_ENCODER(ed25519, ecx, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ed25519, ecx, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ed25519, ecx, PrivateKeyInfo, der);
+MAKE_ENCODER(ed25519, ecx, PrivateKeyInfo, pem);
+MAKE_ENCODER(ed25519, ecx, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ed25519, ecx, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(ed448, ecx, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ed448, ecx, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ed448, ecx, PrivateKeyInfo, der);
+MAKE_ENCODER(ed448, ecx, PrivateKeyInfo, pem);
+MAKE_ENCODER(ed448, ecx, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ed448, ecx, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(x25519, ecx, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(x25519, ecx, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(x25519, ecx, PrivateKeyInfo, der);
+MAKE_ENCODER(x25519, ecx, PrivateKeyInfo, pem);
+MAKE_ENCODER(x25519, ecx, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(x25519, ecx, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(x448, ecx, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(x448, ecx, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(x448, ecx, PrivateKeyInfo, der);
+MAKE_ENCODER(x448, ecx, PrivateKeyInfo, pem);
+MAKE_ENCODER(x448, ecx, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(x448, ecx, SubjectPublicKeyInfo, pem);
+# endif
+#endif
+#ifndef OPENSSL_NO_SLH_DSA
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_sha2_128s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_128f, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_192f, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_sha2_256f, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(slh_dsa_shake_128s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_128f, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_192f, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256s, slh_dsa, SubjectPublicKeyInfo, pem);
+MAKE_ENCODER(slh_dsa_shake_256f, slh_dsa, SubjectPublicKeyInfo, pem);
+#endif /* OPENSSL_NO_SLH_DSA */
+
+#ifndef OPENSSL_NO_ML_KEM
+MAKE_ENCODER(ml_kem_512, ml_kem, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_512, ml_kem, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_512, ml_kem, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_512, ml_kem, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_512, ml_kem, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_kem_512, ml_kem, SubjectPublicKeyInfo, pem);
+
+MAKE_ENCODER(ml_kem_768, ml_kem, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_768, ml_kem, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_768, ml_kem, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_768, ml_kem, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_768, ml_kem, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_kem_768, ml_kem, SubjectPublicKeyInfo, pem);
+
+MAKE_ENCODER(ml_kem_1024, ml_kem, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_1024, ml_kem, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_1024, ml_kem, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_kem_1024, ml_kem, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_kem_1024, ml_kem, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_kem_1024, ml_kem, SubjectPublicKeyInfo, pem);
+#endif
+
+/*
+ * Support for key type specific output formats. Not all key types have
+ * this, we only aim to duplicate what is available in 1.1.1 as
+ * i2d_TYPEPrivateKey(), i2d_TYPEPublicKey() and i2d_TYPEparams().
+ * For example, there are no publicly available i2d_ function for
+ * ED25519, ED448, X25519 or X448, and they therefore only have PKCS#8
+ * and SubjectPublicKeyInfo implementations as implemented above.
+ */
+MAKE_ENCODER(rsa, rsa, RSA, der);
+MAKE_ENCODER(rsa, rsa, RSA, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, DH, der);
+MAKE_ENCODER(dh, dh, DH, pem);
+MAKE_ENCODER(dhx, dh, DHX, der);
+MAKE_ENCODER(dhx, dh, DHX, pem);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_ENCODER(dsa, dsa, DSA, der);
+MAKE_ENCODER(dsa, dsa, DSA, pem);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, EC, der);
+MAKE_ENCODER(ec, ec, EC, pem);
+# ifndef OPENSSL_NO_SM2
+MAKE_ENCODER(sm2, ec, SM2, der);
+MAKE_ENCODER(sm2, ec, SM2, pem);
+# endif
+#endif
+
+/* Convenience structure names */
+MAKE_ENCODER(rsa, rsa, PKCS1, der);
+MAKE_ENCODER(rsa, rsa, PKCS1, pem);
+MAKE_ENCODER(rsapss, rsa, PKCS1, der);
+MAKE_ENCODER(rsapss, rsa, PKCS1, pem);
+#ifndef OPENSSL_NO_DH
+MAKE_ENCODER(dh, dh, PKCS3, der); /* parameters only */
+MAKE_ENCODER(dh, dh, PKCS3, pem); /* parameters only */
+MAKE_ENCODER(dhx, dh, X9_42, der); /* parameters only */
+MAKE_ENCODER(dhx, dh, X9_42, pem); /* parameters only */
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_ENCODER(ec, ec, X9_62, der);
+MAKE_ENCODER(ec, ec, X9_62, pem);
+#endif
+
+#ifndef OPENSSL_NO_ML_DSA
+MAKE_ENCODER(ml_dsa_44, ml_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_44, ml_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_44, ml_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_44, ml_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_44, ml_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_dsa_44, ml_dsa, SubjectPublicKeyInfo, pem);
+
+MAKE_ENCODER(ml_dsa_65, ml_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_65, ml_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_65, ml_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_65, ml_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_65, ml_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_dsa_65, ml_dsa, SubjectPublicKeyInfo, pem);
+
+MAKE_ENCODER(ml_dsa_87, ml_dsa, EncryptedPrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_87, ml_dsa, EncryptedPrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_87, ml_dsa, PrivateKeyInfo, der);
+MAKE_ENCODER(ml_dsa_87, ml_dsa, PrivateKeyInfo, pem);
+MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, der);
+MAKE_ENCODER(ml_dsa_87, ml_dsa, SubjectPublicKeyInfo, pem);
+#endif /* OPENSSL_NO_ML_DSA */
diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c
new file mode 100644
index 000000000000..29e72faa63de
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2blob.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2021-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Low level APIs are deprecated for public use, but still ok for internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/ec.h>
+#include "internal/passphrase.h"
+#include "internal/nelem.h"
+#include "prov/implementations.h"
+#include "prov/bio.h"
+#include "prov/provider_ctx.h"
+#include "endecoder_local.h"
+
+static int write_blob(void *provctx, OSSL_CORE_BIO *cout,
+ void *data, int len)
+{
+ BIO *out = ossl_bio_new_from_core_bio(provctx, cout);
+ int ret;
+
+ if (out == NULL)
+ return 0;
+ ret = BIO_write(out, data, len);
+
+ BIO_free(out);
+ return ret;
+}
+
+static OSSL_FUNC_encoder_newctx_fn key2blob_newctx;
+static OSSL_FUNC_encoder_freectx_fn key2blob_freectx;
+
+static void *key2blob_newctx(void *provctx)
+{
+ return provctx;
+}
+
+static void key2blob_freectx(void *vctx)
+{
+}
+
+static int key2blob_check_selection(int selection, int selection_mask)
+{
+ /*
+ * The selections are kinda sorta "levels", i.e. each selection given
+ * here is assumed to include those following.
+ */
+ int checks[] = {
+ OSSL_KEYMGMT_SELECT_PRIVATE_KEY,
+ OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
+ OSSL_KEYMGMT_SELECT_ALL_PARAMETERS
+ };
+ size_t i;
+
+ /* The decoder implementations made here support guessing */
+ if (selection == 0)
+ return 1;
+
+ for (i = 0; i < OSSL_NELEM(checks); i++) {
+ int check1 = (selection & checks[i]) != 0;
+ int check2 = (selection_mask & checks[i]) != 0;
+
+ /*
+ * If the caller asked for the currently checked bit(s), return
+ * whether the decoder description says it's supported.
+ */
+ if (check1)
+ return check2;
+ }
+
+ /* This should be dead code, but just to be safe... */
+ return 0;
+}
+
+static int key2blob_encode(void *vctx, const void *key, int selection,
+ OSSL_CORE_BIO *cout)
+{
+ int pubkey_len = 0, ok = 0;
+ unsigned char *pubkey = NULL;
+
+ pubkey_len = i2o_ECPublicKey(key, &pubkey);
+ if (pubkey_len > 0 && pubkey != NULL)
+ ok = write_blob(vctx, cout, pubkey, pubkey_len);
+ OPENSSL_free(pubkey);
+ return ok;
+}
+
+/*
+ * MAKE_BLOB_ENCODER() Makes an OSSL_DISPATCH table for a particular key->blob
+ * encoder
+ *
+ * impl: The keytype to encode
+ * type: The C structure type holding the key data
+ * selection_name: The acceptable selections. This translates into
+ * the macro EVP_PKEY_##selection_name.
+ *
+ * The selection is understood as a "level" rather than an exact set of
+ * requests from the caller. The encoder has to decide what contents fit
+ * the encoded format. For example, the EC public key blob will only contain
+ * the encoded public key itself, no matter if the selection bits include
+ * OSSL_KEYMGMT_SELECT_PARAMETERS or not. However, if the selection includes
+ * OSSL_KEYMGMT_SELECT_PRIVATE_KEY, the same encoder will simply refuse to
+ * cooperate, because it cannot output the private key.
+ *
+ * EVP_PKEY_##selection_name are convenience macros that combine "typical"
+ * OSSL_KEYMGMT_SELECT_ macros for a certain type of EVP_PKEY content.
+ */
+#define MAKE_BLOB_ENCODER(impl, type, selection_name) \
+ static OSSL_FUNC_encoder_import_object_fn \
+ impl##2blob_import_object; \
+ static OSSL_FUNC_encoder_free_object_fn impl##2blob_free_object; \
+ static OSSL_FUNC_encoder_does_selection_fn \
+ impl##2blob_does_selection; \
+ static OSSL_FUNC_encoder_encode_fn impl##2blob_encode; \
+ \
+ static void *impl##2blob_import_object(void *ctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \
+ ctx, selection, params); \
+ } \
+ static void impl##2blob_free_object(void *key) \
+ { \
+ ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \
+ } \
+ static int impl##2blob_does_selection(void *ctx, int selection) \
+ { \
+ return key2blob_check_selection(selection, \
+ EVP_PKEY_##selection_name); \
+ } \
+ static int impl##2blob_encode(void *vctx, OSSL_CORE_BIO *cout, \
+ const void *key, \
+ const OSSL_PARAM key_abstract[], \
+ int selection, \
+ OSSL_PASSPHRASE_CALLBACK *cb, \
+ void *cbarg) \
+ { \
+ /* We don't deal with abstract objects */ \
+ if (key_abstract != NULL) { \
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \
+ return 0; \
+ } \
+ return key2blob_encode(vctx, key, selection, cout); \
+ } \
+ const OSSL_DISPATCH ossl_##impl##_to_blob_encoder_functions[] = { \
+ { OSSL_FUNC_ENCODER_NEWCTX, \
+ (void (*)(void))key2blob_newctx }, \
+ { OSSL_FUNC_ENCODER_FREECTX, \
+ (void (*)(void))key2blob_freectx }, \
+ { OSSL_FUNC_ENCODER_DOES_SELECTION, \
+ (void (*)(void))impl##2blob_does_selection }, \
+ { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \
+ (void (*)(void))impl##2blob_import_object }, \
+ { OSSL_FUNC_ENCODER_FREE_OBJECT, \
+ (void (*)(void))impl##2blob_free_object }, \
+ { OSSL_FUNC_ENCODER_ENCODE, \
+ (void (*)(void))impl##2blob_encode }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_EC
+MAKE_BLOB_ENCODER(ec, ec, PUBLIC_KEY);
+# ifndef OPENSSL_NO_SM2
+MAKE_BLOB_ENCODER(sm2, ec, PUBLIC_KEY);
+# endif
+#endif
diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c
new file mode 100644
index 000000000000..1f21a5129615
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2ms.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Low level APIs are deprecated for public use, but still ok for internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/pem.h> /* Functions for writing MSBLOB and PVK */
+#include <openssl/dsa.h>
+#include "internal/passphrase.h"
+#include "crypto/rsa.h"
+#include "prov/implementations.h"
+#include "prov/bio.h"
+#include "prov/provider_ctx.h"
+#include "endecoder_local.h"
+
+struct key2ms_ctx_st {
+ PROV_CTX *provctx;
+
+ int pvk_encr_level;
+
+ struct ossl_passphrase_data_st pwdata;
+};
+
+static int write_msblob(struct key2ms_ctx_st *ctx, OSSL_CORE_BIO *cout,
+ EVP_PKEY *pkey, int ispub)
+{
+ BIO *out = ossl_bio_new_from_core_bio(ctx->provctx, cout);
+ int ret;
+
+ if (out == NULL)
+ return 0;
+ ret = ispub ? i2b_PublicKey_bio(out, pkey) : i2b_PrivateKey_bio(out, pkey);
+
+ BIO_free(out);
+ return ret;
+}
+
+static int write_pvk(struct key2ms_ctx_st *ctx, OSSL_CORE_BIO *cout,
+ EVP_PKEY *pkey)
+{
+ BIO *out = NULL;
+ int ret;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ out = ossl_bio_new_from_core_bio(ctx->provctx, cout);
+ if (out == NULL)
+ return 0;
+ ret = i2b_PVK_bio_ex(out, pkey, ctx->pvk_encr_level,
+ ossl_pw_pvk_password, &ctx->pwdata, libctx, NULL);
+ BIO_free(out);
+ return ret;
+}
+
+static OSSL_FUNC_encoder_freectx_fn key2ms_freectx;
+static OSSL_FUNC_encoder_does_selection_fn key2ms_does_selection;
+
+static struct key2ms_ctx_st *key2ms_newctx(void *provctx)
+{
+ struct key2ms_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL) {
+ ctx->provctx = provctx;
+ /* This is the strongest encryption level */
+ ctx->pvk_encr_level = 2;
+ }
+ return ctx;
+}
+
+static void key2ms_freectx(void *vctx)
+{
+ struct key2ms_ctx_st *ctx = vctx;
+
+ ossl_pw_clear_passphrase_data(&ctx->pwdata);
+ OPENSSL_free(ctx);
+}
+
+static const OSSL_PARAM *key2pvk_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_int(OSSL_ENCODER_PARAM_ENCRYPT_LEVEL, NULL),
+ OSSL_PARAM_END,
+ };
+
+ return settables;
+}
+
+static int key2pvk_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct key2ms_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ENCODER_PARAM_ENCRYPT_LEVEL);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->pvk_encr_level))
+ return 0;
+ return 1;
+}
+
+static int key2ms_does_selection(void *vctx, int selection)
+{
+ return (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0;
+}
+
+/*
+ * The real EVP_PKEY_set1_TYPE() functions take a non-const key, while the key
+ * pointer in the encode function is a const pointer. We violate the constness
+ * knowingly, since we know that the key comes from the same provider, is never
+ * actually const, and the implied reference count change is safe.
+ *
+ * EVP_PKEY_assign() can't be used, because there's no way to clear the internal
+ * key using that function without freeing the existing internal key.
+ */
+typedef int evp_pkey_set1_fn(EVP_PKEY *, const void *key);
+
+static int key2msblob_encode(void *vctx, const void *key, int selection,
+ OSSL_CORE_BIO *cout, evp_pkey_set1_fn *set1_key,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct key2ms_ctx_st *ctx = vctx;
+ int ispub = -1;
+ EVP_PKEY *pkey = NULL;
+ int ok = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ispub = 0;
+ else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ispub = 1;
+ else
+ return 0; /* Error */
+
+ if ((pkey = EVP_PKEY_new()) != NULL && set1_key(pkey, key))
+ ok = write_msblob(ctx, cout, pkey, ispub);
+ EVP_PKEY_free(pkey);
+ return ok;
+}
+
+static int key2pvk_encode(void *vctx, const void *key, int selection,
+ OSSL_CORE_BIO *cout, evp_pkey_set1_fn *set1_key,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct key2ms_ctx_st *ctx = vctx;
+ EVP_PKEY *pkey = NULL;
+ int ok = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0)
+ return 0; /* Error */
+
+ if ((pkey = EVP_PKEY_new()) != NULL && set1_key(pkey, key)
+ && (pw_cb == NULL
+ || ossl_pw_set_ossl_passphrase_cb(&ctx->pwdata, pw_cb, pw_cbarg)))
+ ok = write_pvk(ctx, cout, pkey);
+ EVP_PKEY_free(pkey);
+ return ok;
+}
+
+#define dsa_set1 (evp_pkey_set1_fn *)EVP_PKEY_set1_DSA
+#define rsa_set1 (evp_pkey_set1_fn *)EVP_PKEY_set1_RSA
+
+#define msblob_set_params
+#define pvk_set_params \
+ { OSSL_FUNC_ENCODER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))key2pvk_settable_ctx_params }, \
+ { OSSL_FUNC_ENCODER_SET_CTX_PARAMS, \
+ (void (*)(void))key2pvk_set_ctx_params },
+
+#define MAKE_MS_ENCODER(impl, output, type) \
+ static OSSL_FUNC_encoder_import_object_fn \
+ impl##2##output##_import_object; \
+ static OSSL_FUNC_encoder_free_object_fn impl##2##output##_free_object; \
+ static OSSL_FUNC_encoder_encode_fn impl##2##output##_encode; \
+ \
+ static void * \
+ impl##2##output##_import_object(void *ctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \
+ ctx, selection, params); \
+ } \
+ static void impl##2##output##_free_object(void *key) \
+ { \
+ ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \
+ } \
+ static int impl##2##output##_encode(void *vctx, OSSL_CORE_BIO *cout, \
+ const void *key, \
+ const OSSL_PARAM key_abstract[], \
+ int selection, \
+ OSSL_PASSPHRASE_CALLBACK *cb, \
+ void *cbarg) \
+ { \
+ /* We don't deal with abstract objects */ \
+ if (key_abstract != NULL) { \
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \
+ return 0; \
+ } \
+ return key2##output##_encode(vctx, key, selection, cout, type##_set1, \
+ cb, cbarg); \
+ } \
+ const OSSL_DISPATCH ossl_##impl##_to_##output##_encoder_functions[] = { \
+ { OSSL_FUNC_ENCODER_NEWCTX, \
+ (void (*)(void))key2ms_newctx }, \
+ { OSSL_FUNC_ENCODER_FREECTX, \
+ (void (*)(void))key2ms_freectx }, \
+ output##_set_params \
+ { OSSL_FUNC_ENCODER_DOES_SELECTION, \
+ (void (*)(void))key2ms_does_selection }, \
+ { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \
+ (void (*)(void))impl##2##output##_import_object }, \
+ { OSSL_FUNC_ENCODER_FREE_OBJECT, \
+ (void (*)(void))impl##2##output##_free_object }, \
+ { OSSL_FUNC_ENCODER_ENCODE, \
+ (void (*)(void))impl##2##output##_encode }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_DSA
+MAKE_MS_ENCODER(dsa, pvk, dsa);
+MAKE_MS_ENCODER(dsa, msblob, dsa);
+#endif
+
+MAKE_MS_ENCODER(rsa, pvk, rsa);
+MAKE_MS_ENCODER(rsa, msblob, rsa);
diff --git a/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c b/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c
new file mode 100644
index 000000000000..7a564807326f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/encode_key2text.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Low level APIs are deprecated for public use, but still ok for internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/safestack.h>
+#include <openssl/proverr.h>
+#include "crypto/dh.h" /* ossl_dh_get0_params() */
+#include "crypto/dsa.h" /* ossl_dsa_get0_params() */
+#include "crypto/ec.h" /* ossl_ec_key_get_libctx */
+#include "crypto/ecx.h" /* ECX_KEY, etc... */
+#include "crypto/ml_kem.h" /* ML_KEM_KEY, etc... */
+#include "crypto/rsa.h" /* RSA_PSS_PARAMS_30, etc... */
+#include "crypto/ml_dsa.h"
+#include "crypto/slh_dsa.h"
+#include "prov/bio.h"
+#include "prov/implementations.h"
+#include "internal/encoder.h"
+#include "endecoder_local.h"
+#include "ml_dsa_codecs.h"
+#include "ml_kem_codecs.h"
+
+DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DH
+static int dh_to_text(BIO *out, const void *key, int selection)
+{
+ const DH *dh = key;
+ const char *type_label = NULL;
+ const BIGNUM *priv_key = NULL, *pub_key = NULL;
+ const FFC_PARAMS *params = NULL;
+ const BIGNUM *p = NULL;
+ long length;
+
+ if (out == NULL || dh == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ type_label = "DH Private-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ type_label = "DH Public-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ type_label = "DH Parameters";
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ priv_key = DH_get0_priv_key(dh);
+ if (priv_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ pub_key = DH_get0_pub_key(dh);
+ if (pub_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ params = ossl_dh_get0_params((DH *)dh);
+ if (params == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_PARAMETERS);
+ return 0;
+ }
+ }
+
+ p = DH_get0_p(dh);
+ if (p == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ if (BIO_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p)) <= 0)
+ return 0;
+ if (priv_key != NULL
+ && !ossl_bio_print_labeled_bignum(out, "private-key:", priv_key))
+ return 0;
+ if (pub_key != NULL
+ && !ossl_bio_print_labeled_bignum(out, "public-key:", pub_key))
+ return 0;
+ if (params != NULL
+ && !ossl_bio_print_ffc_params(out, params))
+ return 0;
+ length = DH_get_length(dh);
+ if (length > 0
+ && BIO_printf(out, "recommended-private-length: %ld bits\n",
+ length) <= 0)
+ return 0;
+
+ return 1;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_DSA
+static int dsa_to_text(BIO *out, const void *key, int selection)
+{
+ const DSA *dsa = key;
+ const char *type_label = NULL;
+ const BIGNUM *priv_key = NULL, *pub_key = NULL;
+ const FFC_PARAMS *params = NULL;
+ const BIGNUM *p = NULL;
+
+ if (out == NULL || dsa == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ type_label = "Private-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ type_label = "Public-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ type_label = "DSA-Parameters";
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ priv_key = DSA_get0_priv_key(dsa);
+ if (priv_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ pub_key = DSA_get0_pub_key(dsa);
+ if (pub_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ params = ossl_dsa_get0_params((DSA *)dsa);
+ if (params == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_PARAMETERS);
+ return 0;
+ }
+ }
+
+ p = DSA_get0_p(dsa);
+ if (p == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ if (BIO_printf(out, "%s: (%d bit)\n", type_label, BN_num_bits(p)) <= 0)
+ return 0;
+ if (priv_key != NULL
+ && !ossl_bio_print_labeled_bignum(out, "priv:", priv_key))
+ return 0;
+ if (pub_key != NULL
+ && !ossl_bio_print_labeled_bignum(out, "pub: ", pub_key))
+ return 0;
+ if (params != NULL
+ && !ossl_bio_print_ffc_params(out, params))
+ return 0;
+
+ return 1;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_EC
+static int ec_param_explicit_curve_to_text(BIO *out, const EC_GROUP *group,
+ BN_CTX *ctx)
+{
+ const char *plabel = "Prime:";
+ BIGNUM *p = NULL, *a = NULL, *b = NULL;
+
+ p = BN_CTX_get(ctx);
+ a = BN_CTX_get(ctx);
+ b = BN_CTX_get(ctx);
+ if (b == NULL
+ || !EC_GROUP_get_curve(group, p, a, b, ctx))
+ return 0;
+
+ if (EC_GROUP_get_field_type(group) == NID_X9_62_characteristic_two_field) {
+ int basis_type = EC_GROUP_get_basis_type(group);
+
+ /* print the 'short name' of the base type OID */
+ if (basis_type == NID_undef
+ || BIO_printf(out, "Basis Type: %s\n", OBJ_nid2sn(basis_type)) <= 0)
+ return 0;
+ plabel = "Polynomial:";
+ }
+ return ossl_bio_print_labeled_bignum(out, plabel, p)
+ && ossl_bio_print_labeled_bignum(out, "A: ", a)
+ && ossl_bio_print_labeled_bignum(out, "B: ", b);
+}
+
+static int ec_param_explicit_gen_to_text(BIO *out, const EC_GROUP *group,
+ BN_CTX *ctx)
+{
+ int ret;
+ size_t buflen;
+ point_conversion_form_t form;
+ const EC_POINT *point = NULL;
+ const char *glabel = NULL;
+ unsigned char *buf = NULL;
+
+ form = EC_GROUP_get_point_conversion_form(group);
+ point = EC_GROUP_get0_generator(group);
+
+ if (point == NULL)
+ return 0;
+
+ switch (form) {
+ case POINT_CONVERSION_COMPRESSED:
+ glabel = "Generator (compressed):";
+ break;
+ case POINT_CONVERSION_UNCOMPRESSED:
+ glabel = "Generator (uncompressed):";
+ break;
+ case POINT_CONVERSION_HYBRID:
+ glabel = "Generator (hybrid):";
+ break;
+ default:
+ return 0;
+ }
+
+ buflen = EC_POINT_point2buf(group, point, form, &buf, ctx);
+ if (buflen == 0)
+ return 0;
+
+ ret = ossl_bio_print_labeled_buf(out, glabel, buf, buflen);
+ OPENSSL_clear_free(buf, buflen);
+ return ret;
+}
+
+/* Print explicit parameters */
+static int ec_param_explicit_to_text(BIO *out, const EC_GROUP *group,
+ OSSL_LIB_CTX *libctx)
+{
+ int ret = 0, tmp_nid;
+ BN_CTX *ctx = NULL;
+ const BIGNUM *order = NULL, *cofactor = NULL;
+ const unsigned char *seed;
+ size_t seed_len = 0;
+
+ ctx = BN_CTX_new_ex(libctx);
+ if (ctx == NULL)
+ return 0;
+ BN_CTX_start(ctx);
+
+ tmp_nid = EC_GROUP_get_field_type(group);
+ order = EC_GROUP_get0_order(group);
+ if (order == NULL)
+ goto err;
+
+ seed = EC_GROUP_get0_seed(group);
+ if (seed != NULL)
+ seed_len = EC_GROUP_get_seed_len(group);
+ cofactor = EC_GROUP_get0_cofactor(group);
+
+ /* print the 'short name' of the field type */
+ if (BIO_printf(out, "Field Type: %s\n", OBJ_nid2sn(tmp_nid)) <= 0
+ || !ec_param_explicit_curve_to_text(out, group, ctx)
+ || !ec_param_explicit_gen_to_text(out, group, ctx)
+ || !ossl_bio_print_labeled_bignum(out, "Order: ", order)
+ || (cofactor != NULL
+ && !ossl_bio_print_labeled_bignum(out, "Cofactor: ", cofactor))
+ || (seed != NULL
+ && !ossl_bio_print_labeled_buf(out, "Seed:", seed, seed_len)))
+ goto err;
+ ret = 1;
+err:
+ BN_CTX_end(ctx);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+static int ec_param_to_text(BIO *out, const EC_GROUP *group,
+ OSSL_LIB_CTX *libctx)
+{
+ if (EC_GROUP_get_asn1_flag(group) & OPENSSL_EC_NAMED_CURVE) {
+ const char *curve_name;
+ int curve_nid = EC_GROUP_get_curve_name(group);
+
+ /* Explicit parameters */
+ if (curve_nid == NID_undef)
+ return 0;
+
+ if (BIO_printf(out, "%s: %s\n", "ASN1 OID", OBJ_nid2sn(curve_nid)) <= 0)
+ return 0;
+
+ curve_name = EC_curve_nid2nist(curve_nid);
+ return (curve_name == NULL
+ || BIO_printf(out, "%s: %s\n", "NIST CURVE", curve_name) > 0);
+ } else {
+ return ec_param_explicit_to_text(out, group, libctx);
+ }
+}
+
+static int ec_to_text(BIO *out, const void *key, int selection)
+{
+ const EC_KEY *ec = key;
+ const char *type_label = NULL;
+ unsigned char *priv = NULL, *pub = NULL;
+ size_t priv_len = 0, pub_len = 0;
+ const EC_GROUP *group;
+ int ret = 0;
+
+ if (out == NULL || ec == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ if ((group = EC_KEY_get0_group(ec)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ type_label = "Private-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ type_label = "Public-Key";
+ else if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ if (EC_GROUP_get_curve_name(group) != NID_sm2)
+ type_label = "EC-Parameters";
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const BIGNUM *priv_key = EC_KEY_get0_private_key(ec);
+
+ if (priv_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ goto err;
+ }
+ priv_len = EC_KEY_priv2buf(ec, &priv);
+ if (priv_len == 0)
+ goto err;
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ const EC_POINT *pub_pt = EC_KEY_get0_public_key(ec);
+
+ if (pub_pt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ goto err;
+ }
+
+ pub_len = EC_KEY_key2buf(ec, EC_KEY_get_conv_form(ec), &pub, NULL);
+ if (pub_len == 0)
+ goto err;
+ }
+
+ if (type_label != NULL
+ && BIO_printf(out, "%s: (%d bit)\n", type_label,
+ EC_GROUP_order_bits(group)) <= 0)
+ goto err;
+ if (priv != NULL
+ && !ossl_bio_print_labeled_buf(out, "priv:", priv, priv_len))
+ goto err;
+ if (pub != NULL
+ && !ossl_bio_print_labeled_buf(out, "pub:", pub, pub_len))
+ goto err;
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ret = ec_param_to_text(out, group, ossl_ec_key_get_libctx(ec));
+err:
+ OPENSSL_clear_free(priv, priv_len);
+ OPENSSL_free(pub);
+ return ret;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ECX
+static int ecx_to_text(BIO *out, const void *key, int selection)
+{
+ const ECX_KEY *ecx = key;
+ const char *type_label = NULL;
+
+ if (out == NULL || ecx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+
+ switch (ecx->type) {
+ case ECX_KEY_TYPE_X25519:
+ type_label = "X25519";
+ break;
+ case ECX_KEY_TYPE_X448:
+ type_label = "X448";
+ break;
+ case ECX_KEY_TYPE_ED25519:
+ type_label = "ED25519";
+ break;
+ case ECX_KEY_TYPE_ED448:
+ type_label = "ED448";
+ break;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ if (ecx->privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+
+ if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0)
+ return 0;
+ if (!ossl_bio_print_labeled_buf(out, "priv:", ecx->privkey, ecx->keylen))
+ return 0;
+ } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ /* ecx->pubkey is an array, not a pointer... */
+ if (!ecx->haspubkey) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+
+ if (BIO_printf(out, "%s Public-Key:\n", type_label) <= 0)
+ return 0;
+ }
+
+ if (!ossl_bio_print_labeled_buf(out, "pub:", ecx->pubkey, ecx->keylen))
+ return 0;
+
+ return 1;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_KEM
+static int ml_kem_to_text(BIO *out, const void *vkey, int selection)
+{
+ return ossl_ml_kem_key_to_text(out, (ML_KEM_KEY *)vkey, selection);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_SLH_DSA
+static int slh_dsa_to_text(BIO *out, const void *key, int selection)
+{
+ return ossl_slh_dsa_key_to_text(out, (SLH_DSA_KEY *)key, selection);
+}
+#endif /* OPENSSL_NO_SLH_DSA */
+
+static int rsa_to_text(BIO *out, const void *key, int selection)
+{
+ const RSA *rsa = key;
+ const char *type_label = "RSA key";
+ const char *modulus_label = NULL;
+ const char *exponent_label = NULL;
+ const BIGNUM *rsa_d = NULL, *rsa_n = NULL, *rsa_e = NULL;
+ STACK_OF(BIGNUM_const) *factors = NULL;
+ STACK_OF(BIGNUM_const) *exps = NULL;
+ STACK_OF(BIGNUM_const) *coeffs = NULL;
+ int primes;
+ const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30((RSA *)rsa);
+ int ret = 0;
+
+ if (out == NULL || rsa == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ goto err;
+ }
+
+ factors = sk_BIGNUM_const_new_null();
+ exps = sk_BIGNUM_const_new_null();
+ coeffs = sk_BIGNUM_const_new_null();
+
+ if (factors == NULL || exps == NULL || coeffs == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_CRYPTO_LIB);
+ goto err;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ type_label = "Private-Key";
+ modulus_label = "modulus:";
+ exponent_label = "publicExponent:";
+ } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ type_label = "Public-Key";
+ modulus_label = "Modulus:";
+ exponent_label = "Exponent:";
+ }
+
+ RSA_get0_key(rsa, &rsa_n, &rsa_e, &rsa_d);
+ ossl_rsa_get0_all_params((RSA *)rsa, factors, exps, coeffs);
+ primes = sk_BIGNUM_const_num(factors);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ if (BIO_printf(out, "%s: (%d bit, %d primes)\n",
+ type_label, BN_num_bits(rsa_n), primes) <= 0)
+ goto err;
+ } else {
+ if (BIO_printf(out, "%s: (%d bit)\n",
+ type_label, BN_num_bits(rsa_n)) <= 0)
+ goto err;
+ }
+
+ if (!ossl_bio_print_labeled_bignum(out, modulus_label, rsa_n))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, exponent_label, rsa_e))
+ goto err;
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ int i;
+
+ if (!ossl_bio_print_labeled_bignum(out, "privateExponent:", rsa_d))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, "prime1:",
+ sk_BIGNUM_const_value(factors, 0)))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, "prime2:",
+ sk_BIGNUM_const_value(factors, 1)))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, "exponent1:",
+ sk_BIGNUM_const_value(exps, 0)))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, "exponent2:",
+ sk_BIGNUM_const_value(exps, 1)))
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, "coefficient:",
+ sk_BIGNUM_const_value(coeffs, 0)))
+ goto err;
+ for (i = 2; i < sk_BIGNUM_const_num(factors); i++) {
+ if (BIO_printf(out, "prime%d:", i + 1) <= 0)
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, NULL,
+ sk_BIGNUM_const_value(factors, i)))
+ goto err;
+ if (BIO_printf(out, "exponent%d:", i + 1) <= 0)
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, NULL,
+ sk_BIGNUM_const_value(exps, i)))
+ goto err;
+ if (BIO_printf(out, "coefficient%d:", i + 1) <= 0)
+ goto err;
+ if (!ossl_bio_print_labeled_bignum(out, NULL,
+ sk_BIGNUM_const_value(coeffs, i - 1)))
+ goto err;
+ }
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0) {
+ switch (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ if (!ossl_rsa_pss_params_30_is_unrestricted(pss_params)) {
+ if (BIO_printf(out, "(INVALID PSS PARAMETERS)\n") <= 0)
+ goto err;
+ }
+ break;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ if (ossl_rsa_pss_params_30_is_unrestricted(pss_params)) {
+ if (BIO_printf(out, "No PSS parameter restrictions\n") <= 0)
+ goto err;
+ } else {
+ int hashalg_nid = ossl_rsa_pss_params_30_hashalg(pss_params);
+ int maskgenalg_nid =
+ ossl_rsa_pss_params_30_maskgenalg(pss_params);
+ int maskgenhashalg_nid =
+ ossl_rsa_pss_params_30_maskgenhashalg(pss_params);
+ int saltlen = ossl_rsa_pss_params_30_saltlen(pss_params);
+ int trailerfield =
+ ossl_rsa_pss_params_30_trailerfield(pss_params);
+
+ if (BIO_printf(out, "PSS parameter restrictions:\n") <= 0)
+ goto err;
+ if (BIO_printf(out, " Hash Algorithm: %s%s\n",
+ ossl_rsa_oaeppss_nid2name(hashalg_nid),
+ (hashalg_nid == NID_sha1
+ ? " (default)" : "")) <= 0)
+ goto err;
+ if (BIO_printf(out, " Mask Algorithm: %s with %s%s\n",
+ ossl_rsa_mgf_nid2name(maskgenalg_nid),
+ ossl_rsa_oaeppss_nid2name(maskgenhashalg_nid),
+ (maskgenalg_nid == NID_mgf1
+ && maskgenhashalg_nid == NID_sha1
+ ? " (default)" : "")) <= 0)
+ goto err;
+ if (BIO_printf(out, " Minimum Salt Length: %d%s\n",
+ saltlen,
+ (saltlen == 20 ? " (default)" : "")) <= 0)
+ goto err;
+ if (BIO_printf(out, " Trailer Field: 0x%x%s\n",
+ trailerfield,
+ (trailerfield == 1 ? " (default)" : "")) <= 0)
+ goto err;
+ }
+ break;
+ }
+ }
+
+ ret = 1;
+ err:
+ sk_BIGNUM_const_free(factors);
+ sk_BIGNUM_const_free(exps);
+ sk_BIGNUM_const_free(coeffs);
+ return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef OPENSSL_NO_ML_DSA
+static int ml_dsa_to_text(BIO *out, const void *key, int selection)
+{
+ return ossl_ml_dsa_key_to_text(out, (ML_DSA_KEY *)key, selection);
+}
+#endif /* OPENSSL_NO_ML_DSA */
+/* ---------------------------------------------------------------------- */
+
+static void *key2text_newctx(void *provctx)
+{
+ return provctx;
+}
+
+static void key2text_freectx(ossl_unused void *vctx)
+{
+}
+
+static int key2text_encode(void *vctx, const void *key, int selection,
+ OSSL_CORE_BIO *cout,
+ int (*key2text)(BIO *out, const void *key,
+ int selection),
+ OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)
+{
+ BIO *out = ossl_bio_new_from_core_bio(vctx, cout);
+ int ret;
+
+ if (out == NULL)
+ return 0;
+
+ ret = key2text(out, key, selection);
+ BIO_free(out);
+
+ return ret;
+}
+
+#define MAKE_TEXT_ENCODER(impl, type) \
+ static OSSL_FUNC_encoder_import_object_fn \
+ impl##2text_import_object; \
+ static OSSL_FUNC_encoder_free_object_fn \
+ impl##2text_free_object; \
+ static OSSL_FUNC_encoder_encode_fn impl##2text_encode; \
+ \
+ static void *impl##2text_import_object(void *ctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ return ossl_prov_import_key(ossl_##impl##_keymgmt_functions, \
+ ctx, selection, params); \
+ } \
+ static void impl##2text_free_object(void *key) \
+ { \
+ ossl_prov_free_key(ossl_##impl##_keymgmt_functions, key); \
+ } \
+ static int impl##2text_encode(void *vctx, OSSL_CORE_BIO *cout, \
+ const void *key, \
+ const OSSL_PARAM key_abstract[], \
+ int selection, \
+ OSSL_PASSPHRASE_CALLBACK *cb, \
+ void *cbarg) \
+ { \
+ /* We don't deal with abstract objects */ \
+ if (key_abstract != NULL) { \
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT); \
+ return 0; \
+ } \
+ return key2text_encode(vctx, key, selection, cout, \
+ type##_to_text, cb, cbarg); \
+ } \
+ const OSSL_DISPATCH ossl_##impl##_to_text_encoder_functions[] = { \
+ { OSSL_FUNC_ENCODER_NEWCTX, \
+ (void (*)(void))key2text_newctx }, \
+ { OSSL_FUNC_ENCODER_FREECTX, \
+ (void (*)(void))key2text_freectx }, \
+ { OSSL_FUNC_ENCODER_IMPORT_OBJECT, \
+ (void (*)(void))impl##2text_import_object }, \
+ { OSSL_FUNC_ENCODER_FREE_OBJECT, \
+ (void (*)(void))impl##2text_free_object }, \
+ { OSSL_FUNC_ENCODER_ENCODE, \
+ (void (*)(void))impl##2text_encode }, \
+ OSSL_DISPATCH_END \
+ }
+
+#ifndef OPENSSL_NO_DH
+MAKE_TEXT_ENCODER(dh, dh);
+MAKE_TEXT_ENCODER(dhx, dh);
+#endif
+#ifndef OPENSSL_NO_DSA
+MAKE_TEXT_ENCODER(dsa, dsa);
+#endif
+#ifndef OPENSSL_NO_EC
+MAKE_TEXT_ENCODER(ec, ec);
+# ifndef OPENSSL_NO_SM2
+MAKE_TEXT_ENCODER(sm2, ec);
+# endif
+# ifndef OPENSSL_NO_ECX
+MAKE_TEXT_ENCODER(ed25519, ecx);
+MAKE_TEXT_ENCODER(ed448, ecx);
+MAKE_TEXT_ENCODER(x25519, ecx);
+MAKE_TEXT_ENCODER(x448, ecx);
+# endif
+#endif
+#ifndef OPENSSL_NO_ML_KEM
+MAKE_TEXT_ENCODER(ml_kem_512, ml_kem);
+MAKE_TEXT_ENCODER(ml_kem_768, ml_kem);
+MAKE_TEXT_ENCODER(ml_kem_1024, ml_kem);
+#endif
+MAKE_TEXT_ENCODER(rsa, rsa);
+MAKE_TEXT_ENCODER(rsapss, rsa);
+
+#ifndef OPENSSL_NO_ML_DSA
+MAKE_TEXT_ENCODER(ml_dsa_44, ml_dsa);
+MAKE_TEXT_ENCODER(ml_dsa_65, ml_dsa);
+MAKE_TEXT_ENCODER(ml_dsa_87, ml_dsa);
+#endif
+
+#ifndef OPENSSL_NO_SLH_DSA
+MAKE_TEXT_ENCODER(slh_dsa_sha2_128s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_sha2_128f, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_sha2_192s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_sha2_192f, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_sha2_256s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_sha2_256f, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_128s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_128f, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_192s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_192f, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_256s, slh_dsa);
+MAKE_TEXT_ENCODER(slh_dsa_shake_256f, slh_dsa);
+#endif
diff --git a/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c b/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c
new file mode 100644
index 000000000000..c4ea2f853cfc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/endecoder_common.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2020-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/buffer.h>
+#include "internal/asn1.h"
+#include "prov/bio.h"
+#include "endecoder_local.h"
+
+OSSL_FUNC_keymgmt_new_fn *
+ossl_prov_get_keymgmt_new(const OSSL_DISPATCH *fns)
+{
+ /* Pilfer the keymgmt dispatch table */
+ for (; fns->function_id != 0; fns++)
+ if (fns->function_id == OSSL_FUNC_KEYMGMT_NEW)
+ return OSSL_FUNC_keymgmt_new(fns);
+
+ return NULL;
+}
+
+OSSL_FUNC_keymgmt_free_fn *
+ossl_prov_get_keymgmt_free(const OSSL_DISPATCH *fns)
+{
+ /* Pilfer the keymgmt dispatch table */
+ for (; fns->function_id != 0; fns++)
+ if (fns->function_id == OSSL_FUNC_KEYMGMT_FREE)
+ return OSSL_FUNC_keymgmt_free(fns);
+
+ return NULL;
+}
+
+OSSL_FUNC_keymgmt_import_fn *
+ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *fns)
+{
+ /* Pilfer the keymgmt dispatch table */
+ for (; fns->function_id != 0; fns++)
+ if (fns->function_id == OSSL_FUNC_KEYMGMT_IMPORT)
+ return OSSL_FUNC_keymgmt_import(fns);
+
+ return NULL;
+}
+
+OSSL_FUNC_keymgmt_export_fn *
+ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns)
+{
+ /* Pilfer the keymgmt dispatch table */
+ for (; fns->function_id != 0; fns++)
+ if (fns->function_id == OSSL_FUNC_KEYMGMT_EXPORT)
+ return OSSL_FUNC_keymgmt_export(fns);
+
+ return NULL;
+}
+
+void *ossl_prov_import_key(const OSSL_DISPATCH *fns, void *provctx,
+ int selection, const OSSL_PARAM params[])
+{
+ OSSL_FUNC_keymgmt_new_fn *kmgmt_new = ossl_prov_get_keymgmt_new(fns);
+ OSSL_FUNC_keymgmt_free_fn *kmgmt_free = ossl_prov_get_keymgmt_free(fns);
+ OSSL_FUNC_keymgmt_import_fn *kmgmt_import =
+ ossl_prov_get_keymgmt_import(fns);
+ void *key = NULL;
+
+ if (kmgmt_new != NULL && kmgmt_import != NULL && kmgmt_free != NULL) {
+ if ((key = kmgmt_new(provctx)) == NULL
+ || !kmgmt_import(key, selection, params)) {
+ kmgmt_free(key);
+ key = NULL;
+ }
+ }
+ return key;
+}
+
+void ossl_prov_free_key(const OSSL_DISPATCH *fns, void *key)
+{
+ OSSL_FUNC_keymgmt_free_fn *kmgmt_free = ossl_prov_get_keymgmt_free(fns);
+
+ if (kmgmt_free != NULL)
+ kmgmt_free(key);
+}
+
+int ossl_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin, unsigned char **data,
+ long *len)
+{
+ BUF_MEM *mem = NULL;
+ BIO *in = ossl_bio_new_from_core_bio(provctx, cin);
+ int ok;
+
+ if (in == NULL)
+ return 0;
+ ok = (asn1_d2i_read_bio(in, &mem) >= 0);
+ if (ok) {
+ *data = (unsigned char *)mem->data;
+ *len = (long)mem->length;
+ OPENSSL_free(mem);
+ }
+ BIO_free(in);
+ return ok;
+}
diff --git a/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h b/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h
new file mode 100644
index 000000000000..a65d05ffaeac
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/endecoder_local.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/types.h>
+#include "prov/provider_ctx.h"
+
+OSSL_FUNC_keymgmt_new_fn *ossl_prov_get_keymgmt_new(const OSSL_DISPATCH *fns);
+OSSL_FUNC_keymgmt_free_fn *ossl_prov_get_keymgmt_free(const OSSL_DISPATCH *fns);
+OSSL_FUNC_keymgmt_import_fn *ossl_prov_get_keymgmt_import(const OSSL_DISPATCH *fns);
+OSSL_FUNC_keymgmt_export_fn *ossl_prov_get_keymgmt_export(const OSSL_DISPATCH *fns);
+
+int ossl_prov_der_from_p8(unsigned char **new_der, long *new_der_len,
+ unsigned char *input_der, long input_der_len,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg);
+
+void *ossl_prov_import_key(const OSSL_DISPATCH *fns, void *provctx,
+ int selection, const OSSL_PARAM params[]);
+void ossl_prov_free_key(const OSSL_DISPATCH *fns, void *key);
+int ossl_read_der(PROV_CTX *provctx, OSSL_CORE_BIO *cin, unsigned char **data,
+ long *len);
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c
new file mode 100644
index 000000000000..773550c9fb93
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "ml_common_codecs.h"
+
+static int pref_cmp(const void *va, const void *vb)
+{
+ const ML_COMMON_PKCS8_FMT_PREF *a = va;
+ const ML_COMMON_PKCS8_FMT_PREF *b = vb;
+
+ /*
+ * Zeros sort last, otherwise the sort is in increasing order.
+ *
+ * The preferences are small enough to ensure the comparison is transitive
+ * as required by qsort(3). When overflow or underflow is possible, the
+ * correct transitive comparison would be: (b < a) - (a < b).
+ */
+ if (a->pref > 0 && b->pref > 0)
+ return a->pref - b->pref;
+ /* A preference of 0 is "larger" than (sorts after) any nonzero value. */
+ return b->pref - a->pref;
+}
+
+ML_COMMON_PKCS8_FMT_PREF *
+ossl_ml_common_pkcs8_fmt_order(const char *algorithm_name,
+ const ML_COMMON_PKCS8_FMT *p8fmt,
+ const char *direction, const char *formats)
+{
+ ML_COMMON_PKCS8_FMT_PREF *ret;
+ int i, count = 0;
+ const char *fmt = formats, *end;
+ const char *sep = "\t ,";
+
+ /* Reserve an extra terminal slot with fmt == NULL */
+ if ((ret = OPENSSL_zalloc((NUM_PKCS8_FORMATS + 1) * sizeof(*ret))) == NULL)
+ return NULL;
+
+ /* Entries that match a format will get a non-zero preference. */
+ for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
+ ret[i].fmt = &p8fmt[i];
+ ret[i].pref = 0;
+ }
+
+ /* Default to compile-time table order when none specified. */
+ if (formats == NULL)
+ return ret;
+
+ /*
+ * Formats are case-insensitive, separated by spaces, tabs or commas.
+ * Duplicate formats are allowed, the first occurence determines the order.
+ */
+ do {
+ if (*(fmt += strspn(fmt, sep)) == '\0')
+ break;
+ end = fmt + strcspn(fmt, sep);
+ for (i = 0; i < NUM_PKCS8_FORMATS; ++i) {
+ /* Skip slots already selected or with a different name. */
+ if (ret[i].pref > 0
+ || OPENSSL_strncasecmp(ret[i].fmt->p8_name,
+ fmt, (end - fmt)) != 0)
+ continue;
+ /* First time match */
+ ret[i].pref = ++count;
+ break;
+ }
+ fmt = end;
+ } while (count < NUM_PKCS8_FORMATS);
+
+ /* No formats matched, raise an error */
+ if (count == 0) {
+ OPENSSL_free(ret);
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT,
+ "no %s private key %s formats are enabled",
+ algorithm_name, direction);
+ return NULL;
+ }
+ /* Sort by preference, with 0's last */
+ qsort(ret, NUM_PKCS8_FORMATS, sizeof(*ret), pref_cmp);
+ /* Terminate the list at first unselected entry, perhaps reserved slot. */
+ ret[count].fmt = NULL;
+ return ret;
+}
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h
new file mode 100644
index 000000000000..4bf618ea0b79
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_common_codecs.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef PROV_ML_COMMON_CODECS_H
+# define PROV_ML_COMMON_CODECS_H
+# pragma once
+
+# include <openssl/e_os2.h>
+# include "crypto/ml_dsa.h"
+# include "prov/provider_ctx.h"
+
+ /*-
+ * The DER ASN.1 encoding of ML-DSA and ML-KEM public keys prepends 22 bytes
+ * to the encoded public key:
+ *
+ * - 4 byte outer sequence tag and length
+ * - 2 byte algorithm sequence tag and length
+ * - 2 byte algorithm OID tag and length
+ * - 9 byte algorithm OID (from NIST CSOR OID arc)
+ * - 4 byte bit string tag and length
+ * - 1 bitstring lead byte
+ */
+# define ML_COMMON_SPKI_OVERHEAD 22
+typedef struct {
+ const uint8_t asn1_prefix[ML_COMMON_SPKI_OVERHEAD];
+} ML_COMMON_SPKI_FMT;
+
+/*-
+* For each parameter set we support a few PKCS#8 input formats, three
+ * corresponding to the "either or both" variants of:
+ *
+ * ML-DSA-PrivateKey ::= CHOICE {
+ * seed [0] IMPLICIT OCTET STRING (SIZE (32)),
+ * expandedKey OCTET STRING (SIZE (2560 | 4032 | 4896)),
+ * both SEQUENCE {
+ * seed OCTET STRING (SIZE (32)),
+ * expandedKey OCTET STRING (SIZE (2560 | 4032 | 4896)) } }
+ *
+ * ML-KEM-PrivateKey ::= CHOICE {
+ * seed [0] IMPLICIT OCTET STRING (SIZE (64)),
+ * expandedKey OCTET STRING (SIZE (1632 | 2400 | 3168)),
+ * both SEQUENCE {
+ * seed OCTET STRING (SIZE (64)),
+ * expandedKey OCTET STRING SIZE ((1632 | 2400 | 3168)) } }
+ *
+ * one more for a historical OQS encoding:
+ *
+ * - OQS private + public key: OCTET STRING
+ * (The public key is ignored, just as with PKCS#8 v2.)
+ *
+ * and two more that are the minimal IETF non-ASN.1 seed encoding:
+ *
+ * - Bare seed (just the 32 or 64 bytes)
+ * - Bare priv (just the key bytes)
+ *
+ * A length of zero means that particular field is absent.
+ *
+ * The p8_shift is 0 when the top-level tag+length occupy four bytes, 2 when
+ * they occupy two by†es, and 4 when no tag is used at all.
+ */
+#define NUM_PKCS8_FORMATS 6
+
+typedef struct {
+ const char *p8_name; /* Format name */
+ size_t p8_bytes; /* Total P8 encoding length */
+ int p8_shift; /* 4 - (top-level tag + len) */
+ uint32_t p8_magic; /* The tag + len value */
+ uint16_t seed_magic; /* Interior tag + len for the seed */
+ size_t seed_offset; /* Seed offset from start */
+ size_t seed_length; /* Seed bytes */
+ uint32_t priv_magic; /* Interior tag + len for the key */
+ size_t priv_offset; /* Key offset from start */
+ size_t priv_length; /* Key bytes */
+ size_t pub_offset; /* Pubkey offset */
+ size_t pub_length; /* Pubkey bytes */
+} ML_COMMON_PKCS8_FMT;
+
+typedef struct {
+ const ML_COMMON_SPKI_FMT *spkifmt;
+ const ML_COMMON_PKCS8_FMT *p8fmt;
+} ML_COMMON_CODEC;
+
+typedef struct {
+ const ML_COMMON_PKCS8_FMT *fmt;
+ int pref;
+} ML_COMMON_PKCS8_FMT_PREF;
+
+ML_COMMON_PKCS8_FMT_PREF *
+ossl_ml_common_pkcs8_fmt_order(const char *algorithm_name,
+ const ML_COMMON_PKCS8_FMT *p8fmt,
+ const char *direction, const char *formats);
+#endif
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c
new file mode 100644
index 000000000000..dd54137fe500
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/byteorder.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/x509.h>
+#include <openssl/core_names.h>
+#include "internal/encoder.h"
+#include "prov/ml_dsa.h"
+#include "ml_dsa_codecs.h"
+
+/*-
+ * Tables describing supported ASN.1 input/output formats.
+ */
+
+/*-
+ * ML-DSA-44:
+ * Public key bytes: 1312 (0x0520)
+ * Private key bytes: 2560 (0x0a00)
+ */
+static const ML_COMMON_SPKI_FMT ml_dsa_44_spkifmt = {
+ { 0x30, 0x82, 0x05, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x03, 0x11, 0x03, 0x82, 0x05, 0x21, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_dsa_44_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x0a2a, 0, 0x30820a26, 0x0420, 6, 0x20, 0x04820a00, 0x2a, 0x0a00, 0, 0, },
+ { "priv-only", 0x0a04, 0, 0x04820a00, 0, 0, 0, 0, 0x04, 0x0a00, 0, 0, },
+ { "oqskeypair", 0x0f24, 0, 0x04820f20, 0, 0, 0, 0, 0x04, 0x0a00, 0x0a04, 0x0520 },
+ { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, },
+ { "bare-priv", 0x0a00, 4, 0, 0, 0, 0, 0, 0, 0x0a00, 0, 0, },
+ { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, },
+};
+
+/*
+ * ML-DSA-65:
+ * Public key bytes: 1952 (0x07a0)
+ * Private key bytes: 4032 (0x0fc0)
+ */
+static const ML_COMMON_SPKI_FMT ml_dsa_65_spkifmt = {
+ { 0x30, 0x82, 0x07, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x03, 0x12, 0x03, 0x82, 0x07, 0xa1, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_dsa_65_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x0fea, 0, 0x30820fe6, 0x0420, 6, 0x20, 0x04820fc0, 0x2a, 0x0fc0, 0, 0, },
+ { "priv-only", 0x0fc4, 0, 0x04820fc0, 0, 0, 0, 0, 0x04, 0x0fc0, 0, 0, },
+ { "oqskeypair", 0x1764, 0, 0x04821760, 0, 0, 0, 0, 0x04, 0x0fc0, 0x0fc4, 0x07a0 },
+ { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, },
+ { "bare-priv", 0x0fc0, 4, 0, 0, 0, 0, 0, 0, 0x0fc0, 0, 0, },
+ { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, },
+};
+
+/*-
+ * ML-DSA-87:
+ * Public key bytes: 2592 (0x0a20)
+ * Private key bytes: 4896 (0x1320)
+ */
+static const ML_COMMON_SPKI_FMT ml_dsa_87_spkifmt = {
+ { 0x30, 0x82, 0x0a, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x03, 0x13, 0x03, 0x82, 0x0a, 0x21, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_dsa_87_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x134a, 0, 0x30821346, 0x0420, 6, 0x20, 0x04821320, 0x2a, 0x1320, 0, 0, },
+ { "priv-only", 0x1324, 0, 0x04821320, 0, 0, 0, 0, 0x04, 0x1320, 0, 0, },
+ { "oqskeypair", 0x1d44, 0, 0x04821d40, 0, 0, 0, 0, 0x04, 0x1320, 0x1324, 0x0a20 },
+ { "seed-only", 0x0022, 2, 0x8020, 0, 2, 0x20, 0, 0, 0, 0, 0, },
+ { "bare-priv", 0x1320, 4, 0, 0, 0, 0, 0, 0, 0x1320, 0, 0, },
+ { "bare-seed", 0x0020, 4, 0, 0, 0, 0x20, 0, 0, 0, 0, 0, },
+};
+
+/* Indices of slots in the codec table below */
+#define ML_DSA_44_CODEC 0
+#define ML_DSA_65_CODEC 1
+#define ML_DSA_87_CODEC 2
+
+/*
+ * Per-variant fixed parameters
+ */
+static const ML_COMMON_CODEC codecs[3] = {
+ { &ml_dsa_44_spkifmt, ml_dsa_44_p8fmt },
+ { &ml_dsa_65_spkifmt, ml_dsa_65_p8fmt },
+ { &ml_dsa_87_spkifmt, ml_dsa_87_p8fmt }
+};
+
+/* Retrieve the parameters of one of the ML-DSA variants */
+static const ML_COMMON_CODEC *ml_dsa_get_codec(int evp_type)
+{
+ switch (evp_type) {
+ case EVP_PKEY_ML_DSA_44:
+ return &codecs[ML_DSA_44_CODEC];
+ case EVP_PKEY_ML_DSA_65:
+ return &codecs[ML_DSA_65_CODEC];
+ case EVP_PKEY_ML_DSA_87:
+ return &codecs[ML_DSA_87_CODEC];
+ }
+ return NULL;
+}
+
+ML_DSA_KEY *
+ossl_ml_dsa_d2i_PUBKEY(const uint8_t *pk, int pk_len, int evp_type,
+ PROV_CTX *provctx, const char *propq)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ const ML_COMMON_CODEC *codec;
+ const ML_DSA_PARAMS *params;
+ ML_DSA_KEY *ret;
+
+ if ((params = ossl_ml_dsa_params_get(evp_type)) == NULL
+ || (codec = ml_dsa_get_codec(evp_type)) == NULL)
+ return NULL;
+ if (pk_len != ML_COMMON_SPKI_OVERHEAD + (ossl_ssize_t) params->pk_len
+ || memcmp(pk, codec->spkifmt->asn1_prefix, ML_COMMON_SPKI_OVERHEAD) != 0)
+ return NULL;
+ pk_len -= ML_COMMON_SPKI_OVERHEAD;
+ pk += ML_COMMON_SPKI_OVERHEAD;
+
+ if ((ret = ossl_ml_dsa_key_new(libctx, propq, evp_type)) == NULL)
+ return NULL;
+
+ if (!ossl_ml_dsa_pk_decode(ret, pk, (size_t) pk_len)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "errror parsing %s public key from input SPKI",
+ params->alg);
+ ossl_ml_dsa_key_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+ML_DSA_KEY *
+ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq)
+{
+ const ML_DSA_PARAMS *v;
+ const ML_COMMON_CODEC *codec;
+ ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
+ const ML_COMMON_PKCS8_FMT *p8fmt;
+ ML_DSA_KEY *key = NULL, *ret = NULL;
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+ const uint8_t *buf, *pos;
+ const X509_ALGOR *alg = NULL;
+ const char *formats;
+ int len, ptype;
+ uint32_t magic;
+ uint16_t seed_magic;
+ const uint8_t *seed = NULL;
+ const uint8_t *priv = NULL;
+
+ /* Which ML-DSA variant? */
+ if ((v = ossl_ml_dsa_params_get(evp_type)) == NULL
+ || (codec = ml_dsa_get_codec(evp_type)) == NULL)
+ return 0;
+
+ /* Extract the key OID and any parameters. */
+ if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL)
+ return 0;
+ /* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */
+ if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf))
+ goto end;
+ /* Bail out early if this is some other key type. */
+ if (OBJ_obj2nid(alg->algorithm) != evp_type)
+ goto end;
+
+ /* Get the list of enabled decoders. Their order is not important here. */
+ formats = ossl_prov_ctx_get_param(
+ provctx, OSSL_PKEY_PARAM_ML_DSA_INPUT_FORMATS, NULL);
+ fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->alg, codec->p8fmt,
+ "input", formats);
+ if (fmt_slots == NULL)
+ goto end;
+
+ /* Parameters must be absent. */
+ X509_ALGOR_get0(NULL, &ptype, NULL, alg);
+ if (ptype != V_ASN1_UNDEF) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS,
+ "unexpected parameters with a PKCS#8 %s private key",
+ v->alg);
+ goto end;
+ }
+ if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic))
+ goto end;
+
+ /* Find the matching p8 info slot, that also has the expected length. */
+ pos = OPENSSL_load_u32_be(&magic, buf);
+ for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) {
+ if (len != (ossl_ssize_t)p8fmt->p8_bytes)
+ continue;
+ if (p8fmt->p8_shift == sizeof(magic)
+ || (magic >> (p8fmt->p8_shift * 8)) == p8fmt->p8_magic) {
+ pos -= p8fmt->p8_shift;
+ break;
+ }
+ }
+ if (p8fmt == NULL
+ || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_DSA_SEED_BYTES)
+ || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->sk_len)
+ || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pk_len)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT,
+ "no matching enabled %s private key input formats",
+ v->alg);
+ goto end;
+ }
+
+ if (p8fmt->seed_length > 0) {
+ /* Check |seed| tag/len, if not subsumed by |magic|. */
+ if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) {
+ pos = OPENSSL_load_u16_be(&seed_magic, pos);
+ if (seed_magic != p8fmt->seed_magic)
+ goto end;
+ } else if (pos != buf + p8fmt->seed_offset) {
+ goto end;
+ }
+ pos += ML_DSA_SEED_BYTES;
+ }
+ if (p8fmt->priv_length > 0) {
+ /* Check |priv| tag/len */
+ if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) {
+ pos = OPENSSL_load_u32_be(&magic, pos);
+ if (magic != p8fmt->priv_magic)
+ goto end;
+ } else if (pos != buf + p8fmt->priv_offset) {
+ goto end;
+ }
+ pos += v->sk_len;
+ }
+ if (p8fmt->pub_length > 0) {
+ if (pos != buf + p8fmt->pub_offset)
+ goto end;
+ pos += v->pk_len;
+ }
+ if (pos != buf + len)
+ goto end;
+
+ /*
+ * Collect the seed and/or key into a "decoded" private key object,
+ * to be turned into a real key on provider "load" or "import".
+ */
+ if ((key = ossl_prov_ml_dsa_new(provctx, propq, evp_type)) == NULL)
+ goto end;
+ if (p8fmt->seed_length > 0)
+ seed = buf + p8fmt->seed_offset;
+ if (p8fmt->priv_length > 0)
+ priv = buf + p8fmt->priv_offset;
+ /* Any OQS public key content is ignored */
+
+ if (ossl_ml_dsa_set_prekey(key, 0, 0,
+ seed, ML_DSA_SEED_BYTES, priv, v->sk_len))
+ ret = key;
+
+ end:
+ OPENSSL_free(fmt_slots);
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ if (ret == NULL)
+ ossl_ml_dsa_key_free(key);
+ return ret;
+}
+
+/* Same as ossl_ml_dsa_encode_pubkey, but allocates the output buffer. */
+int ossl_ml_dsa_i2d_pubkey(const ML_DSA_KEY *key, unsigned char **out)
+{
+ const ML_DSA_PARAMS *params = ossl_ml_dsa_key_params(key);
+ const uint8_t *pk = ossl_ml_dsa_key_get_pub(key);
+
+ if (pk == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY,
+ "no %s public key data available", params->alg);
+ return 0;
+ }
+ if (out != NULL
+ && (*out = OPENSSL_memdup(pk, params->pk_len)) == NULL)
+ return 0;
+ return (int)params->pk_len;
+}
+
+/* Allocate and encode PKCS#8 private key payload. */
+int ossl_ml_dsa_i2d_prvkey(const ML_DSA_KEY *key, uint8_t **out,
+ PROV_CTX *provctx)
+{
+ const ML_DSA_PARAMS *params = ossl_ml_dsa_key_params(key);
+ const ML_COMMON_CODEC *codec;
+ ML_COMMON_PKCS8_FMT_PREF *fmt_slots, *slot;
+ const ML_COMMON_PKCS8_FMT *p8fmt;
+ uint8_t *buf = NULL, *pos;
+ const char *formats;
+ int len = ML_DSA_SEED_BYTES;
+ int ret = 0;
+ const uint8_t *seed = ossl_ml_dsa_key_get_seed(key);
+ const uint8_t *sk = ossl_ml_dsa_key_get_priv(key);
+
+ /* Not ours to handle */
+ if ((codec = ml_dsa_get_codec(params->evp_type)) == NULL)
+ return 0;
+
+ if (sk == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY,
+ "no %s private key data available",
+ params->alg);
+ return 0;
+ }
+
+ formats = ossl_prov_ctx_get_param(
+ provctx, OSSL_PKEY_PARAM_ML_DSA_OUTPUT_FORMATS, NULL);
+ fmt_slots = ossl_ml_common_pkcs8_fmt_order(params->alg, codec->p8fmt,
+ "output", formats);
+ if (fmt_slots == NULL)
+ return 0;
+
+ /* If we don't have a seed, skip seedful entries */
+ for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot)
+ if (seed != NULL || p8fmt->seed_length == 0)
+ break;
+ /* No matching table entries, give up */
+ if (p8fmt == NULL
+ || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_DSA_SEED_BYTES)
+ || (p8fmt->priv_length > 0 && p8fmt->priv_length != params->sk_len)
+ || (p8fmt->pub_length > 0 && p8fmt->pub_length != params->pk_len)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_DSA_NO_FORMAT,
+ "no matching enabled %s private key output formats",
+ params->alg);
+ goto end;
+ }
+ len = p8fmt->p8_bytes;
+
+ if (out == NULL) {
+ ret = len;
+ goto end;
+ }
+
+ if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL)
+ goto end;
+
+ switch (p8fmt->p8_shift) {
+ case 0:
+ pos = OPENSSL_store_u32_be(pos, p8fmt->p8_magic);
+ break;
+ case 2:
+ pos = OPENSSL_store_u16_be(pos, (uint16_t)p8fmt->p8_magic);
+ break;
+ case 4:
+ break;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key", params->alg);
+ goto end;
+ }
+
+ if (p8fmt->seed_length != 0) {
+ /*
+ * Either the tag/len were already included in |magic| or they require
+ * us to write two bytes now.
+ */
+ if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset)
+ pos = OPENSSL_store_u16_be(pos, p8fmt->seed_magic);
+ if (pos != buf + p8fmt->seed_offset) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key", params->alg);
+ goto end;
+ }
+ memcpy(pos, seed, ML_DSA_SEED_BYTES);
+ pos += ML_DSA_SEED_BYTES;
+ }
+ if (p8fmt->priv_length != 0) {
+ if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset)
+ pos = OPENSSL_store_u32_be(pos, p8fmt->priv_magic);
+ if (pos != buf + p8fmt->priv_offset) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key", params->alg);
+ goto end;
+ }
+ memcpy(pos, sk, params->sk_len);
+ pos += params->sk_len;
+ }
+ /* OQS form output with tacked-on public key */
+ if (p8fmt->pub_length != 0) {
+ /* The OQS pubkey is never separately DER-wrapped */
+ if (pos != buf + p8fmt->pub_offset) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key", params->alg);
+ goto end;
+ }
+ memcpy(pos, ossl_ml_dsa_key_get_pub(key), params->pk_len);
+ pos += params->pk_len;
+ }
+
+ if (pos == buf + len) {
+ *out = buf;
+ ret = len;
+ }
+
+ end:
+ OPENSSL_free(fmt_slots);
+ if (ret == 0)
+ OPENSSL_free(buf);
+ return ret;
+}
+
+int ossl_ml_dsa_key_to_text(BIO *out, const ML_DSA_KEY *key, int selection)
+{
+ const ML_DSA_PARAMS *params;
+ const uint8_t *seed, *sk, *pk;
+
+ if (out == NULL || key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ params = ossl_ml_dsa_key_params(key);
+ pk = ossl_ml_dsa_key_get_pub(key);
+ sk = ossl_ml_dsa_key_get_priv(key);
+ seed = ossl_ml_dsa_key_get_seed(key);
+
+ if (pk == NULL) {
+ /* Regardless of the |selection|, there must be a public key */
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
+ "no %s key material available", params->alg);
+ return 0;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ if (sk == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
+ "no %s key material available", params->alg);
+ return 0;
+ }
+ if (BIO_printf(out, "%s Private-Key:\n", params->alg) <= 0)
+ return 0;
+ if (seed != NULL && !ossl_bio_print_labeled_buf(out, "seed:", seed,
+ ML_DSA_SEED_BYTES))
+ return 0;
+ if (!ossl_bio_print_labeled_buf(out, "priv:", sk, params->sk_len))
+ return 0;
+ } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ if (BIO_printf(out, "%s Public-Key:\n", params->alg) <= 0)
+ return 0;
+ }
+
+ if (!ossl_bio_print_labeled_buf(out, "pub:", pk, params->pk_len))
+ return 0;
+
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h
new file mode 100644
index 000000000000..c0c2e842a242
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_dsa_codecs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef PROV_ML_DSA_CODECS_H
+# define PROV_ML_DSA_CODECS_H
+# pragma once
+
+# ifndef OPENSSL_NO_ML_DSA
+# include <openssl/e_os2.h>
+# include "crypto/ml_dsa.h"
+# include "prov/provider_ctx.h"
+# include "ml_common_codecs.h"
+
+__owur
+ML_DSA_KEY *ossl_ml_dsa_d2i_PUBKEY(const uint8_t *pubenc, int publen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq);
+__owur
+ML_DSA_KEY *ossl_ml_dsa_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq);
+__owur
+int ossl_ml_dsa_key_to_text(BIO *out, const ML_DSA_KEY *key, int selection);
+__owur
+__owur
+int ossl_ml_dsa_i2d_pubkey(const ML_DSA_KEY *key, unsigned char **out);
+__owur
+__owur
+int ossl_ml_dsa_i2d_prvkey(const ML_DSA_KEY *key, unsigned char **out,
+ PROV_CTX *provctx);
+
+# endif /* OPENSSL_NO_ML_DSA */
+#endif /* PROV_ML_DSA_CODECS_H */
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c
new file mode 100644
index 000000000000..fe0c8acc7e63
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.c
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/byteorder.h>
+#include <openssl/proverr.h>
+#include <openssl/x509.h>
+#include <openssl/core_names.h>
+#include "internal/encoder.h"
+#include "prov/ml_kem.h"
+#include "ml_kem_codecs.h"
+
+/* Tables describing supported ASN.1 input/output formats. */
+
+/*-
+ * ML-KEM-512:
+ * Public key bytes: 800 (0x0320)
+ * Private key bytes: 1632 (0x0660)
+ */
+static const ML_COMMON_SPKI_FMT ml_kem_512_spkifmt = {
+ { 0x30, 0x82, 0x03, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x04, 0x01, 0x03, 0x82, 0x03, 0x21, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_kem_512_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x06aa, 0, 0x308206a6, 0x0440, 6, 0x40, 0x04820660, 0x4a, 0x0660, 0, 0 },
+ { "priv-only", 0x0664, 0, 0x04820660, 0, 0, 0, 0, 0x04, 0x0660, 0, 0 },
+ { "oqskeypair", 0x0984, 0, 0x04820980, 0, 0, 0, 0, 0x04, 0x0660, 0x0664, 0x0320 },
+ { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0 },
+ { "bare-priv", 0x0660, 4, 0, 0, 0, 0, 0, 0, 0x0660, 0, 0 },
+ { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 },
+};
+
+/*-
+ * ML-KEM-768:
+ * Public key bytes: 1184 (0x04a0)
+ * Private key bytes: 2400 (0x0960)
+ */
+static const ML_COMMON_SPKI_FMT ml_kem_768_spkifmt = {
+ { 0x30, 0x82, 0x04, 0xb2, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x04, 0x02, 0x03, 0x82, 0x04, 0xa1, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_kem_768_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x09aa, 0, 0x308209a6, 0x0440, 6, 0x40, 0x04820960, 0x4a, 0x0960, 0, 0, },
+ { "priv-only", 0x0964, 0, 0x04820960, 0, 0, 0, 0, 0x04, 0x0960, 0, 0, },
+ { "oqskeypair", 0x0e04, 0, 0x04820e00, 0, 0, 0, 0, 0x04, 0x0960, 0x0964, 0x04a0 },
+ { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0, },
+ { "bare-priv", 0x0960, 4, 0, 0, 0, 0, 0, 0, 0x0960, 0, 0, },
+ { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0, },
+};
+
+/*-
+ * ML-KEM-1024:
+ * Private key bytes: 3168 (0x0c60)
+ * Public key bytes: 1568 (0x0620)
+ */
+static const ML_COMMON_SPKI_FMT ml_kem_1024_spkifmt = {
+ { 0x30, 0x82, 0x06, 0x32, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x04, 0x03, 0x03, 0x82, 0x06, 0x21, 0x00, }
+};
+static const ML_COMMON_PKCS8_FMT ml_kem_1024_p8fmt[NUM_PKCS8_FORMATS] = {
+ { "seed-priv", 0x0caa, 0, 0x30820ca6, 0x0440, 6, 0x40, 0x04820c60, 0x4a, 0x0c60, 0, 0 },
+ { "priv-only", 0x0c64, 0, 0x04820c60, 0, 0, 0, 0, 0x04, 0x0c60, 0, 0 },
+ { "oqskeypair", 0x1284, 0, 0x04821280, 0, 0, 0, 0, 0x04, 0x0c60, 0x0c64, 0x0620 },
+ { "seed-only", 0x0042, 2, 0x8040, 0, 2, 0x40, 0, 0, 0, 0, 0 },
+ { "bare-priv", 0x0c60, 4, 0, 0, 0, 0, 0, 0, 0x0c60, 0, 0 },
+ { "bare-seed", 0x0040, 4, 0, 0, 0, 0x40, 0, 0, 0, 0, 0 },
+};
+
+/* Indices of slots in the `codecs` table below */
+#define ML_KEM_512_CODEC 0
+#define ML_KEM_768_CODEC 1
+#define ML_KEM_1024_CODEC 2
+
+/*
+ * Per-variant fixed parameters
+ */
+static const ML_COMMON_CODEC codecs[3] = {
+ { &ml_kem_512_spkifmt, ml_kem_512_p8fmt },
+ { &ml_kem_768_spkifmt, ml_kem_768_p8fmt },
+ { &ml_kem_1024_spkifmt, ml_kem_1024_p8fmt }
+};
+
+/* Retrieve the parameters of one of the ML-KEM variants */
+static const ML_COMMON_CODEC *ml_kem_get_codec(int evp_type)
+{
+ switch (evp_type) {
+ case EVP_PKEY_ML_KEM_512:
+ return &codecs[ML_KEM_512_CODEC];
+ case EVP_PKEY_ML_KEM_768:
+ return &codecs[ML_KEM_768_CODEC];
+ case EVP_PKEY_ML_KEM_1024:
+ return &codecs[ML_KEM_1024_CODEC];
+ }
+ return NULL;
+}
+
+ML_KEM_KEY *
+ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen, int evp_type,
+ PROV_CTX *provctx, const char *propq)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ const ML_KEM_VINFO *v;
+ const ML_COMMON_CODEC *codec;
+ const ML_COMMON_SPKI_FMT *vspki;
+ ML_KEM_KEY *ret;
+
+ if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL
+ || (codec = ml_kem_get_codec(evp_type)) == NULL)
+ return NULL;
+ vspki = codec->spkifmt;
+ if (publen != ML_COMMON_SPKI_OVERHEAD + (ossl_ssize_t) v->pubkey_bytes
+ || memcmp(pubenc, vspki->asn1_prefix, ML_COMMON_SPKI_OVERHEAD) != 0)
+ return NULL;
+ publen -= ML_COMMON_SPKI_OVERHEAD;
+ pubenc += ML_COMMON_SPKI_OVERHEAD;
+
+ if ((ret = ossl_ml_kem_key_new(libctx, propq, evp_type)) == NULL)
+ return NULL;
+
+ if (!ossl_ml_kem_parse_public_key(pubenc, (size_t) publen, ret)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_ENCODING,
+ "errror parsing %s public key from input SPKI",
+ v->algorithm_name);
+ ossl_ml_kem_key_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+ML_KEM_KEY *
+ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq)
+{
+ const ML_KEM_VINFO *v;
+ const ML_COMMON_CODEC *codec;
+ ML_COMMON_PKCS8_FMT_PREF *fmt_slots = NULL, *slot;
+ const ML_COMMON_PKCS8_FMT *p8fmt;
+ ML_KEM_KEY *key = NULL, *ret = NULL;
+ PKCS8_PRIV_KEY_INFO *p8inf = NULL;
+ const uint8_t *buf, *pos;
+ const X509_ALGOR *alg = NULL;
+ const char *formats;
+ int len, ptype;
+ uint32_t magic;
+ uint16_t seed_magic;
+
+ /* Which ML-KEM variant? */
+ if ((v = ossl_ml_kem_get_vinfo(evp_type)) == NULL
+ || (codec = ml_kem_get_codec(evp_type)) == NULL)
+ return 0;
+
+ /* Extract the key OID and any parameters. */
+ if ((p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &prvenc, prvlen)) == NULL)
+ return 0;
+ /* Shortest prefix is 4 bytes: seq tag/len + octet string tag/len */
+ if (!PKCS8_pkey_get0(NULL, &buf, &len, &alg, p8inf))
+ goto end;
+ /* Bail out early if this is some other key type. */
+ if (OBJ_obj2nid(alg->algorithm) != evp_type)
+ goto end;
+
+ /* Get the list of enabled decoders. Their order is not important here. */
+ formats = ossl_prov_ctx_get_param(
+ provctx, OSSL_PKEY_PARAM_ML_KEM_INPUT_FORMATS, NULL);
+ fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->algorithm_name, codec->p8fmt,
+ "input", formats);
+ if (fmt_slots == NULL)
+ goto end;
+
+ /* Parameters must be absent. */
+ X509_ALGOR_get0(NULL, &ptype, NULL, alg);
+ if (ptype != V_ASN1_UNDEF) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_UNEXPECTED_KEY_PARAMETERS,
+ "unexpected parameters with a PKCS#8 %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+ if ((ossl_ssize_t)len < (ossl_ssize_t)sizeof(magic))
+ goto end;
+
+ /* Find the matching p8 info slot, that also has the expected length. */
+ pos = OPENSSL_load_u32_be(&magic, buf);
+ for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot) {
+ if (len != (ossl_ssize_t)p8fmt->p8_bytes)
+ continue;
+ if (p8fmt->p8_shift == sizeof(magic)
+ || (magic >> (p8fmt->p8_shift * 8)) == p8fmt->p8_magic) {
+ pos -= p8fmt->p8_shift;
+ break;
+ }
+ }
+ if (p8fmt == NULL
+ || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_KEM_SEED_BYTES)
+ || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->prvkey_bytes)
+ || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pubkey_bytes)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
+ "no matching enabled %s private key input formats",
+ v->algorithm_name);
+ goto end;
+ }
+
+ if (p8fmt->seed_length > 0) {
+ /* Check |seed| tag/len, if not subsumed by |magic|. */
+ if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset) {
+ pos = OPENSSL_load_u16_be(&seed_magic, pos);
+ if (seed_magic != p8fmt->seed_magic)
+ goto end;
+ } else if (pos != buf + p8fmt->seed_offset) {
+ goto end;
+ }
+ pos += ML_KEM_SEED_BYTES;
+ }
+ if (p8fmt->priv_length > 0) {
+ /* Check |priv| tag/len */
+ if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset) {
+ pos = OPENSSL_load_u32_be(&magic, pos);
+ if (magic != p8fmt->priv_magic)
+ goto end;
+ } else if (pos != buf + p8fmt->priv_offset) {
+ goto end;
+ }
+ pos += v->prvkey_bytes;
+ }
+ if (p8fmt->pub_length > 0) {
+ if (pos != buf + p8fmt->pub_offset)
+ goto end;
+ pos += v->pubkey_bytes;
+ }
+ if (pos != buf + len)
+ goto end;
+
+ /*
+ * Collect the seed and/or key into a "decoded" private key object,
+ * to be turned into a real key on provider "load" or "import".
+ */
+ if ((key = ossl_prov_ml_kem_new(provctx, propq, evp_type)) == NULL)
+ goto end;
+
+ if (p8fmt->seed_length > 0) {
+ if (!ossl_ml_kem_set_seed(buf + p8fmt->seed_offset,
+ ML_KEM_SEED_BYTES, key)) {
+ ERR_raise_data(ERR_LIB_OSSL_DECODER, ERR_R_INTERNAL_ERROR,
+ "error storing %s private key seed",
+ v->algorithm_name);
+ goto end;
+ }
+ }
+ if (p8fmt->priv_length > 0) {
+ if ((key->encoded_dk = OPENSSL_malloc(p8fmt->priv_length)) == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "error parsing %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+ memcpy(key->encoded_dk, buf + p8fmt->priv_offset, p8fmt->priv_length);
+ }
+ /* Any OQS public key content is ignored */
+ ret = key;
+
+ end:
+ OPENSSL_free(fmt_slots);
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ if (ret == NULL)
+ ossl_ml_kem_key_free(key);
+ return ret;
+}
+
+/* Same as ossl_ml_kem_encode_pubkey, but allocates the output buffer. */
+int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out)
+{
+ size_t publen;
+
+ if (!ossl_ml_kem_have_pubkey(key)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY,
+ "no %s public key data available",
+ key->vinfo->algorithm_name);
+ return 0;
+ }
+ publen = key->vinfo->pubkey_bytes;
+
+ if (out != NULL
+ && (*out = OPENSSL_malloc(publen)) == NULL)
+ return 0;
+ if (!ossl_ml_kem_encode_public_key(*out, publen, key)) {
+ ERR_raise_data(ERR_LIB_OSSL_ENCODER, ERR_R_INTERNAL_ERROR,
+ "error encoding %s public key",
+ key->vinfo->algorithm_name);
+ OPENSSL_free(*out);
+ return 0;
+ }
+
+ return (int)publen;
+}
+
+/* Allocate and encode PKCS#8 private key payload. */
+int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, uint8_t **out,
+ PROV_CTX *provctx)
+{
+ const ML_KEM_VINFO *v = key->vinfo;
+ const ML_COMMON_CODEC *codec;
+ ML_COMMON_PKCS8_FMT_PREF *fmt_slots, *slot;
+ const ML_COMMON_PKCS8_FMT *p8fmt;
+ uint8_t *buf = NULL, *pos;
+ const char *formats;
+ int len = ML_KEM_SEED_BYTES;
+ int ret = 0;
+
+ /* Not ours to handle */
+ if ((codec = ml_kem_get_codec(v->evp_type)) == NULL)
+ return 0;
+
+ if (!ossl_ml_kem_have_prvkey(key)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY,
+ "no %s private key data available",
+ key->vinfo->algorithm_name);
+ return 0;
+ }
+
+ formats = ossl_prov_ctx_get_param(
+ provctx, OSSL_PKEY_PARAM_ML_KEM_OUTPUT_FORMATS, NULL);
+ fmt_slots = ossl_ml_common_pkcs8_fmt_order(v->algorithm_name, codec->p8fmt,
+ "output", formats);
+ if (fmt_slots == NULL)
+ return 0;
+
+ /* If we don't have a seed, skip seedful entries */
+ for (slot = fmt_slots; (p8fmt = slot->fmt) != NULL; ++slot)
+ if (ossl_ml_kem_have_seed(key) || p8fmt->seed_length == 0)
+ break;
+ /* No matching table entries, give up */
+ if (p8fmt == NULL
+ || (p8fmt->seed_length > 0 && p8fmt->seed_length != ML_KEM_SEED_BYTES)
+ || (p8fmt->priv_length > 0 && p8fmt->priv_length != v->prvkey_bytes)
+ || (p8fmt->pub_length > 0 && p8fmt->pub_length != v->pubkey_bytes)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_ML_KEM_NO_FORMAT,
+ "no matching enabled %s private key output formats",
+ v->algorithm_name);
+ goto end;
+ }
+ len = p8fmt->p8_bytes;
+
+ if (out == NULL) {
+ ret = len;
+ goto end;
+ }
+
+ if ((pos = buf = OPENSSL_malloc((size_t) len)) == NULL)
+ goto end;
+
+ switch (p8fmt->p8_shift) {
+ case 0:
+ pos = OPENSSL_store_u32_be(pos, p8fmt->p8_magic);
+ break;
+ case 2:
+ pos = OPENSSL_store_u16_be(pos, (uint16_t)p8fmt->p8_magic);
+ break;
+ case 4:
+ break;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+
+ if (p8fmt->seed_length != 0) {
+ /*
+ * Either the tag/len were already included in |magic| or they require
+ * us to write two bytes now.
+ */
+ if (pos + sizeof(uint16_t) == buf + p8fmt->seed_offset)
+ pos = OPENSSL_store_u16_be(pos, p8fmt->seed_magic);
+ if (pos != buf + p8fmt->seed_offset
+ || !ossl_ml_kem_encode_seed(pos, ML_KEM_SEED_BYTES, key)) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+ pos += ML_KEM_SEED_BYTES;
+ }
+ if (p8fmt->priv_length != 0) {
+ if (pos + sizeof(uint32_t) == buf + p8fmt->priv_offset)
+ pos = OPENSSL_store_u32_be(pos, p8fmt->priv_magic);
+ if (pos != buf + p8fmt->priv_offset
+ || !ossl_ml_kem_encode_private_key(pos, v->prvkey_bytes, key)) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+ pos += v->prvkey_bytes;
+ }
+ /* OQS form output with tacked-on public key */
+ if (p8fmt->pub_length != 0) {
+ /* The OQS pubkey is never separately DER-wrapped */
+ if (pos != buf + p8fmt->pub_offset
+ || !ossl_ml_kem_encode_public_key(pos, v->pubkey_bytes, key)) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "error encoding %s private key",
+ v->algorithm_name);
+ goto end;
+ }
+ pos += v->pubkey_bytes;
+ }
+
+ if (pos == buf + len) {
+ *out = buf;
+ ret = len;
+ }
+
+ end:
+ OPENSSL_free(fmt_slots);
+ if (ret == 0)
+ OPENSSL_free(buf);
+ return ret;
+}
+
+int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection)
+{
+ uint8_t seed[ML_KEM_SEED_BYTES], *prvenc = NULL, *pubenc = NULL;
+ size_t publen, prvlen;
+ const char *type_label = NULL;
+ int ret = 0;
+
+ if (out == NULL || key == NULL) {
+ ERR_raise(ERR_LIB_OSSL_ENCODER, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ type_label = key->vinfo->algorithm_name;
+ publen = key->vinfo->pubkey_bytes;
+ prvlen = key->vinfo->prvkey_bytes;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+ && (ossl_ml_kem_have_prvkey(key)
+ || ossl_ml_kem_have_seed(key))) {
+ if (BIO_printf(out, "%s Private-Key:\n", type_label) <= 0)
+ return 0;
+
+ if (ossl_ml_kem_have_seed(key)) {
+ if (!ossl_ml_kem_encode_seed(seed, sizeof(seed), key))
+ goto end;
+ if (!ossl_bio_print_labeled_buf(out, "seed:", seed, sizeof(seed)))
+ goto end;
+ }
+ if (ossl_ml_kem_have_prvkey(key)) {
+ if ((prvenc = OPENSSL_malloc(prvlen)) == NULL)
+ return 0;
+ if (!ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
+ goto end;
+ if (!ossl_bio_print_labeled_buf(out, "dk:", prvenc, prvlen))
+ goto end;
+ }
+ ret = 1;
+ }
+
+ /* The public key is output regardless of the selection */
+ if (ossl_ml_kem_have_pubkey(key)) {
+ /* If we did not output private key bits, this is a public key */
+ if (ret == 0 && BIO_printf(out, "%s Public-Key:\n", type_label) <= 0)
+ goto end;
+
+ if ((pubenc = OPENSSL_malloc(key->vinfo->pubkey_bytes)) == NULL
+ || !ossl_ml_kem_encode_public_key(pubenc, publen, key)
+ || !ossl_bio_print_labeled_buf(out, "ek:", pubenc, publen))
+ goto end;
+ ret = 1;
+ }
+
+ /* If we got here, and ret == 0, there was no key material */
+ if (ret == 0)
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_KEY,
+ "no %s key material available",
+ type_label);
+
+ end:
+ OPENSSL_free(pubenc);
+ OPENSSL_free(prvenc);
+ return ret;
+}
diff --git a/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h
new file mode 100644
index 000000000000..b8a22201ab17
--- /dev/null
+++ b/crypto/openssl/providers/implementations/encode_decode/ml_kem_codecs.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef PROV_ML_KEM_CODECS_H
+# define PROV_ML_KEM_CODECS_H
+# pragma once
+
+# ifndef OPENSSL_NO_ML_KEM
+# include <openssl/e_os2.h>
+# include "crypto/ml_kem.h"
+# include "prov/provider_ctx.h"
+# include "ml_common_codecs.h"
+
+__owur
+ML_KEM_KEY *ossl_ml_kem_d2i_PUBKEY(const uint8_t *pubenc, int publen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq);
+__owur
+ML_KEM_KEY *ossl_ml_kem_d2i_PKCS8(const uint8_t *prvenc, int prvlen,
+ int evp_type, PROV_CTX *provctx,
+ const char *propq);
+__owur
+int ossl_ml_kem_key_to_text(BIO *out, const ML_KEM_KEY *key, int selection);
+__owur
+__owur
+int ossl_ml_kem_i2d_pubkey(const ML_KEM_KEY *key, unsigned char **out);
+__owur
+__owur
+int ossl_ml_kem_i2d_prvkey(const ML_KEM_KEY *key, unsigned char **out,
+ PROV_CTX *provctx);
+
+# endif /* OPENSSL_NO_ML_KEM */
+#endif /* PROV_ML_KEM_CODECS_H */
diff --git a/crypto/openssl/providers/implementations/exchange/build.info b/crypto/openssl/providers/implementations/exchange/build.info
new file mode 100644
index 000000000000..0333c9f49c2c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/exchange/build.info
@@ -0,0 +1,31 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$DH_GOAL=../../libdefault.a ../../libfips.a
+$ECDH_GOAL=../../libdefault.a ../../libfips.a
+$ECX_GOAL=../../libdefault.a ../../libfips.a
+$KDF_GOAL=../../libdefault.a ../../libfips.a
+
+IF[{- !$disabled{dh} -}]
+ SOURCE[$DH_GOAL]=dh_exch.c
+ENDIF
+
+IF[{- !$disabled{asm} -}]
+ $ECDEF_s390x=S390X_EC_ASM
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
+ IF[$ECASM_{- $target{asm_arch} -}]
+ $ECDEF=$ECDEF_{- $target{asm_arch} -}
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{ec} -}]
+ IF[{- !$disabled{ecx} -}]
+ SOURCE[$ECX_GOAL]=ecx_exch.c
+ DEFINE[$ECX_GOAL]=$ECDEF
+ ENDIF
+ SOURCE[$ECDH_GOAL]=ecdh_exch.c
+ENDIF
+
+SOURCE[$KDF_GOAL]=kdf_exch.c
diff --git a/crypto/openssl/providers/implementations/exchange/dh_exch.c b/crypto/openssl/providers/implementations/exchange/dh_exch.c
new file mode 100644
index 000000000000..cfb3938810b8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/exchange/dh_exch.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DH low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/dh.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/params.h>
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+#include "crypto/dh.h"
+
+static OSSL_FUNC_keyexch_newctx_fn dh_newctx;
+static OSSL_FUNC_keyexch_init_fn dh_init;
+static OSSL_FUNC_keyexch_set_peer_fn dh_set_peer;
+static OSSL_FUNC_keyexch_derive_fn dh_derive;
+static OSSL_FUNC_keyexch_freectx_fn dh_freectx;
+static OSSL_FUNC_keyexch_dupctx_fn dh_dupctx;
+static OSSL_FUNC_keyexch_set_ctx_params_fn dh_set_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn dh_settable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn dh_get_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn dh_gettable_ctx_params;
+
+/*
+ * This type is only really used to handle some legacy related functionality.
+ * If you need to use other KDF's (such as SSKDF) just use PROV_DH_KDF_NONE
+ * here and then create and run a KDF after the key is derived.
+ * Note that X942 has 2 variants of key derivation:
+ * (1) DH_KDF_X9_42_ASN1 - which contains an ANS1 encoded object that has
+ * the counter embedded in it.
+ * (2) DH_KDF_X941_CONCAT - which is the same as ECDH_X963_KDF (which can be
+ * done by creating a "X963KDF".
+ */
+enum kdf_type {
+ PROV_DH_KDF_NONE = 0,
+ PROV_DH_KDF_X9_42_ASN1
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes DH structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ DH *dh;
+ DH *dhpeer;
+ unsigned int pad : 1;
+
+ /* DH KDF */
+ /* KDF (if any) to use for DH */
+ enum kdf_type kdf_type;
+ /* Message digest to use for key derivation */
+ EVP_MD *kdf_md;
+ /* User key material */
+ unsigned char *kdf_ukm;
+ size_t kdf_ukmlen;
+ /* KDF output length */
+ size_t kdf_outlen;
+ char *kdf_cekalg;
+ OSSL_FIPS_IND_DECLARE
+} PROV_DH_CTX;
+
+static void *dh_newctx(void *provctx)
+{
+ PROV_DH_CTX *pdhctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ pdhctx = OPENSSL_zalloc(sizeof(PROV_DH_CTX));
+ if (pdhctx == NULL)
+ return NULL;
+ OSSL_FIPS_IND_INIT(pdhctx)
+ pdhctx->libctx = PROV_LIBCTX_OF(provctx);
+ pdhctx->kdf_type = PROV_DH_KDF_NONE;
+ return pdhctx;
+}
+
+#ifdef FIPS_MODULE
+static int dh_check_key(PROV_DH_CTX *ctx)
+{
+ int key_approved = ossl_dh_check_key(ctx->dh);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ ctx->libctx, "DH Init", "DH Key",
+ ossl_fips_config_securitycheck_enabled)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int digest_check(PROV_DH_CTX *ctx, const EVP_MD *md)
+{
+ return ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE1, ctx->libctx,
+ md, "DH Set Ctx");
+}
+#endif
+
+static int dh_init(void *vpdhctx, void *vdh, const OSSL_PARAM params[])
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+
+ if (!ossl_prov_is_running()
+ || pdhctx == NULL
+ || vdh == NULL
+ || !DH_up_ref(vdh))
+ return 0;
+ DH_free(pdhctx->dh);
+ pdhctx->dh = vdh;
+ pdhctx->kdf_type = PROV_DH_KDF_NONE;
+
+ OSSL_FIPS_IND_SET_APPROVED(pdhctx)
+ if (!dh_set_ctx_params(pdhctx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!dh_check_key(pdhctx))
+ return 0;
+#endif
+ return 1;
+}
+
+/* The 2 parties must share the same domain parameters */
+static int dh_match_params(DH *priv, DH *peer)
+{
+ int ret;
+ FFC_PARAMS *dhparams_priv = ossl_dh_get0_params(priv);
+ FFC_PARAMS *dhparams_peer = ossl_dh_get0_params(peer);
+
+ ret = dhparams_priv != NULL
+ && dhparams_peer != NULL
+ && ossl_ffc_params_cmp(dhparams_priv, dhparams_peer, 1);
+ if (!ret)
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
+ return ret;
+}
+
+static int dh_set_peer(void *vpdhctx, void *vdh)
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+
+ if (!ossl_prov_is_running()
+ || pdhctx == NULL
+ || vdh == NULL
+ || !dh_match_params(vdh, pdhctx->dh)
+ || !DH_up_ref(vdh))
+ return 0;
+ DH_free(pdhctx->dhpeer);
+ pdhctx->dhpeer = vdh;
+ return 1;
+}
+
+static int dh_plain_derive(void *vpdhctx,
+ unsigned char *secret, size_t *secretlen,
+ size_t outlen, unsigned int pad)
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+ int ret;
+ size_t dhsize;
+ const BIGNUM *pub_key = NULL;
+
+ if (pdhctx->dh == NULL || pdhctx->dhpeer == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ dhsize = (size_t)DH_size(pdhctx->dh);
+ if (secret == NULL) {
+ *secretlen = dhsize;
+ return 1;
+ }
+ if (outlen < dhsize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ DH_get0_key(pdhctx->dhpeer, &pub_key, NULL);
+ if (pad)
+ ret = DH_compute_key_padded(secret, pub_key, pdhctx->dh);
+ else
+ ret = DH_compute_key(secret, pub_key, pdhctx->dh);
+ if (ret <= 0)
+ return 0;
+
+ *secretlen = ret;
+ return 1;
+}
+
+static int dh_X9_42_kdf_derive(void *vpdhctx, unsigned char *secret,
+ size_t *secretlen, size_t outlen)
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+ unsigned char *stmp = NULL;
+ size_t stmplen;
+ int ret = 0;
+
+ if (secret == NULL) {
+ *secretlen = pdhctx->kdf_outlen;
+ return 1;
+ }
+
+ if (pdhctx->kdf_outlen > outlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!dh_plain_derive(pdhctx, NULL, &stmplen, 0, 1))
+ return 0;
+ if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)
+ return 0;
+ if (!dh_plain_derive(pdhctx, stmp, &stmplen, stmplen, 1))
+ goto err;
+
+ /* Do KDF stuff */
+ if (pdhctx->kdf_type == PROV_DH_KDF_X9_42_ASN1) {
+ if (!ossl_dh_kdf_X9_42_asn1(secret, pdhctx->kdf_outlen,
+ stmp, stmplen,
+ pdhctx->kdf_cekalg,
+ pdhctx->kdf_ukm,
+ pdhctx->kdf_ukmlen,
+ pdhctx->kdf_md,
+ pdhctx->libctx, NULL))
+ goto err;
+ }
+ *secretlen = pdhctx->kdf_outlen;
+ ret = 1;
+err:
+ OPENSSL_secure_clear_free(stmp, stmplen);
+ return ret;
+}
+
+static int dh_derive(void *vpdhctx, unsigned char *secret,
+ size_t *psecretlen, size_t outlen)
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ switch (pdhctx->kdf_type) {
+ case PROV_DH_KDF_NONE:
+ return dh_plain_derive(pdhctx, secret, psecretlen, outlen,
+ pdhctx->pad);
+ case PROV_DH_KDF_X9_42_ASN1:
+ return dh_X9_42_kdf_derive(pdhctx, secret, psecretlen, outlen);
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void dh_freectx(void *vpdhctx)
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+
+ OPENSSL_free(pdhctx->kdf_cekalg);
+ DH_free(pdhctx->dh);
+ DH_free(pdhctx->dhpeer);
+ EVP_MD_free(pdhctx->kdf_md);
+ OPENSSL_clear_free(pdhctx->kdf_ukm, pdhctx->kdf_ukmlen);
+
+ OPENSSL_free(pdhctx);
+}
+
+static void *dh_dupctx(void *vpdhctx)
+{
+ PROV_DH_CTX *srcctx = (PROV_DH_CTX *)vpdhctx;
+ PROV_DH_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->dh = NULL;
+ dstctx->dhpeer = NULL;
+ dstctx->kdf_md = NULL;
+ dstctx->kdf_ukm = NULL;
+ dstctx->kdf_cekalg = NULL;
+
+ if (srcctx->dh != NULL && !DH_up_ref(srcctx->dh))
+ goto err;
+ else
+ dstctx->dh = srcctx->dh;
+
+ if (srcctx->dhpeer != NULL && !DH_up_ref(srcctx->dhpeer))
+ goto err;
+ else
+ dstctx->dhpeer = srcctx->dhpeer;
+
+ if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
+ goto err;
+ else
+ dstctx->kdf_md = srcctx->kdf_md;
+
+ /* Duplicate UKM data if present */
+ if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
+ dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
+ srcctx->kdf_ukmlen);
+ if (dstctx->kdf_ukm == NULL)
+ goto err;
+ }
+
+ if (srcctx->kdf_cekalg != NULL) {
+ dstctx->kdf_cekalg = OPENSSL_strdup(srcctx->kdf_cekalg);
+ if (dstctx->kdf_cekalg == NULL)
+ goto err;
+ }
+
+ return dstctx;
+err:
+ dh_freectx(dstctx);
+ return NULL;
+}
+
+static int dh_set_ctx_params(void *vpdhctx, const OSSL_PARAM params[])
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+ const OSSL_PARAM *p;
+ unsigned int pad;
+ char name[80] = { '\0' }; /* should be big enough */
+ char *str = NULL;
+
+ if (pdhctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdhctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdhctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+ if (p != NULL) {
+ str = name;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+ return 0;
+
+ if (name[0] == '\0')
+ pdhctx->kdf_type = PROV_DH_KDF_NONE;
+ else if (strcmp(name, OSSL_KDF_NAME_X942KDF_ASN1) == 0)
+ pdhctx->kdf_type = PROV_DH_KDF_X9_42_ASN1;
+ else
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+ if (p != NULL) {
+ char mdprops[80] = { '\0' }; /* should be big enough */
+
+ str = name;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+ return 0;
+
+ str = mdprops;
+ p = OSSL_PARAM_locate_const(params,
+ OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
+
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+ return 0;
+ }
+
+ EVP_MD_free(pdhctx->kdf_md);
+ pdhctx->kdf_md = EVP_MD_fetch(pdhctx->libctx, name, mdprops);
+ if (pdhctx->kdf_md == NULL)
+ return 0;
+ /* XOF digests are not allowed */
+ if (EVP_MD_xof(pdhctx->kdf_md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ if (!digest_check(pdhctx, pdhctx->kdf_md)) {
+ EVP_MD_free(pdhctx->kdf_md);
+ pdhctx->kdf_md = NULL;
+ return 0;
+ }
+#endif
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+ if (p != NULL) {
+ size_t outlen;
+
+ if (!OSSL_PARAM_get_size_t(p, &outlen))
+ return 0;
+ pdhctx->kdf_outlen = outlen;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+ if (p != NULL) {
+ void *tmp_ukm = NULL;
+ size_t tmp_ukmlen;
+
+ OPENSSL_free(pdhctx->kdf_ukm);
+ pdhctx->kdf_ukm = NULL;
+ pdhctx->kdf_ukmlen = 0;
+ /* ukm is an optional field so it can be NULL */
+ if (p->data != NULL && p->data_size != 0) {
+ if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
+ return 0;
+ pdhctx->kdf_ukm = tmp_ukm;
+ pdhctx->kdf_ukmlen = tmp_ukmlen;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_PAD);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_uint(p, &pad))
+ return 0;
+ pdhctx->pad = pad ? 1 : 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG);
+ if (p != NULL) {
+ str = name;
+
+ OPENSSL_free(pdhctx->kdf_cekalg);
+ pdhctx->kdf_cekalg = NULL;
+ if (p->data != NULL && p->data_size != 0) {
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+ return 0;
+ pdhctx->kdf_cekalg = OPENSSL_strdup(name);
+ if (pdhctx->kdf_cekalg == NULL)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_PAD, NULL),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dh_settable_ctx_params(ossl_unused void *vpdhctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+ OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
+ NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dh_gettable_ctx_params(ossl_unused void *vpdhctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int dh_get_ctx_params(void *vpdhctx, OSSL_PARAM params[])
+{
+ PROV_DH_CTX *pdhctx = (PROV_DH_CTX *)vpdhctx;
+ OSSL_PARAM *p;
+
+ if (pdhctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+ if (p != NULL) {
+ const char *kdf_type = NULL;
+
+ switch (pdhctx->kdf_type) {
+ case PROV_DH_KDF_NONE:
+ kdf_type = "";
+ break;
+ case PROV_DH_KDF_X9_42_ASN1:
+ kdf_type = OSSL_KDF_NAME_X942KDF_ASN1;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+ if (p != NULL
+ && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_md == NULL
+ ? ""
+ : EVP_MD_get0_name(pdhctx->kdf_md))) {
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, pdhctx->kdf_outlen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_ptr(p, pdhctx->kdf_ukm, pdhctx->kdf_ukmlen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_CEK_ALG);
+ if (p != NULL
+ && !OSSL_PARAM_set_utf8_string(p, pdhctx->kdf_cekalg == NULL
+ ? "" : pdhctx->kdf_cekalg))
+ return 0;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(pdhctx, params))
+ return 0;
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_dh_keyexch_functions[] = {
+ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))dh_newctx },
+ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))dh_init },
+ { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))dh_derive },
+ { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))dh_set_peer },
+ { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))dh_freectx },
+ { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))dh_dupctx },
+ { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))dh_set_ctx_params },
+ { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
+ (void (*)(void))dh_settable_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))dh_get_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+ (void (*)(void))dh_gettable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/exchange/ecdh_exch.c b/crypto/openssl/providers/implementations/exchange/ecdh_exch.c
new file mode 100644
index 000000000000..58fbc7bc09f0
--- /dev/null
+++ b/crypto/openssl/providers/implementations/exchange/ecdh_exch.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * ECDH low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/ec.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "crypto/ec.h" /* ossl_ecdh_kdf_X9_63() */
+
+static OSSL_FUNC_keyexch_newctx_fn ecdh_newctx;
+static OSSL_FUNC_keyexch_init_fn ecdh_init;
+static OSSL_FUNC_keyexch_set_peer_fn ecdh_set_peer;
+static OSSL_FUNC_keyexch_derive_fn ecdh_derive;
+static OSSL_FUNC_keyexch_freectx_fn ecdh_freectx;
+static OSSL_FUNC_keyexch_dupctx_fn ecdh_dupctx;
+static OSSL_FUNC_keyexch_set_ctx_params_fn ecdh_set_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn ecdh_settable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn ecdh_get_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecdh_gettable_ctx_params;
+
+enum kdf_type {
+ PROV_ECDH_KDF_NONE = 0,
+ PROV_ECDH_KDF_X9_63
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes EC_KEY structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+
+ EC_KEY *k;
+ EC_KEY *peerk;
+
+ /*
+ * ECDH cofactor mode:
+ *
+ * . 0 disabled
+ * . 1 enabled
+ * . -1 use cofactor mode set for k
+ */
+ int cofactor_mode;
+
+ /************
+ * ECDH KDF *
+ ************/
+ /* KDF (if any) to use for ECDH */
+ enum kdf_type kdf_type;
+ /* Message digest to use for key derivation */
+ EVP_MD *kdf_md;
+ /* User key material */
+ unsigned char *kdf_ukm;
+ size_t kdf_ukmlen;
+ /* KDF output length */
+ size_t kdf_outlen;
+ OSSL_FIPS_IND_DECLARE
+} PROV_ECDH_CTX;
+
+static
+void *ecdh_newctx(void *provctx)
+{
+ PROV_ECDH_CTX *pectx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ pectx = OPENSSL_zalloc(sizeof(*pectx));
+ if (pectx == NULL)
+ return NULL;
+
+ pectx->libctx = PROV_LIBCTX_OF(provctx);
+ pectx->cofactor_mode = -1;
+ pectx->kdf_type = PROV_ECDH_KDF_NONE;
+ OSSL_FIPS_IND_INIT(pectx)
+
+ return (void *)pectx;
+}
+
+static
+int ecdh_init(void *vpecdhctx, void *vecdh, const OSSL_PARAM params[])
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+ if (!ossl_prov_is_running()
+ || pecdhctx == NULL
+ || vecdh == NULL
+ || (EC_KEY_get0_group(vecdh) == NULL)
+ || !EC_KEY_up_ref(vecdh))
+ return 0;
+ EC_KEY_free(pecdhctx->k);
+ pecdhctx->k = vecdh;
+ pecdhctx->cofactor_mode = -1;
+ pecdhctx->kdf_type = PROV_ECDH_KDF_NONE;
+
+ OSSL_FIPS_IND_SET_APPROVED(pecdhctx)
+ if (!ecdh_set_ctx_params(pecdhctx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
+ OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
+ EC_KEY_get0_group(vecdh), "ECDH Init", 1))
+ return 0;
+#endif
+ return 1;
+}
+
+static
+int ecdh_match_params(const EC_KEY *priv, const EC_KEY *peer)
+{
+ int ret;
+ BN_CTX *ctx = NULL;
+ const EC_GROUP *group_priv = EC_KEY_get0_group(priv);
+ const EC_GROUP *group_peer = EC_KEY_get0_group(peer);
+
+ ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(priv));
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_BN_LIB);
+ return 0;
+ }
+ ret = group_priv != NULL
+ && group_peer != NULL
+ && EC_GROUP_cmp(group_priv, group_peer, ctx) == 0;
+ if (!ret)
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+static
+int ecdh_set_peer(void *vpecdhctx, void *vecdh)
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+ if (!ossl_prov_is_running()
+ || pecdhctx == NULL
+ || vecdh == NULL
+ || !ecdh_match_params(pecdhctx->k, vecdh))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(pecdhctx),
+ OSSL_FIPS_IND_SETTABLE0, pecdhctx->libctx,
+ EC_KEY_get0_group(vecdh), "ECDH Set Peer",
+ 1))
+ return 0;
+#endif
+ if (!EC_KEY_up_ref(vecdh))
+ return 0;
+
+ EC_KEY_free(pecdhctx->peerk);
+ pecdhctx->peerk = vecdh;
+ return 1;
+}
+
+static
+void ecdh_freectx(void *vpecdhctx)
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+ EC_KEY_free(pecdhctx->k);
+ EC_KEY_free(pecdhctx->peerk);
+
+ EVP_MD_free(pecdhctx->kdf_md);
+ OPENSSL_clear_free(pecdhctx->kdf_ukm, pecdhctx->kdf_ukmlen);
+
+ OPENSSL_free(pecdhctx);
+}
+
+static
+void *ecdh_dupctx(void *vpecdhctx)
+{
+ PROV_ECDH_CTX *srcctx = (PROV_ECDH_CTX *)vpecdhctx;
+ PROV_ECDH_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+
+ /* clear all pointers */
+
+ dstctx->k= NULL;
+ dstctx->peerk = NULL;
+ dstctx->kdf_md = NULL;
+ dstctx->kdf_ukm = NULL;
+
+ /* up-ref all ref-counted objects referenced in dstctx */
+
+ if (srcctx->k != NULL && !EC_KEY_up_ref(srcctx->k))
+ goto err;
+ else
+ dstctx->k = srcctx->k;
+
+ if (srcctx->peerk != NULL && !EC_KEY_up_ref(srcctx->peerk))
+ goto err;
+ else
+ dstctx->peerk = srcctx->peerk;
+
+ if (srcctx->kdf_md != NULL && !EVP_MD_up_ref(srcctx->kdf_md))
+ goto err;
+ else
+ dstctx->kdf_md = srcctx->kdf_md;
+
+ /* Duplicate UKM data if present */
+ if (srcctx->kdf_ukm != NULL && srcctx->kdf_ukmlen > 0) {
+ dstctx->kdf_ukm = OPENSSL_memdup(srcctx->kdf_ukm,
+ srcctx->kdf_ukmlen);
+ if (dstctx->kdf_ukm == NULL)
+ goto err;
+ }
+
+ return dstctx;
+
+ err:
+ ecdh_freectx(dstctx);
+ return NULL;
+}
+
+static
+int ecdh_set_ctx_params(void *vpecdhctx, const OSSL_PARAM params[])
+{
+ char name[80] = { '\0' }; /* should be big enough */
+ char *str = NULL;
+ PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
+ const OSSL_PARAM *p;
+
+ if (pectx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pectx, OSSL_FIPS_IND_SETTABLE2, params,
+ OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+ if (p != NULL) {
+ int mode;
+
+ if (!OSSL_PARAM_get_int(p, &mode))
+ return 0;
+
+ if (mode < -1 || mode > 1)
+ return 0;
+
+ pectx->cofactor_mode = mode;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+ if (p != NULL) {
+ str = name;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+ return 0;
+
+ if (name[0] == '\0')
+ pectx->kdf_type = PROV_ECDH_KDF_NONE;
+ else if (strcmp(name, OSSL_KDF_NAME_X963KDF) == 0)
+ pectx->kdf_type = PROV_ECDH_KDF_X9_63;
+ else
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+ if (p != NULL) {
+ char mdprops[80] = { '\0' }; /* should be big enough */
+
+ str = name;
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(name)))
+ return 0;
+
+ str = mdprops;
+ p = OSSL_PARAM_locate_const(params,
+ OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS);
+
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_utf8_string(p, &str, sizeof(mdprops)))
+ return 0;
+ }
+
+ EVP_MD_free(pectx->kdf_md);
+ pectx->kdf_md = EVP_MD_fetch(pectx->libctx, name, mdprops);
+ if (pectx->kdf_md == NULL)
+ return 0;
+ /* XOF digests are not allowed */
+ if (EVP_MD_xof(pectx->kdf_md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_digest_exch_check(OSSL_FIPS_IND_GET(pectx),
+ OSSL_FIPS_IND_SETTABLE1, pectx->libctx,
+ pectx->kdf_md, "ECDH Set Ctx")) {
+ EVP_MD_free(pectx->kdf_md);
+ pectx->kdf_md = NULL;
+ return 0;
+ }
+#endif
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+ if (p != NULL) {
+ size_t outlen;
+
+ if (!OSSL_PARAM_get_size_t(p, &outlen))
+ return 0;
+ pectx->kdf_outlen = outlen;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+ if (p != NULL) {
+ void *tmp_ukm = NULL;
+ size_t tmp_ukmlen;
+
+ if (!OSSL_PARAM_get_octet_string(p, &tmp_ukm, 0, &tmp_ukmlen))
+ return 0;
+ OPENSSL_free(pectx->kdf_ukm);
+ pectx->kdf_ukm = tmp_ukm;
+ pectx->kdf_ukmlen = tmp_ukmlen;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+ OSSL_PARAM_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_KEY_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_EXCHANGE_PARAM_FIPS_ECDH_COFACTOR_CHECK)
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ecdh_settable_ctx_params(ossl_unused void *vpecdhctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+static
+int ecdh_get_ctx_params(void *vpecdhctx, OSSL_PARAM params[])
+{
+ PROV_ECDH_CTX *pectx = (PROV_ECDH_CTX *)vpecdhctx;
+ OSSL_PARAM *p;
+
+ if (pectx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE);
+ if (p != NULL) {
+ int mode = pectx->cofactor_mode;
+
+ if (mode == -1) {
+ /* check what is the default for pecdhctx->k */
+ mode = EC_KEY_get_flags(pectx->k) & EC_FLAG_COFACTOR_ECDH ? 1 : 0;
+ }
+
+ if (!OSSL_PARAM_set_int(p, mode))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_TYPE);
+ if (p != NULL) {
+ const char *kdf_type = NULL;
+
+ switch (pectx->kdf_type) {
+ case PROV_ECDH_KDF_NONE:
+ kdf_type = "";
+ break;
+ case PROV_ECDH_KDF_X9_63:
+ kdf_type = OSSL_KDF_NAME_X963KDF;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!OSSL_PARAM_set_utf8_string(p, kdf_type))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_DIGEST);
+ if (p != NULL
+ && !OSSL_PARAM_set_utf8_string(p, pectx->kdf_md == NULL
+ ? ""
+ : EVP_MD_get0_name(pectx->kdf_md))) {
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_OUTLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, pectx->kdf_outlen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_EXCHANGE_PARAM_KDF_UKM);
+ if (p != NULL &&
+ !OSSL_PARAM_set_octet_ptr(p, pectx->kdf_ukm, pectx->kdf_ukmlen))
+ return 0;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(pectx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_EXCHANGE_PARAM_EC_ECDH_COFACTOR_MODE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN, NULL),
+ OSSL_PARAM_DEFN(OSSL_EXCHANGE_PARAM_KDF_UKM, OSSL_PARAM_OCTET_PTR,
+ NULL, 0),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ecdh_gettable_ctx_params(ossl_unused void *vpecdhctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static ossl_inline
+size_t ecdh_size(const EC_KEY *k)
+{
+ size_t degree = 0;
+ const EC_GROUP *group;
+
+ if (k == NULL
+ || (group = EC_KEY_get0_group(k)) == NULL)
+ return 0;
+
+ degree = EC_GROUP_get_degree(group);
+
+ return (degree + 7) / 8;
+}
+
+static ossl_inline
+int ecdh_plain_derive(void *vpecdhctx, unsigned char *secret,
+ size_t *psecretlen, size_t outlen)
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+ int retlen, ret = 0;
+ size_t ecdhsize, size;
+ const EC_POINT *ppubkey = NULL;
+ EC_KEY *privk = NULL;
+ const EC_GROUP *group;
+ const BIGNUM *cofactor;
+ int key_cofactor_mode;
+ int has_cofactor;
+#ifdef FIPS_MODULE
+ int cofactor_approved = 0;
+#endif
+
+ if (pecdhctx->k == NULL || pecdhctx->peerk == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ ecdhsize = ecdh_size(pecdhctx->k);
+ if (secret == NULL) {
+ *psecretlen = ecdhsize;
+ return 1;
+ }
+
+ if ((group = EC_KEY_get0_group(pecdhctx->k)) == NULL
+ || (cofactor = EC_GROUP_get0_cofactor(group)) == NULL)
+ return 0;
+
+ has_cofactor = !BN_is_one(cofactor);
+
+ /*
+ * NB: unlike PKCS#3 DH, if outlen is less than maximum size this is not
+ * an error, the result is truncated.
+ */
+ size = outlen < ecdhsize ? outlen : ecdhsize;
+
+ /*
+ * The ctx->cofactor_mode flag has precedence over the
+ * cofactor_mode flag set on ctx->k.
+ *
+ * - if ctx->cofactor_mode == -1, use ctx->k directly
+ * - if ctx->cofactor_mode == key_cofactor_mode, use ctx->k directly
+ * - if ctx->cofactor_mode != key_cofactor_mode:
+ * - if ctx->k->cofactor == 1, the cofactor_mode flag is irrelevant, use
+ * ctx->k directly
+ * - if ctx->k->cofactor != 1, use a duplicate of ctx->k with the flag
+ * set to ctx->cofactor_mode
+ */
+ key_cofactor_mode =
+ (EC_KEY_get_flags(pecdhctx->k) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+ if (pecdhctx->cofactor_mode != -1
+ && pecdhctx->cofactor_mode != key_cofactor_mode
+ && has_cofactor) {
+ if ((privk = EC_KEY_dup(pecdhctx->k)) == NULL)
+ return 0;
+
+ if (pecdhctx->cofactor_mode == 1) {
+ EC_KEY_set_flags(privk, EC_FLAG_COFACTOR_ECDH);
+#ifdef FIPS_MODULE
+ cofactor_approved = 1;
+#endif
+ } else {
+ EC_KEY_clear_flags(privk, EC_FLAG_COFACTOR_ECDH);
+ }
+ } else {
+ privk = pecdhctx->k;
+#ifdef FIPS_MODULE
+ cofactor_approved = key_cofactor_mode;
+#endif
+ }
+
+#ifdef FIPS_MODULE
+ /*
+ * SP800-56A r3 Section 5.7.1.2 requires ECC Cofactor DH to be used.
+ * This applies to the 'B' and 'K' curves that have cofactors that are not 1.
+ */
+ if (has_cofactor && !cofactor_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(pecdhctx, OSSL_FIPS_IND_SETTABLE2,
+ pecdhctx->libctx, "ECDH", "Cofactor",
+ ossl_fips_config_ecdh_cofactor_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_COFACTOR_REQUIRED);
+ goto end;
+ }
+ }
+#endif
+
+ ppubkey = EC_KEY_get0_public_key(pecdhctx->peerk);
+
+ retlen = ECDH_compute_key(secret, size, ppubkey, privk, NULL);
+
+ if (retlen <= 0)
+ goto end;
+
+ *psecretlen = retlen;
+ ret = 1;
+
+ end:
+ if (privk != pecdhctx->k)
+ EC_KEY_free(privk);
+ return ret;
+}
+
+static ossl_inline
+int ecdh_X9_63_kdf_derive(void *vpecdhctx, unsigned char *secret,
+ size_t *psecretlen, size_t outlen)
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+ unsigned char *stmp = NULL;
+ size_t stmplen;
+ int ret = 0;
+
+ if (secret == NULL) {
+ *psecretlen = pecdhctx->kdf_outlen;
+ return 1;
+ }
+
+ if (pecdhctx->kdf_outlen > outlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!ecdh_plain_derive(vpecdhctx, NULL, &stmplen, 0))
+ return 0;
+ if ((stmp = OPENSSL_secure_malloc(stmplen)) == NULL)
+ return 0;
+ if (!ecdh_plain_derive(vpecdhctx, stmp, &stmplen, stmplen))
+ goto err;
+
+ /* Do KDF stuff */
+ if (!ossl_ecdh_kdf_X9_63(secret, pecdhctx->kdf_outlen,
+ stmp, stmplen,
+ pecdhctx->kdf_ukm,
+ pecdhctx->kdf_ukmlen,
+ pecdhctx->kdf_md,
+ pecdhctx->libctx, NULL))
+ goto err;
+ *psecretlen = pecdhctx->kdf_outlen;
+ ret = 1;
+
+ err:
+ OPENSSL_secure_clear_free(stmp, stmplen);
+ return ret;
+}
+
+static
+int ecdh_derive(void *vpecdhctx, unsigned char *secret,
+ size_t *psecretlen, size_t outlen)
+{
+ PROV_ECDH_CTX *pecdhctx = (PROV_ECDH_CTX *)vpecdhctx;
+
+ switch (pecdhctx->kdf_type) {
+ case PROV_ECDH_KDF_NONE:
+ return ecdh_plain_derive(vpecdhctx, secret, psecretlen, outlen);
+ case PROV_ECDH_KDF_X9_63:
+ return ecdh_X9_63_kdf_derive(vpecdhctx, secret, psecretlen, outlen);
+ default:
+ break;
+ }
+ return 0;
+}
+
+const OSSL_DISPATCH ossl_ecdh_keyexch_functions[] = {
+ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))ecdh_newctx },
+ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))ecdh_init },
+ { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecdh_derive },
+ { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecdh_set_peer },
+ { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecdh_freectx },
+ { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecdh_dupctx },
+ { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))ecdh_set_ctx_params },
+ { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS,
+ (void (*)(void))ecdh_settable_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecdh_get_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+ (void (*)(void))ecdh_gettable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/exchange/ecx_exch.c b/crypto/openssl/providers/implementations/exchange/ecx_exch.c
new file mode 100644
index 000000000000..28e2ff61c7cc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/exchange/ecx_exch.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "crypto/ecx.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/securitycheck.h"
+
+static OSSL_FUNC_keyexch_newctx_fn x25519_newctx;
+static OSSL_FUNC_keyexch_newctx_fn x448_newctx;
+static OSSL_FUNC_keyexch_init_fn x25519_init;
+static OSSL_FUNC_keyexch_init_fn x448_init;
+static OSSL_FUNC_keyexch_set_peer_fn ecx_set_peer;
+static OSSL_FUNC_keyexch_derive_fn ecx_derive;
+static OSSL_FUNC_keyexch_freectx_fn ecx_freectx;
+static OSSL_FUNC_keyexch_dupctx_fn ecx_dupctx;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn ecx_gettable_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn ecx_get_ctx_params;
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes ECX_KEY structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ size_t keylen;
+ ECX_KEY *key;
+ ECX_KEY *peerkey;
+} PROV_ECX_CTX;
+
+static void *ecx_newctx(void *provctx, size_t keylen)
+{
+ PROV_ECX_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->keylen = keylen;
+
+ return ctx;
+}
+
+static void *x25519_newctx(void *provctx)
+{
+ return ecx_newctx(provctx, X25519_KEYLEN);
+}
+
+static void *x448_newctx(void *provctx)
+{
+ return ecx_newctx(provctx, X448_KEYLEN);
+}
+
+static int ecx_init(void *vecxctx, void *vkey, const char *algname)
+{
+ PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx;
+ ECX_KEY *key = vkey;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (ecxctx == NULL
+ || key == NULL
+ || key->keylen != ecxctx->keylen
+ || !ossl_ecx_key_up_ref(key)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ ossl_ecx_key_free(ecxctx->key);
+ ecxctx->key = key;
+
+#ifdef FIPS_MODULE
+ if (!ossl_FIPS_IND_callback(key->libctx, algname, "Init"))
+ return 0;
+#endif
+ return 1;
+}
+
+static int x25519_init(void *vecxctx, void *vkey,
+ ossl_unused const OSSL_PARAM params[])
+{
+ return ecx_init(vecxctx, vkey, "X25519");
+}
+
+static int x448_init(void *vecxctx, void *vkey,
+ ossl_unused const OSSL_PARAM params[])
+{
+ return ecx_init(vecxctx, vkey, "X448");
+}
+
+static int ecx_set_peer(void *vecxctx, void *vkey)
+{
+ PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx;
+ ECX_KEY *key = vkey;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (ecxctx == NULL
+ || key == NULL
+ || key->keylen != ecxctx->keylen
+ || !ossl_ecx_key_up_ref(key)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ ossl_ecx_key_free(ecxctx->peerkey);
+ ecxctx->peerkey = key;
+
+ return 1;
+}
+
+static int ecx_derive(void *vecxctx, unsigned char *secret, size_t *secretlen,
+ size_t outlen)
+{
+ PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ecx_compute_key(ecxctx->peerkey, ecxctx->key, ecxctx->keylen,
+ secret, secretlen, outlen);
+}
+
+static void ecx_freectx(void *vecxctx)
+{
+ PROV_ECX_CTX *ecxctx = (PROV_ECX_CTX *)vecxctx;
+
+ ossl_ecx_key_free(ecxctx->key);
+ ossl_ecx_key_free(ecxctx->peerkey);
+
+ OPENSSL_free(ecxctx);
+}
+
+static void *ecx_dupctx(void *vecxctx)
+{
+ PROV_ECX_CTX *srcctx = (PROV_ECX_CTX *)vecxctx;
+ PROV_ECX_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ if (dstctx->key != NULL && !ossl_ecx_key_up_ref(dstctx->key)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ if (dstctx->peerkey != NULL && !ossl_ecx_key_up_ref(dstctx->peerkey)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ ossl_ecx_key_free(dstctx->key);
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ return dstctx;
+}
+
+static const OSSL_PARAM *ecx_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int ecx_get_ctx_params(ossl_unused void *vctx, OSSL_PARAM params[])
+{
+#ifdef FIPS_MODULE
+ int approved = 0;
+ OSSL_PARAM *p = OSSL_PARAM_locate(params,
+ OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR);
+
+ if (p != NULL && !OSSL_PARAM_set_int(p, approved))
+ return 0;
+#endif
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_x25519_keyexch_functions[] = {
+ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))x25519_newctx },
+ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))x25519_init },
+ { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecx_derive },
+ { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecx_set_peer },
+ { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecx_freectx },
+ { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecx_dupctx },
+ { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecx_get_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+ (void (*)(void))ecx_gettable_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_x448_keyexch_functions[] = {
+ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))x448_newctx },
+ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))x448_init },
+ { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))ecx_derive },
+ { OSSL_FUNC_KEYEXCH_SET_PEER, (void (*)(void))ecx_set_peer },
+ { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))ecx_freectx },
+ { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))ecx_dupctx },
+ { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))ecx_get_ctx_params },
+ { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS,
+ (void (*)(void))ecx_gettable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/exchange/kdf_exch.c b/crypto/openssl/providers/implementations/exchange/kdf_exch.c
new file mode 100644
index 000000000000..340a2663c588
--- /dev/null
+++ b/crypto/openssl/providers/implementations/exchange/kdf_exch.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include <openssl/kdf.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/params.h>
+#include "internal/numbers.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/kdfexchange.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_keyexch_newctx_fn kdf_tls1_prf_newctx;
+static OSSL_FUNC_keyexch_newctx_fn kdf_hkdf_newctx;
+static OSSL_FUNC_keyexch_newctx_fn kdf_scrypt_newctx;
+static OSSL_FUNC_keyexch_init_fn kdf_init;
+static OSSL_FUNC_keyexch_derive_fn kdf_derive;
+static OSSL_FUNC_keyexch_freectx_fn kdf_freectx;
+static OSSL_FUNC_keyexch_dupctx_fn kdf_dupctx;
+static OSSL_FUNC_keyexch_set_ctx_params_fn kdf_set_ctx_params;
+static OSSL_FUNC_keyexch_get_ctx_params_fn kdf_get_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_tls1_prf_settable_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_hkdf_settable_ctx_params;
+static OSSL_FUNC_keyexch_settable_ctx_params_fn kdf_scrypt_settable_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_tls1_prf_gettable_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params;
+static OSSL_FUNC_keyexch_gettable_ctx_params_fn kdf_scrypt_gettable_ctx_params;
+
+typedef struct {
+ void *provctx;
+ EVP_KDF_CTX *kdfctx;
+ KDF_DATA *kdfdata;
+} PROV_KDF_CTX;
+
+static void *kdf_newctx(const char *kdfname, void *provctx)
+{
+ PROV_KDF_CTX *kdfctx;
+ EVP_KDF *kdf = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ kdfctx = OPENSSL_zalloc(sizeof(PROV_KDF_CTX));
+ if (kdfctx == NULL)
+ return NULL;
+
+ kdfctx->provctx = provctx;
+
+ kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname, NULL);
+ if (kdf == NULL)
+ goto err;
+ kdfctx->kdfctx = EVP_KDF_CTX_new(kdf);
+ EVP_KDF_free(kdf);
+
+ if (kdfctx->kdfctx == NULL)
+ goto err;
+
+ return kdfctx;
+err:
+ OPENSSL_free(kdfctx);
+ return NULL;
+}
+
+#define KDF_NEWCTX(funcname, kdfname) \
+ static void *kdf_##funcname##_newctx(void *provctx) \
+ { \
+ return kdf_newctx(kdfname, provctx); \
+ }
+
+KDF_NEWCTX(tls1_prf, "TLS1-PRF")
+KDF_NEWCTX(hkdf, "HKDF")
+KDF_NEWCTX(scrypt, "SCRYPT")
+
+static int kdf_init(void *vpkdfctx, void *vkdf, const OSSL_PARAM params[])
+{
+ PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx;
+
+ if (!ossl_prov_is_running()
+ || pkdfctx == NULL
+ || vkdf == NULL
+ || !ossl_kdf_data_up_ref(vkdf))
+ return 0;
+ pkdfctx->kdfdata = vkdf;
+
+ return kdf_set_ctx_params(pkdfctx, params);
+}
+
+static int kdf_derive(void *vpkdfctx, unsigned char *secret, size_t *secretlen,
+ size_t outlen)
+{
+ PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx;
+ size_t kdfsize;
+ int ret;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ kdfsize = EVP_KDF_CTX_get_kdf_size(pkdfctx->kdfctx);
+
+ if (secret == NULL) {
+ *secretlen = kdfsize;
+ return 1;
+ }
+
+ if (kdfsize != SIZE_MAX) {
+ if (outlen < kdfsize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ outlen = kdfsize;
+ }
+
+ ret = EVP_KDF_derive(pkdfctx->kdfctx, secret, outlen, NULL);
+ if (ret <= 0)
+ return 0;
+
+ *secretlen = outlen;
+ return 1;
+}
+
+static void kdf_freectx(void *vpkdfctx)
+{
+ PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx;
+
+ EVP_KDF_CTX_free(pkdfctx->kdfctx);
+ ossl_kdf_data_free(pkdfctx->kdfdata);
+
+ OPENSSL_free(pkdfctx);
+}
+
+static void *kdf_dupctx(void *vpkdfctx)
+{
+ PROV_KDF_CTX *srcctx = (PROV_KDF_CTX *)vpkdfctx;
+ PROV_KDF_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+
+ dstctx->kdfctx = EVP_KDF_CTX_dup(srcctx->kdfctx);
+ if (dstctx->kdfctx == NULL) {
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+ if (!ossl_kdf_data_up_ref(dstctx->kdfdata)) {
+ EVP_KDF_CTX_free(dstctx->kdfctx);
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+
+ return dstctx;
+}
+
+static int kdf_set_ctx_params(void *vpkdfctx, const OSSL_PARAM params[])
+{
+ PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx;
+
+ return EVP_KDF_CTX_set_params(pkdfctx->kdfctx, params);
+}
+
+static int kdf_get_ctx_params(void *vpkdfctx, OSSL_PARAM params[])
+{
+ PROV_KDF_CTX *pkdfctx = (PROV_KDF_CTX *)vpkdfctx;
+
+ return EVP_KDF_CTX_get_params(pkdfctx->kdfctx, params);
+}
+
+static const OSSL_PARAM *kdf_settable_ctx_params(ossl_unused void *vpkdfctx,
+ void *provctx,
+ const char *kdfname)
+{
+ EVP_KDF *kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname,
+ NULL);
+ const OSSL_PARAM *params;
+
+ if (kdf == NULL)
+ return NULL;
+
+ params = EVP_KDF_settable_ctx_params(kdf);
+ EVP_KDF_free(kdf);
+
+ return params;
+}
+
+#define KDF_SETTABLE_CTX_PARAMS(funcname, kdfname) \
+ static const OSSL_PARAM *kdf_##funcname##_settable_ctx_params(void *vpkdfctx, \
+ void *provctx) \
+ { \
+ return kdf_settable_ctx_params(vpkdfctx, provctx, kdfname); \
+ }
+
+KDF_SETTABLE_CTX_PARAMS(tls1_prf, "TLS1-PRF")
+KDF_SETTABLE_CTX_PARAMS(hkdf, "HKDF")
+KDF_SETTABLE_CTX_PARAMS(scrypt, "SCRYPT")
+
+static const OSSL_PARAM *kdf_gettable_ctx_params(ossl_unused void *vpkdfctx,
+ void *provctx,
+ const char *kdfname)
+{
+ EVP_KDF *kdf = EVP_KDF_fetch(PROV_LIBCTX_OF(provctx), kdfname,
+ NULL);
+ const OSSL_PARAM *params;
+
+ if (kdf == NULL)
+ return NULL;
+
+ params = EVP_KDF_gettable_ctx_params(kdf);
+ EVP_KDF_free(kdf);
+
+ return params;
+}
+
+#define KDF_GETTABLE_CTX_PARAMS(funcname, kdfname) \
+ static const OSSL_PARAM *kdf_##funcname##_gettable_ctx_params(void *vpkdfctx, \
+ void *provctx) \
+ { \
+ return kdf_gettable_ctx_params(vpkdfctx, provctx, kdfname); \
+ }
+
+KDF_GETTABLE_CTX_PARAMS(tls1_prf, "TLS1-PRF")
+KDF_GETTABLE_CTX_PARAMS(hkdf, "HKDF")
+KDF_GETTABLE_CTX_PARAMS(scrypt, "SCRYPT")
+
+#define KDF_KEYEXCH_FUNCTIONS(funcname) \
+ const OSSL_DISPATCH ossl_kdf_##funcname##_keyexch_functions[] = { \
+ { OSSL_FUNC_KEYEXCH_NEWCTX, (void (*)(void))kdf_##funcname##_newctx }, \
+ { OSSL_FUNC_KEYEXCH_INIT, (void (*)(void))kdf_init }, \
+ { OSSL_FUNC_KEYEXCH_DERIVE, (void (*)(void))kdf_derive }, \
+ { OSSL_FUNC_KEYEXCH_FREECTX, (void (*)(void))kdf_freectx }, \
+ { OSSL_FUNC_KEYEXCH_DUPCTX, (void (*)(void))kdf_dupctx }, \
+ { OSSL_FUNC_KEYEXCH_SET_CTX_PARAMS, (void (*)(void))kdf_set_ctx_params }, \
+ { OSSL_FUNC_KEYEXCH_GET_CTX_PARAMS, (void (*)(void))kdf_get_ctx_params }, \
+ { OSSL_FUNC_KEYEXCH_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))kdf_##funcname##_settable_ctx_params }, \
+ { OSSL_FUNC_KEYEXCH_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))kdf_##funcname##_gettable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ };
+
+KDF_KEYEXCH_FUNCTIONS(tls1_prf)
+KDF_KEYEXCH_FUNCTIONS(hkdf)
+KDF_KEYEXCH_FUNCTIONS(scrypt)
diff --git a/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H
new file mode 100644
index 000000000000..2ab493330675
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_EPILOGUE.H
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file is only used by HP C/C++ on VMS, and is included automatically
+ * after each header file from this directory
+ */
+
+/*
+ * The C++ compiler doesn't understand these pragmas, even though it
+ * understands the corresponding command line qualifier.
+ */
+#ifndef __cplusplus
+/* restore state. Must correspond to the save in __decc_include_prologue.h */
+# pragma names restore
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H
new file mode 100644
index 000000000000..8e95fa975488
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/__DECC_INCLUDE_PROLOGUE.H
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This file is only used by HP C/C++ on VMS, and is included automatically
+ * after each header file from this directory
+ */
+
+/*
+ * The C++ compiler doesn't understand these pragmas, even though it
+ * understands the corresponding command line qualifier.
+ */
+#ifndef __cplusplus
+/* save state */
+# pragma names save
+/* have the compiler shorten symbols larger than 31 chars to 23 chars
+ * followed by a 8 hex char CRC
+ */
+# pragma names as_is,shortened
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/blake2.h b/crypto/openssl/providers/implementations/include/prov/blake2.h
new file mode 100644
index 000000000000..42229e2d7402
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/blake2.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_BLAKE2_H
+# define OSSL_PROV_BLAKE2_H
+
+# include <openssl/opensslconf.h>
+
+# include <openssl/e_os2.h>
+# include <stddef.h>
+# include <crypto/evp.h>
+
+# define BLAKE2S_BLOCKBYTES 64
+# define BLAKE2S_OUTBYTES 32
+# define BLAKE2S_KEYBYTES 32
+# define BLAKE2S_SALTBYTES 8
+# define BLAKE2S_PERSONALBYTES 8
+
+# define BLAKE2B_BLOCKBYTES 128
+# define BLAKE2B_OUTBYTES 64
+# define BLAKE2B_KEYBYTES 64
+# define BLAKE2B_SALTBYTES 16
+# define BLAKE2B_PERSONALBYTES 16
+
+struct blake2s_param_st {
+ uint8_t digest_length; /* 1 */
+ uint8_t key_length; /* 2 */
+ uint8_t fanout; /* 3 */
+ uint8_t depth; /* 4 */
+ uint8_t leaf_length[4];/* 8 */
+ uint8_t node_offset[6];/* 14 */
+ uint8_t node_depth; /* 15 */
+ uint8_t inner_length; /* 16 */
+ uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */
+ uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */
+};
+
+typedef struct blake2s_param_st BLAKE2S_PARAM;
+
+struct blake2s_ctx_st {
+ uint32_t h[8];
+ uint32_t t[2];
+ uint32_t f[2];
+ uint8_t buf[BLAKE2S_BLOCKBYTES];
+ size_t buflen;
+ size_t outlen;
+};
+
+struct blake2b_param_st {
+ uint8_t digest_length; /* 1 */
+ uint8_t key_length; /* 2 */
+ uint8_t fanout; /* 3 */
+ uint8_t depth; /* 4 */
+ uint8_t leaf_length[4];/* 8 */
+ uint8_t node_offset[8];/* 16 */
+ uint8_t node_depth; /* 17 */
+ uint8_t inner_length; /* 18 */
+ uint8_t reserved[14]; /* 32 */
+ uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */
+ uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */
+};
+
+typedef struct blake2b_param_st BLAKE2B_PARAM;
+
+struct blake2b_ctx_st {
+ uint64_t h[8];
+ uint64_t t[2];
+ uint64_t f[2];
+ uint8_t buf[BLAKE2B_BLOCKBYTES];
+ size_t buflen;
+ size_t outlen;
+};
+
+#define BLAKE2B_DIGEST_LENGTH 64
+#define BLAKE2S_DIGEST_LENGTH 32
+
+typedef struct blake2s_ctx_st BLAKE2S_CTX;
+typedef struct blake2b_ctx_st BLAKE2B_CTX;
+
+struct blake2b_md_data_st {
+ BLAKE2B_CTX ctx;
+ BLAKE2B_PARAM params;
+};
+
+struct blake2s_md_data_st {
+ BLAKE2S_CTX ctx;
+ BLAKE2S_PARAM params;
+};
+
+int ossl_blake2b_init(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P);
+int ossl_blake2b_init_key(BLAKE2B_CTX *c, const BLAKE2B_PARAM *P,
+ const void *key);
+int ossl_blake2b_update(BLAKE2B_CTX *c, const void *data, size_t datalen);
+int ossl_blake2b_final(unsigned char *md, BLAKE2B_CTX *c);
+
+OSSL_FUNC_digest_get_ctx_params_fn ossl_blake2b_get_ctx_params;
+OSSL_FUNC_digest_set_ctx_params_fn ossl_blake2b_set_ctx_params;
+OSSL_FUNC_digest_gettable_ctx_params_fn ossl_blake2b_gettable_ctx_params;
+OSSL_FUNC_digest_settable_ctx_params_fn ossl_blake2b_settable_ctx_params;
+
+/*
+ * These setters are internal and do not check the validity of their parameters.
+ * See blake2b_mac_ctrl for validation logic.
+ */
+
+void ossl_blake2b_param_init(BLAKE2B_PARAM *P);
+void ossl_blake2b_param_set_digest_length(BLAKE2B_PARAM *P, uint8_t outlen);
+void ossl_blake2b_param_set_key_length(BLAKE2B_PARAM *P, uint8_t keylen);
+void ossl_blake2b_param_set_personal(BLAKE2B_PARAM *P, const uint8_t *personal,
+ size_t length);
+void ossl_blake2b_param_set_salt(BLAKE2B_PARAM *P, const uint8_t *salt,
+ size_t length);
+int ossl_blake2s_init(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P);
+int ossl_blake2s_init_key(BLAKE2S_CTX *c, const BLAKE2S_PARAM *P,
+ const void *key);
+int ossl_blake2s_update(BLAKE2S_CTX *c, const void *data, size_t datalen);
+int ossl_blake2s_final(unsigned char *md, BLAKE2S_CTX *c);
+
+void ossl_blake2s_param_init(BLAKE2S_PARAM *P);
+void ossl_blake2s_param_set_digest_length(BLAKE2S_PARAM *P, uint8_t outlen);
+void ossl_blake2s_param_set_key_length(BLAKE2S_PARAM *P, uint8_t keylen);
+void ossl_blake2s_param_set_personal(BLAKE2S_PARAM *P, const uint8_t *personal,
+ size_t length);
+void ossl_blake2s_param_set_salt(BLAKE2S_PARAM *P, const uint8_t *salt,
+ size_t length);
+
+OSSL_FUNC_digest_get_ctx_params_fn ossl_blake2s_get_ctx_params;
+OSSL_FUNC_digest_set_ctx_params_fn ossl_blake2s_set_ctx_params;
+OSSL_FUNC_digest_gettable_ctx_params_fn ossl_blake2s_gettable_ctx_params;
+OSSL_FUNC_digest_settable_ctx_params_fn ossl_blake2s_settable_ctx_params;
+
+#endif /* OSSL_PROV_BLAKE2_H */
diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon.h
new file mode 100644
index 000000000000..46efdc80033f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon.h
@@ -0,0 +1,375 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_CIPHERCOMMON_H
+# define OSSL_PROV_CIPHERCOMMON_H
+# pragma once
+
+# include <openssl/params.h>
+# include <openssl/core_dispatch.h>
+# include <openssl/core_names.h>
+# include <openssl/evp.h>
+# include "internal/cryptlib.h"
+# include "crypto/modes.h"
+
+# define MAXCHUNK ((size_t)1 << 30)
+# define MAXBITCHUNK ((size_t)1 << (sizeof(size_t) * 8 - 4))
+
+# define GENERIC_BLOCK_SIZE 16
+# define IV_STATE_UNINITIALISED 0 /* initial state is not initialized */
+# define IV_STATE_BUFFERED 1 /* iv has been copied to the iv buffer */
+# define IV_STATE_COPIED 2 /* iv has been copied from the iv buffer */
+# define IV_STATE_FINISHED 3 /* the iv has been used - so don't reuse it */
+
+# define PROV_CIPHER_FUNC(type, name, args) typedef type (* OSSL_##name##_fn)args
+
+typedef struct prov_cipher_hw_st PROV_CIPHER_HW;
+typedef struct prov_cipher_ctx_st PROV_CIPHER_CTX;
+
+typedef int (PROV_CIPHER_HW_FN)(PROV_CIPHER_CTX *dat, unsigned char *out,
+ const unsigned char *in, size_t len);
+
+/* Internal flags that can be queried */
+# define PROV_CIPHER_FLAG_AEAD 0x0001
+# define PROV_CIPHER_FLAG_CUSTOM_IV 0x0002
+# define PROV_CIPHER_FLAG_CTS 0x0004
+# define PROV_CIPHER_FLAG_TLS1_MULTIBLOCK 0x0008
+# define PROV_CIPHER_FLAG_RAND_KEY 0x0010
+/* Internal flags that are only used within the provider */
+# define PROV_CIPHER_FLAG_VARIABLE_LENGTH 0x0100
+# define PROV_CIPHER_FLAG_INVERSE_CIPHER 0x0200
+
+struct prov_cipher_ctx_st {
+ /* place buffer at the beginning for memory alignment */
+ /* The original value of the iv */
+ unsigned char oiv[GENERIC_BLOCK_SIZE];
+ /* Buffer of partial blocks processed via update calls */
+ unsigned char buf[GENERIC_BLOCK_SIZE];
+ unsigned char iv[GENERIC_BLOCK_SIZE];
+
+ block128_f block;
+ union {
+ cbc128_f cbc;
+ ctr128_f ctr;
+ ecb128_f ecb;
+ } stream;
+
+ unsigned int mode;
+ size_t keylen; /* key size (in bytes) */
+ size_t ivlen;
+ size_t blocksize;
+ size_t bufsz; /* Number of bytes in buf */
+ unsigned int cts_mode; /* Use to set the type for CTS modes */
+ unsigned int pad : 1; /* Whether padding should be used or not */
+ unsigned int enc : 1; /* Set to 1 for encrypt, or 0 otherwise */
+ unsigned int iv_set : 1; /* Set when the iv is copied to the iv/oiv buffers */
+ unsigned int key_set : 1; /* Set when key is set on the context */
+ unsigned int updated : 1; /* Set to 1 during update for one shot ciphers */
+ unsigned int variable_keylength : 1;
+ unsigned int inverse_cipher : 1; /* set to 1 to use inverse cipher */
+ unsigned int use_bits : 1; /* Set to 0 for cfb1 to use bits instead of bytes */
+
+ unsigned int tlsversion; /* If TLS padding is in use the TLS version number */
+ unsigned char *tlsmac; /* tls MAC extracted from the last record */
+ int alloced; /*
+ * Whether the tlsmac data has been allocated or
+ * points into the user buffer.
+ */
+ size_t tlsmacsize; /* Size of the TLS MAC */
+ int removetlspad; /* Whether TLS padding should be removed or not */
+ size_t removetlsfixed; /*
+ * Length of the fixed size data to remove when
+ * processing TLS data (equals mac size plus
+ * IV size if applicable)
+ */
+
+ /*
+ * num contains the number of bytes of |iv| which are valid for modes that
+ * manage partial blocks themselves.
+ */
+ unsigned int num;
+ const PROV_CIPHER_HW *hw; /* hardware specific functions */
+ const void *ks; /* Pointer to algorithm specific key data */
+ OSSL_LIB_CTX *libctx;
+};
+
+struct prov_cipher_hw_st {
+ int (*init)(PROV_CIPHER_CTX *dat, const uint8_t *key, size_t keylen);
+ PROV_CIPHER_HW_FN *cipher;
+ void (*copyctx)(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src);
+};
+
+void ossl_cipher_generic_reset_ctx(PROV_CIPHER_CTX *ctx);
+OSSL_FUNC_cipher_encrypt_init_fn ossl_cipher_generic_einit;
+OSSL_FUNC_cipher_decrypt_init_fn ossl_cipher_generic_dinit;
+OSSL_FUNC_cipher_update_fn ossl_cipher_generic_block_update;
+OSSL_FUNC_cipher_final_fn ossl_cipher_generic_block_final;
+OSSL_FUNC_cipher_update_fn ossl_cipher_generic_stream_update;
+OSSL_FUNC_cipher_final_fn ossl_cipher_generic_stream_final;
+OSSL_FUNC_cipher_cipher_fn ossl_cipher_generic_cipher;
+OSSL_FUNC_cipher_get_ctx_params_fn ossl_cipher_generic_get_ctx_params;
+OSSL_FUNC_cipher_set_ctx_params_fn ossl_cipher_generic_set_ctx_params;
+OSSL_FUNC_cipher_gettable_params_fn ossl_cipher_generic_gettable_params;
+OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_cipher_generic_gettable_ctx_params;
+OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_generic_settable_ctx_params;
+OSSL_FUNC_cipher_set_ctx_params_fn ossl_cipher_var_keylen_set_ctx_params;
+OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_var_keylen_settable_ctx_params;
+OSSL_FUNC_cipher_gettable_ctx_params_fn ossl_cipher_aead_gettable_ctx_params;
+OSSL_FUNC_cipher_settable_ctx_params_fn ossl_cipher_aead_settable_ctx_params;
+OSSL_FUNC_cipher_encrypt_skey_init_fn ossl_cipher_generic_skey_einit;
+OSSL_FUNC_cipher_decrypt_skey_init_fn ossl_cipher_generic_skey_dinit;
+
+int ossl_cipher_generic_get_params(OSSL_PARAM params[], unsigned int md,
+ uint64_t flags,
+ size_t kbits, size_t blkbits, size_t ivbits);
+void ossl_cipher_generic_initkey(void *vctx, size_t kbits, size_t blkbits,
+ size_t ivbits, unsigned int mode,
+ uint64_t flags,
+ const PROV_CIPHER_HW *hw, void *provctx);
+
+# define IMPLEMENT_generic_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits,\
+ blkbits, ivbits, typ) \
+const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_cipher_generic_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_cipher_generic_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_settable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_einit },\
+ { OSSL_FUNC_CIPHER_DECRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_dinit },\
+ OSSL_DISPATCH_END \
+};
+
+# define IMPLEMENT_var_keylen_cipher_func(alg, UCALG, lcmode, UCMODE, flags, \
+ kbits, blkbits, ivbits, typ) \
+const OSSL_DISPATCH ossl_##alg##kbits##lcmode##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void)) alg##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void)) alg##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_cipher_generic_einit },\
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_cipher_generic_dinit },\
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_cipher_generic_##typ##_update },\
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_cipher_generic_##typ##_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_cipher_generic_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lcmode##_get_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_var_keylen_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_var_keylen_settable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_einit },\
+ { OSSL_FUNC_CIPHER_DECRYPT_SKEY_INIT, (void (*)(void))ossl_cipher_generic_skey_dinit },\
+ OSSL_DISPATCH_END \
+};
+
+
+# define IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, \
+ kbits, blkbits, ivbits, typ) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lcmode##_get_params; \
+static int alg##_##kbits##_##lcmode##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn alg##_##kbits##_##lcmode##_newctx; \
+static void * alg##_##kbits##_##lcmode##_newctx(void *provctx) \
+{ \
+ PROV_##UCALG##_CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx))\
+ : NULL; \
+ if (ctx != NULL) { \
+ ossl_cipher_generic_initkey(ctx, kbits, blkbits, ivbits, \
+ EVP_CIPH_##UCMODE##_MODE, flags, \
+ ossl_prov_cipher_hw_##alg##_##lcmode(kbits),\
+ provctx); \
+ } \
+ return ctx; \
+} \
+
+# define IMPLEMENT_generic_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+IMPLEMENT_generic_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ)
+
+# define IMPLEMENT_var_keylen_cipher(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+IMPLEMENT_generic_cipher_genfn(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ) \
+IMPLEMENT_var_keylen_cipher_func(alg, UCALG, lcmode, UCMODE, flags, kbits, \
+ blkbits, ivbits, typ)
+
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cbc;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ecb;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ofb128;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb128;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb8;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_cfb1;
+PROV_CIPHER_HW_FN ossl_cipher_hw_generic_ctr;
+PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cbc;
+PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cfb8;
+PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_cfb128;
+PROV_CIPHER_HW_FN ossl_cipher_hw_chunked_ofb128;
+# define ossl_cipher_hw_chunked_ecb ossl_cipher_hw_generic_ecb
+# define ossl_cipher_hw_chunked_ctr ossl_cipher_hw_generic_ctr
+# define ossl_cipher_hw_chunked_cfb1 ossl_cipher_hw_generic_cfb1
+
+# define IMPLEMENT_CIPHER_HW_OFB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \
+static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \
+ unsigned char *out, \
+ const unsigned char *in, size_t len) \
+{ \
+ int num = ctx->num; \
+ KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \
+ \
+ while (len >= MAXCHUNK) { \
+ FUNC_PREFIX##_encrypt(in, out, MAXCHUNK, key, ctx->iv, &num); \
+ len -= MAXCHUNK; \
+ in += MAXCHUNK; \
+ out += MAXCHUNK; \
+ } \
+ if (len > 0) { \
+ FUNC_PREFIX##_encrypt(in, out, (long)len, key, ctx->iv, &num); \
+ } \
+ ctx->num = num; \
+ return 1; \
+}
+
+# define IMPLEMENT_CIPHER_HW_ECB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \
+static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \
+ unsigned char *out, \
+ const unsigned char *in, size_t len) \
+{ \
+ size_t i, bl = ctx->blocksize; \
+ KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \
+ \
+ if (len < bl) \
+ return 1; \
+ for (i = 0, len -= bl; i <= len; i += bl) \
+ FUNC_PREFIX##_encrypt(in + i, out + i, key, ctx->enc); \
+ return 1; \
+}
+
+# define IMPLEMENT_CIPHER_HW_CBC(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \
+static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \
+ unsigned char *out, \
+ const unsigned char *in, size_t len) \
+{ \
+ KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \
+ \
+ while (len >= MAXCHUNK) { \
+ FUNC_PREFIX##_encrypt(in, out, MAXCHUNK, key, ctx->iv, ctx->enc); \
+ len -= MAXCHUNK; \
+ in += MAXCHUNK; \
+ out += MAXCHUNK; \
+ } \
+ if (len > 0) \
+ FUNC_PREFIX##_encrypt(in, out, (long)len, key, ctx->iv, ctx->enc); \
+ return 1; \
+}
+
+# define IMPLEMENT_CIPHER_HW_CFB(MODE, NAME, CTX_NAME, KEY_NAME, FUNC_PREFIX) \
+static int cipher_hw_##NAME##_##MODE##_cipher(PROV_CIPHER_CTX *ctx, \
+ unsigned char *out, \
+ const unsigned char *in, size_t len) \
+{ \
+ size_t chunk = MAXCHUNK; \
+ KEY_NAME *key = &(((CTX_NAME *)ctx)->ks.ks); \
+ int num = ctx->num; \
+ \
+ if (len < chunk) \
+ chunk = len; \
+ while (len > 0 && len >= chunk) { \
+ FUNC_PREFIX##_encrypt(in, out, (long)chunk, key, ctx->iv, &num, \
+ ctx->enc); \
+ len -= chunk; \
+ in += chunk; \
+ out += chunk; \
+ if (len < chunk) \
+ chunk = len; \
+ } \
+ ctx->num = num; \
+ return 1; \
+}
+
+# define IMPLEMENT_CIPHER_HW_COPYCTX(name, CTX_TYPE) \
+static void name(PROV_CIPHER_CTX *dst, const PROV_CIPHER_CTX *src) \
+{ \
+ CTX_TYPE *sctx = (CTX_TYPE *)src; \
+ CTX_TYPE *dctx = (CTX_TYPE *)dst; \
+ \
+ *dctx = *sctx; \
+ dst->ks = &dctx->ks.ks; \
+}
+
+# define CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(name) \
+static const OSSL_PARAM name##_known_gettable_ctx_params[] = { \
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_KEYLEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), \
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), \
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL), \
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_IV, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_CIPHER_PARAM_UPDATED_IV, NULL, 0),
+
+# define CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(name) \
+ OSSL_PARAM_END \
+}; \
+const OSSL_PARAM * name##_gettable_ctx_params(ossl_unused void *cctx, \
+ ossl_unused void *provctx) \
+{ \
+ return name##_known_gettable_ctx_params; \
+}
+
+# define CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(name) \
+static const OSSL_PARAM name##_known_settable_ctx_params[] = { \
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_PADDING, NULL), \
+ OSSL_PARAM_uint(OSSL_CIPHER_PARAM_NUM, NULL),
+# define CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(name) \
+ OSSL_PARAM_END \
+}; \
+const OSSL_PARAM * name##_settable_ctx_params(ossl_unused void *cctx, \
+ ossl_unused void *provctx) \
+{ \
+ return name##_known_settable_ctx_params; \
+}
+
+int ossl_cipher_generic_initiv(PROV_CIPHER_CTX *ctx, const unsigned char *iv,
+ size_t ivlen);
+
+size_t ossl_cipher_fillblock(unsigned char *buf, size_t *buflen,
+ size_t blocksize,
+ const unsigned char **in, size_t *inlen);
+int ossl_cipher_trailingdata(unsigned char *buf, size_t *buflen,
+ size_t blocksize,
+ const unsigned char **in, size_t *inlen);
+
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h
new file mode 100644
index 000000000000..8d709f10ea64
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_aead.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_CIPHERCOMMON_AEAD_H
+# define OSSL_PROV_CIPHERCOMMON_AEAD_H
+# pragma once
+
+# define UNINITIALISED_SIZET ((size_t)-1)
+
+# define AEAD_FLAGS (PROV_CIPHER_FLAG_AEAD | PROV_CIPHER_FLAG_CUSTOM_IV)
+
+# define IMPLEMENT_aead_cipher(alg, lc, UCMODE, flags, kbits, blkbits, ivbits) \
+static OSSL_FUNC_cipher_get_params_fn alg##_##kbits##_##lc##_get_params; \
+static int alg##_##kbits##_##lc##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_cipher_generic_get_params(params, EVP_CIPH_##UCMODE##_MODE, \
+ flags, kbits, blkbits, ivbits); \
+} \
+static OSSL_FUNC_cipher_newctx_fn alg##kbits##lc##_newctx; \
+static void * alg##kbits##lc##_newctx(void *provctx) \
+{ \
+ return alg##_##lc##_newctx(provctx, kbits); \
+} \
+static void * alg##kbits##lc##_dupctx(void *src) \
+{ \
+ return alg##_##lc##_dupctx(src); \
+} \
+const OSSL_DISPATCH ossl_##alg##kbits##lc##_functions[] = { \
+ { OSSL_FUNC_CIPHER_NEWCTX, (void (*)(void))alg##kbits##lc##_newctx }, \
+ { OSSL_FUNC_CIPHER_FREECTX, (void (*)(void))alg##_##lc##_freectx }, \
+ { OSSL_FUNC_CIPHER_DUPCTX, (void (*)(void))alg##kbits##lc##_dupctx }, \
+ { OSSL_FUNC_CIPHER_ENCRYPT_INIT, (void (*)(void))ossl_##lc##_einit }, \
+ { OSSL_FUNC_CIPHER_DECRYPT_INIT, (void (*)(void))ossl_##lc##_dinit }, \
+ { OSSL_FUNC_CIPHER_UPDATE, (void (*)(void))ossl_##lc##_stream_update }, \
+ { OSSL_FUNC_CIPHER_FINAL, (void (*)(void))ossl_##lc##_stream_final }, \
+ { OSSL_FUNC_CIPHER_CIPHER, (void (*)(void))ossl_##lc##_cipher }, \
+ { OSSL_FUNC_CIPHER_GET_PARAMS, \
+ (void (*)(void)) alg##_##kbits##_##lc##_get_params }, \
+ { OSSL_FUNC_CIPHER_GET_CTX_PARAMS, \
+ (void (*)(void)) ossl_##lc##_get_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SET_CTX_PARAMS, \
+ (void (*)(void)) ossl_##lc##_set_ctx_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_cipher_generic_gettable_params }, \
+ { OSSL_FUNC_CIPHER_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_aead_gettable_ctx_params }, \
+ { OSSL_FUNC_CIPHER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ossl_cipher_aead_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h
new file mode 100644
index 000000000000..ce1a2aa0e736
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_ccm.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_CIPHERCOMMON_CCM_H
+# define OSSL_PROV_CIPHERCOMMON_CCM_H
+# pragma once
+
+# include "ciphercommon_aead.h"
+
+typedef struct prov_ccm_hw_st PROV_CCM_HW;
+
+# if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+/*-
+ * KMAC-AES parameter block - begin
+ * (see z/Architecture Principles of Operation >= SA22-7832-08)
+ */
+typedef struct S390X_kmac_params_st {
+ union {
+ unsigned long long g[2];
+ unsigned char b[16];
+ } icv;
+ unsigned char k[32];
+} S390X_KMAC_PARAMS;
+/* KMAC-AES parameter block - end */
+# endif
+
+/* Base structure that is shared by AES & ARIA for CCM MODE */
+typedef struct prov_ccm_st {
+ unsigned int enc : 1;
+ unsigned int key_set : 1; /* Set if key initialised */
+ unsigned int iv_set : 1; /* Set if an iv is set */
+ unsigned int tag_set : 1; /* Set if tag is valid */
+ unsigned int len_set : 1; /* Set if message length set */
+ size_t l, m; /* L and M parameters from RFC3610 */
+ size_t keylen;
+ size_t tls_aad_len; /* TLS AAD length */
+ size_t tls_aad_pad_sz;
+ unsigned char iv[GENERIC_BLOCK_SIZE];
+ unsigned char buf[GENERIC_BLOCK_SIZE];
+ CCM128_CONTEXT ccm_ctx;
+ ccm128_f str;
+ const PROV_CCM_HW *hw; /* hardware specific methods */
+} PROV_CCM_CTX;
+
+PROV_CIPHER_FUNC(int, CCM_cipher, (PROV_CCM_CTX *ctx, unsigned char *out, \
+ size_t *padlen, const unsigned char *in, \
+ size_t len));
+PROV_CIPHER_FUNC(int, CCM_setkey, (PROV_CCM_CTX *ctx, \
+ const unsigned char *key, size_t keylen));
+PROV_CIPHER_FUNC(int, CCM_setiv, (PROV_CCM_CTX *dat, \
+ const unsigned char *iv, size_t ivlen, \
+ size_t mlen));
+PROV_CIPHER_FUNC(int, CCM_setaad, (PROV_CCM_CTX *ctx, \
+ const unsigned char *aad, size_t aadlen));
+PROV_CIPHER_FUNC(int, CCM_auth_encrypt, (PROV_CCM_CTX *ctx, \
+ const unsigned char *in, \
+ unsigned char *out, size_t len, \
+ unsigned char *tag, size_t taglen));
+PROV_CIPHER_FUNC(int, CCM_auth_decrypt, (PROV_CCM_CTX *ctx, \
+ const unsigned char *in, \
+ unsigned char *out, size_t len, \
+ unsigned char *tag, size_t taglen));
+PROV_CIPHER_FUNC(int, CCM_gettag, (PROV_CCM_CTX *ctx, \
+ unsigned char *tag, size_t taglen));
+
+/*
+ * CCM Mode internal method table used to handle hardware specific differences,
+ * (and different algorithms).
+ */
+struct prov_ccm_hw_st {
+ OSSL_CCM_setkey_fn setkey;
+ OSSL_CCM_setiv_fn setiv;
+ OSSL_CCM_setaad_fn setaad;
+ OSSL_CCM_auth_encrypt_fn auth_encrypt;
+ OSSL_CCM_auth_decrypt_fn auth_decrypt;
+ OSSL_CCM_gettag_fn gettag;
+};
+
+OSSL_FUNC_cipher_encrypt_init_fn ossl_ccm_einit;
+OSSL_FUNC_cipher_decrypt_init_fn ossl_ccm_dinit;
+OSSL_FUNC_cipher_get_ctx_params_fn ossl_ccm_get_ctx_params;
+OSSL_FUNC_cipher_set_ctx_params_fn ossl_ccm_set_ctx_params;
+OSSL_FUNC_cipher_update_fn ossl_ccm_stream_update;
+OSSL_FUNC_cipher_final_fn ossl_ccm_stream_final;
+OSSL_FUNC_cipher_cipher_fn ossl_ccm_cipher;
+void ossl_ccm_initctx(PROV_CCM_CTX *ctx, size_t keybits, const PROV_CCM_HW *hw);
+
+int ossl_ccm_generic_setiv(PROV_CCM_CTX *ctx, const unsigned char *nonce,
+ size_t nlen, size_t mlen);
+int ossl_ccm_generic_setaad(PROV_CCM_CTX *ctx, const unsigned char *aad,
+ size_t alen);
+int ossl_ccm_generic_gettag(PROV_CCM_CTX *ctx, unsigned char *tag, size_t tlen);
+int ossl_ccm_generic_auth_encrypt(PROV_CCM_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *tag, size_t taglen);
+int ossl_ccm_generic_auth_decrypt(PROV_CCM_CTX *ctx, const unsigned char *in,
+ unsigned char *out, size_t len,
+ unsigned char *expected_tag, size_t taglen);
+
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h b/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h
new file mode 100644
index 000000000000..ee0b23b92785
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ciphercommon_gcm.h
@@ -0,0 +1,133 @@
+
+/*
+ * Copyright 2019-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_CIPHERCOMMON_GCM_H
+# define OSSL_PROV_CIPHERCOMMON_GCM_H
+# pragma once
+
+# include <openssl/aes.h>
+# include "ciphercommon_aead.h"
+
+typedef struct prov_gcm_hw_st PROV_GCM_HW;
+
+# define GCM_IV_DEFAULT_SIZE 12 /* IV's for AES_GCM should normally be 12 bytes */
+# define GCM_IV_MAX_SIZE (1024 / 8)
+# define GCM_TAG_MAX_SIZE 16
+
+# if defined(OPENSSL_CPUID_OBJ) && defined(__s390__)
+/*-
+ * KMA-GCM-AES parameter block - begin
+ * (see z/Architecture Principles of Operation >= SA22-7832-11)
+ */
+typedef struct S390X_kma_params_st {
+ unsigned char reserved[12];
+ union {
+ unsigned int w;
+ unsigned char b[4];
+ } cv; /* 32 bit counter value */
+ union {
+ unsigned long long g[2];
+ unsigned char b[16];
+ } t; /* tag */
+ unsigned char h[16]; /* hash subkey */
+ unsigned long long taadl; /* total AAD length */
+ unsigned long long tpcl; /* total plaintxt/ciphertxt len */
+ union {
+ unsigned long long g[2];
+ unsigned int w[4];
+ } j0; /* initial counter value */
+ unsigned char k[32]; /* key */
+} S390X_KMA_PARAMS;
+
+# endif
+
+typedef struct prov_gcm_ctx_st {
+ unsigned int mode; /* The mode that we are using */
+ size_t keylen;
+ size_t ivlen;
+ size_t taglen;
+ size_t tls_aad_pad_sz;
+ size_t tls_aad_len; /* TLS AAD length */
+ uint64_t tls_enc_records; /* Number of TLS records encrypted */
+
+ /*
+ * num contains the number of bytes of |iv| which are valid for modes that
+ * manage partial blocks themselves.
+ */
+ size_t num;
+ size_t bufsz; /* Number of bytes in buf */
+ uint64_t flags;
+
+ unsigned int iv_state; /* set to one of IV_STATE_XXX */
+ unsigned int enc:1; /* Set to 1 if we are encrypting or 0 otherwise */
+ unsigned int pad:1; /* Whether padding should be used or not */
+ unsigned int key_set:1; /* Set if key initialised */
+ unsigned int iv_gen_rand:1; /* No IV was specified, so generate a rand IV */
+ unsigned int iv_gen:1; /* It is OK to generate IVs */
+
+ unsigned char iv[GCM_IV_MAX_SIZE]; /* Buffer to use for IV's */
+ unsigned char buf[AES_BLOCK_SIZE]; /* Buffer of partial blocks processed via update calls */
+
+ OSSL_LIB_CTX *libctx; /* needed for rand calls */
+ const PROV_GCM_HW *hw; /* hardware specific methods */
+ GCM128_CONTEXT gcm;
+ ctr128_f ctr;
+} PROV_GCM_CTX;
+
+PROV_CIPHER_FUNC(int, GCM_setkey, (PROV_GCM_CTX *ctx, const unsigned char *key,
+ size_t keylen));
+PROV_CIPHER_FUNC(int, GCM_setiv, (PROV_GCM_CTX *dat, const unsigned char *iv,
+ size_t ivlen));
+PROV_CIPHER_FUNC(int, GCM_aadupdate, (PROV_GCM_CTX *ctx,
+ const unsigned char *aad, size_t aadlen));
+PROV_CIPHER_FUNC(int, GCM_cipherupdate, (PROV_GCM_CTX *ctx,
+ const unsigned char *in, size_t len,
+ unsigned char *out));
+PROV_CIPHER_FUNC(int, GCM_cipherfinal, (PROV_GCM_CTX *ctx, unsigned char *tag));
+PROV_CIPHER_FUNC(int, GCM_oneshot, (PROV_GCM_CTX *ctx, unsigned char *aad,
+ size_t aad_len, const unsigned char *in,
+ size_t in_len, unsigned char *out,
+ unsigned char *tag, size_t taglen));
+struct prov_gcm_hw_st {
+ OSSL_GCM_setkey_fn setkey;
+ OSSL_GCM_setiv_fn setiv;
+ OSSL_GCM_aadupdate_fn aadupdate;
+ OSSL_GCM_cipherupdate_fn cipherupdate;
+ OSSL_GCM_cipherfinal_fn cipherfinal;
+ OSSL_GCM_oneshot_fn oneshot;
+};
+
+OSSL_FUNC_cipher_encrypt_init_fn ossl_gcm_einit;
+OSSL_FUNC_cipher_decrypt_init_fn ossl_gcm_dinit;
+OSSL_FUNC_cipher_get_ctx_params_fn ossl_gcm_get_ctx_params;
+OSSL_FUNC_cipher_set_ctx_params_fn ossl_gcm_set_ctx_params;
+OSSL_FUNC_cipher_cipher_fn ossl_gcm_cipher;
+OSSL_FUNC_cipher_update_fn ossl_gcm_stream_update;
+OSSL_FUNC_cipher_final_fn ossl_gcm_stream_final;
+void ossl_gcm_initctx(void *provctx, PROV_GCM_CTX *ctx, size_t keybits,
+ const PROV_GCM_HW *hw);
+
+int ossl_gcm_setiv(PROV_GCM_CTX *ctx, const unsigned char *iv, size_t ivlen);
+int ossl_gcm_aad_update(PROV_GCM_CTX *ctx, const unsigned char *aad,
+ size_t aad_len);
+int ossl_gcm_cipher_final(PROV_GCM_CTX *ctx, unsigned char *tag);
+int ossl_gcm_one_shot(PROV_GCM_CTX *ctx, unsigned char *aad, size_t aad_len,
+ const unsigned char *in, size_t in_len,
+ unsigned char *out, unsigned char *tag, size_t tag_len);
+int ossl_gcm_cipher_update(PROV_GCM_CTX *ctx, const unsigned char *in,
+ size_t len, unsigned char *out);
+
+# define GCM_HW_SET_KEY_CTR_FN(ks, fn_set_enc_key, fn_block, fn_ctr) \
+ fn_set_enc_key(key, keylen * 8, ks); \
+ CRYPTO_gcm128_init(&ctx->gcm, ks, (block128_f)fn_block); \
+ ctx->ctr = (ctr128_f)fn_ctr; \
+ ctx->key_set = 1;
+
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/decoders.h b/crypto/openssl/providers/implementations/include/prov/decoders.h
new file mode 100644
index 000000000000..654efee607d1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/decoders.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+
+int ossl_epki2pki_der_decode(unsigned char *der, long der_len, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+ OSSL_LIB_CTX *libctx, const char *propq);
+
+int ossl_spki2typespki_der_decode(unsigned char *der, long len, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+ OSSL_LIB_CTX *libctx, const char *propq);
diff --git a/crypto/openssl/providers/implementations/include/prov/digestcommon.h b/crypto/openssl/providers/implementations/include/prov/digestcommon.h
new file mode 100644
index 000000000000..332d47349027
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/digestcommon.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROVIDERS_DIGESTCOMMON_H
+# define OSSL_PROVIDERS_DIGESTCOMMON_H
+
+# include <openssl/core_dispatch.h>
+# include <openssl/core_names.h>
+# include <openssl/params.h>
+# include "prov/providercommon.h"
+
+/* Internal flags that can be queried */
+#define PROV_DIGEST_FLAG_XOF 0x0001
+#define PROV_DIGEST_FLAG_ALGID_ABSENT 0x0002
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+#define PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \
+static OSSL_FUNC_digest_get_params_fn name##_get_params; \
+static int name##_get_params(OSSL_PARAM params[]) \
+{ \
+ return ossl_digest_default_get_params(params, blksize, dgstsize, flags); \
+}
+
+#define PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name) \
+{ OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)(void))name##_get_params }, \
+{ OSSL_FUNC_DIGEST_GETTABLE_PARAMS, \
+ (void (*)(void))ossl_digest_default_gettable_params }
+
+# define PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \
+static OSSL_FUNC_digest_final_fn name##_internal_final; \
+static int name##_internal_final(void *ctx, unsigned char *out, size_t *outl, \
+ size_t outsz) \
+{ \
+ if (ossl_prov_is_running() && outsz >= dgstsize && fin(out, ctx)) { \
+ *outl = dgstsize; \
+ return 1; \
+ } \
+ return 0; \
+}
+
+# define PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START( \
+ name, CTX, blksize, dgstsize, flags, upd, fin) \
+static OSSL_FUNC_digest_newctx_fn name##_newctx; \
+static OSSL_FUNC_digest_freectx_fn name##_freectx; \
+static OSSL_FUNC_digest_dupctx_fn name##_dupctx; \
+static void *name##_newctx(void *prov_ctx) \
+{ \
+ CTX *ctx = ossl_prov_is_running() ? OPENSSL_zalloc(sizeof(*ctx)) : NULL; \
+ return ctx; \
+} \
+static void name##_freectx(void *vctx) \
+{ \
+ CTX *ctx = (CTX *)vctx; \
+ OPENSSL_clear_free(ctx, sizeof(*ctx)); \
+} \
+static void *name##_dupctx(void *ctx) \
+{ \
+ CTX *in = (CTX *)ctx; \
+ CTX *ret = ossl_prov_is_running() ? OPENSSL_malloc(sizeof(*ret)) : NULL; \
+ if (ret != NULL) \
+ *ret = *in; \
+ return ret; \
+} \
+static void name##_copyctx(void *voutctx, void *vinctx) \
+{ \
+ CTX *outctx = (CTX *)voutctx; \
+ CTX *inctx = (CTX *)vinctx; \
+ *outctx = *inctx; \
+} \
+PROV_FUNC_DIGEST_FINAL(name, dgstsize, fin) \
+PROV_FUNC_DIGEST_GET_PARAM(name, blksize, dgstsize, flags) \
+const OSSL_DISPATCH ossl_##name##_functions[] = { \
+ { OSSL_FUNC_DIGEST_NEWCTX, (void (*)(void))name##_newctx }, \
+ { OSSL_FUNC_DIGEST_UPDATE, (void (*)(void))upd }, \
+ { OSSL_FUNC_DIGEST_FINAL, (void (*)(void))name##_internal_final }, \
+ { OSSL_FUNC_DIGEST_FREECTX, (void (*)(void))name##_freectx }, \
+ { OSSL_FUNC_DIGEST_DUPCTX, (void (*)(void))name##_dupctx }, \
+ { OSSL_FUNC_DIGEST_COPYCTX, (void (*)(void))name##_copyctx }, \
+ PROV_DISPATCH_FUNC_DIGEST_GET_PARAMS(name)
+
+# define PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END \
+ { 0, NULL } \
+};
+
+# define IMPLEMENT_digest_functions( \
+ name, CTX, blksize, dgstsize, flags, init, upd, fin) \
+static OSSL_FUNC_digest_init_fn name##_internal_init; \
+static int name##_internal_init(void *ctx, \
+ ossl_unused const OSSL_PARAM params[]) \
+{ \
+ return ossl_prov_is_running() && init(ctx); \
+} \
+PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START(name, CTX, blksize, dgstsize, flags, \
+ upd, fin), \
+ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))name##_internal_init }, \
+PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
+
+# define IMPLEMENT_digest_functions_with_settable_ctx( \
+ name, CTX, blksize, dgstsize, flags, init, upd, fin, \
+ settable_ctx_params, set_ctx_params) \
+static OSSL_FUNC_digest_init_fn name##_internal_init; \
+static int name##_internal_init(void *ctx, const OSSL_PARAM params[]) \
+{ \
+ return ossl_prov_is_running() \
+ && init(ctx) \
+ && set_ctx_params(ctx, params); \
+} \
+PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_START(name, CTX, blksize, dgstsize, flags, \
+ upd, fin), \
+ { OSSL_FUNC_DIGEST_INIT, (void (*)(void))name##_internal_init }, \
+ { OSSL_FUNC_DIGEST_SETTABLE_CTX_PARAMS, (void (*)(void))settable_ctx_params }, \
+ { OSSL_FUNC_DIGEST_SET_CTX_PARAMS, (void (*)(void))set_ctx_params }, \
+PROV_DISPATCH_FUNC_DIGEST_CONSTRUCT_END
+
+
+const OSSL_PARAM *ossl_digest_default_gettable_params(void *provctx);
+int ossl_digest_default_get_params(OSSL_PARAM params[], size_t blksz,
+ size_t paramsz, unsigned long flags);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif /* OSSL_PROVIDERS_DIGESTCOMMON_H */
diff --git a/crypto/openssl/providers/implementations/include/prov/ecx.h b/crypto/openssl/providers/implementations/include/prov/ecx.h
new file mode 100644
index 000000000000..3427d154aa8a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ecx.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/types.h"
+
+#ifndef OPENSSL_NO_EC
+
+/* RFC 9180 Labels used for Extract and Expand operations */
+
+/* ASCII: "eae_prk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_EAE_PRK "\x65\x61\x65\x5F\x70\x72\x6B"
+/* ASCII: "shared_secret", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_SHARED_SECRET "\x73\x68\x61\x72\x65\x64\x5F\x73\x65\x63\x72\x65\x74"
+/* ASCII: "dkp_prk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_DKP_PRK "\x64\x6B\x70\x5F\x70\x72\x6B"
+/* ASCII: "candidate", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_CANDIDATE "\x63\x61\x6E\x64\x69\x64\x61\x74\x65"
+/* ASCII: "sk", in hex for EBCDIC compatibility */
+#define OSSL_DHKEM_LABEL_SK "\x73\x6B"
+
+int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
+ const unsigned char *ikm, size_t ikmlen);
+int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *privout,
+ const unsigned char *ikm, size_t ikmlen);
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h b/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h
new file mode 100644
index 000000000000..28aa5bc1adfc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/hmac_drbg.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_HMAC_DRBG_H
+# define OSSL_PROV_HMAC_DRBG_H
+# pragma once
+
+#include <openssl/evp.h>
+#include "prov/provider_util.h"
+
+typedef struct drbg_hmac_st {
+ EVP_MAC_CTX *ctx; /* H(x) = HMAC_hash OR H(x) = KMAC */
+ PROV_DIGEST digest; /* H(x) = hash(x) */
+ size_t blocklen;
+ unsigned char K[EVP_MAX_MD_SIZE];
+ unsigned char V[EVP_MAX_MD_SIZE];
+} PROV_DRBG_HMAC;
+
+int ossl_drbg_hmac_init(PROV_DRBG_HMAC *drbg,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *nonce, size_t nonce_len,
+ const unsigned char *pstr, size_t pstr_len);
+int ossl_drbg_hmac_generate(PROV_DRBG_HMAC *hmac,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len);
+
+#endif /* OSSL_PROV_HMAC_DRBG_H */
diff --git a/crypto/openssl/providers/implementations/include/prov/implementations.h b/crypto/openssl/providers/implementations/include/prov/implementations.h
new file mode 100644
index 000000000000..35b0b0b97406
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/implementations.h
@@ -0,0 +1,849 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core.h>
+#include <openssl/types.h>
+
+/* Digests */
+extern const OSSL_DISPATCH ossl_sha1_functions[];
+extern const OSSL_DISPATCH ossl_sha224_functions[];
+extern const OSSL_DISPATCH ossl_sha256_functions[];
+extern const OSSL_DISPATCH ossl_sha256_192_functions[];
+extern const OSSL_DISPATCH ossl_sha384_functions[];
+extern const OSSL_DISPATCH ossl_sha512_functions[];
+extern const OSSL_DISPATCH ossl_sha512_224_functions[];
+extern const OSSL_DISPATCH ossl_sha512_256_functions[];
+extern const OSSL_DISPATCH ossl_sha3_224_functions[];
+extern const OSSL_DISPATCH ossl_sha3_256_functions[];
+extern const OSSL_DISPATCH ossl_sha3_384_functions[];
+extern const OSSL_DISPATCH ossl_sha3_512_functions[];
+extern const OSSL_DISPATCH ossl_keccak_224_functions[];
+extern const OSSL_DISPATCH ossl_keccak_256_functions[];
+extern const OSSL_DISPATCH ossl_keccak_384_functions[];
+extern const OSSL_DISPATCH ossl_keccak_512_functions[];
+extern const OSSL_DISPATCH ossl_keccak_kmac_128_functions[];
+extern const OSSL_DISPATCH ossl_keccak_kmac_256_functions[];
+extern const OSSL_DISPATCH ossl_shake_128_functions[];
+extern const OSSL_DISPATCH ossl_shake_256_functions[];
+extern const OSSL_DISPATCH ossl_blake2s256_functions[];
+extern const OSSL_DISPATCH ossl_blake2b512_functions[];
+extern const OSSL_DISPATCH ossl_md5_functions[];
+extern const OSSL_DISPATCH ossl_md5_sha1_functions[];
+extern const OSSL_DISPATCH ossl_sm3_functions[];
+extern const OSSL_DISPATCH ossl_md2_functions[];
+extern const OSSL_DISPATCH ossl_md4_functions[];
+extern const OSSL_DISPATCH ossl_mdc2_functions[];
+extern const OSSL_DISPATCH ossl_wp_functions[];
+extern const OSSL_DISPATCH ossl_ripemd160_functions[];
+extern const OSSL_DISPATCH ossl_nullmd_functions[];
+
+/* Ciphers */
+extern const OSSL_DISPATCH ossl_null_functions[];
+extern const OSSL_DISPATCH ossl_aes256ecb_functions[];
+extern const OSSL_DISPATCH ossl_aes192ecb_functions[];
+extern const OSSL_DISPATCH ossl_aes128ecb_functions[];
+extern const OSSL_DISPATCH ossl_aes256cbc_functions[];
+extern const OSSL_DISPATCH ossl_aes192cbc_functions[];
+extern const OSSL_DISPATCH ossl_aes128cbc_functions[];
+extern const OSSL_DISPATCH ossl_aes256cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_aes192cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_aes128cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_aes256ofb_functions[];
+extern const OSSL_DISPATCH ossl_aes192ofb_functions[];
+extern const OSSL_DISPATCH ossl_aes128ofb_functions[];
+extern const OSSL_DISPATCH ossl_aes256cfb_functions[];
+extern const OSSL_DISPATCH ossl_aes192cfb_functions[];
+extern const OSSL_DISPATCH ossl_aes128cfb_functions[];
+extern const OSSL_DISPATCH ossl_aes256cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aes192cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aes128cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aes256cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aes192cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aes128cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aes256ctr_functions[];
+extern const OSSL_DISPATCH ossl_aes192ctr_functions[];
+extern const OSSL_DISPATCH ossl_aes128ctr_functions[];
+extern const OSSL_DISPATCH ossl_aes256xts_functions[];
+extern const OSSL_DISPATCH ossl_aes128xts_functions[];
+#ifndef OPENSSL_NO_OCB
+extern const OSSL_DISPATCH ossl_aes256ocb_functions[];
+extern const OSSL_DISPATCH ossl_aes192ocb_functions[];
+extern const OSSL_DISPATCH ossl_aes128ocb_functions[];
+#endif /* OPENSSL_NO_OCB */
+extern const OSSL_DISPATCH ossl_aes256gcm_functions[];
+extern const OSSL_DISPATCH ossl_aes192gcm_functions[];
+extern const OSSL_DISPATCH ossl_aes128gcm_functions[];
+extern const OSSL_DISPATCH ossl_aes256ccm_functions[];
+extern const OSSL_DISPATCH ossl_aes192ccm_functions[];
+extern const OSSL_DISPATCH ossl_aes128ccm_functions[];
+extern const OSSL_DISPATCH ossl_aes256wrap_functions[];
+extern const OSSL_DISPATCH ossl_aes192wrap_functions[];
+extern const OSSL_DISPATCH ossl_aes128wrap_functions[];
+extern const OSSL_DISPATCH ossl_aes256wrappad_functions[];
+extern const OSSL_DISPATCH ossl_aes192wrappad_functions[];
+extern const OSSL_DISPATCH ossl_aes128wrappad_functions[];
+extern const OSSL_DISPATCH ossl_aes256wrapinv_functions[];
+extern const OSSL_DISPATCH ossl_aes192wrapinv_functions[];
+extern const OSSL_DISPATCH ossl_aes128wrapinv_functions[];
+extern const OSSL_DISPATCH ossl_aes256wrappadinv_functions[];
+extern const OSSL_DISPATCH ossl_aes192wrappadinv_functions[];
+extern const OSSL_DISPATCH ossl_aes128wrappadinv_functions[];
+extern const OSSL_DISPATCH ossl_aes256cbc_hmac_sha1_functions[];
+extern const OSSL_DISPATCH ossl_aes128cbc_hmac_sha1_functions[];
+extern const OSSL_DISPATCH ossl_aes256cbc_hmac_sha256_functions[];
+extern const OSSL_DISPATCH ossl_aes128cbc_hmac_sha256_functions[];
+
+#ifndef OPENSSL_NO_ARIA
+extern const OSSL_DISPATCH ossl_aria256gcm_functions[];
+extern const OSSL_DISPATCH ossl_aria192gcm_functions[];
+extern const OSSL_DISPATCH ossl_aria128gcm_functions[];
+extern const OSSL_DISPATCH ossl_aria256ccm_functions[];
+extern const OSSL_DISPATCH ossl_aria192ccm_functions[];
+extern const OSSL_DISPATCH ossl_aria128ccm_functions[];
+extern const OSSL_DISPATCH ossl_aria256ecb_functions[];
+extern const OSSL_DISPATCH ossl_aria192ecb_functions[];
+extern const OSSL_DISPATCH ossl_aria128ecb_functions[];
+extern const OSSL_DISPATCH ossl_aria256cbc_functions[];
+extern const OSSL_DISPATCH ossl_aria192cbc_functions[];
+extern const OSSL_DISPATCH ossl_aria128cbc_functions[];
+extern const OSSL_DISPATCH ossl_aria256ofb_functions[];
+extern const OSSL_DISPATCH ossl_aria192ofb_functions[];
+extern const OSSL_DISPATCH ossl_aria128ofb_functions[];
+extern const OSSL_DISPATCH ossl_aria256cfb_functions[];
+extern const OSSL_DISPATCH ossl_aria192cfb_functions[];
+extern const OSSL_DISPATCH ossl_aria128cfb_functions[];
+extern const OSSL_DISPATCH ossl_aria256cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aria192cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aria128cfb1_functions[];
+extern const OSSL_DISPATCH ossl_aria256cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aria192cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aria128cfb8_functions[];
+extern const OSSL_DISPATCH ossl_aria256ctr_functions[];
+extern const OSSL_DISPATCH ossl_aria192ctr_functions[];
+extern const OSSL_DISPATCH ossl_aria128ctr_functions[];
+#endif /* OPENSSL_NO_ARIA */
+#ifndef OPENSSL_NO_CAMELLIA
+extern const OSSL_DISPATCH ossl_camellia256ecb_functions[];
+extern const OSSL_DISPATCH ossl_camellia192ecb_functions[];
+extern const OSSL_DISPATCH ossl_camellia128ecb_functions[];
+extern const OSSL_DISPATCH ossl_camellia256cbc_functions[];
+extern const OSSL_DISPATCH ossl_camellia192cbc_functions[];
+extern const OSSL_DISPATCH ossl_camellia128cbc_functions[];
+extern const OSSL_DISPATCH ossl_camellia256cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_camellia192cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_camellia128cbc_cts_functions[];
+extern const OSSL_DISPATCH ossl_camellia256ofb_functions[];
+extern const OSSL_DISPATCH ossl_camellia192ofb_functions[];
+extern const OSSL_DISPATCH ossl_camellia128ofb_functions[];
+extern const OSSL_DISPATCH ossl_camellia256cfb_functions[];
+extern const OSSL_DISPATCH ossl_camellia192cfb_functions[];
+extern const OSSL_DISPATCH ossl_camellia128cfb_functions[];
+extern const OSSL_DISPATCH ossl_camellia256cfb1_functions[];
+extern const OSSL_DISPATCH ossl_camellia192cfb1_functions[];
+extern const OSSL_DISPATCH ossl_camellia128cfb1_functions[];
+extern const OSSL_DISPATCH ossl_camellia256cfb8_functions[];
+extern const OSSL_DISPATCH ossl_camellia192cfb8_functions[];
+extern const OSSL_DISPATCH ossl_camellia128cfb8_functions[];
+extern const OSSL_DISPATCH ossl_camellia256ctr_functions[];
+extern const OSSL_DISPATCH ossl_camellia192ctr_functions[];
+extern const OSSL_DISPATCH ossl_camellia128ctr_functions[];
+#endif /* OPENSSL_NO_CAMELLIA */
+#ifndef OPENSSL_NO_BF
+extern const OSSL_DISPATCH ossl_blowfish128ecb_functions[];
+extern const OSSL_DISPATCH ossl_blowfish128cbc_functions[];
+extern const OSSL_DISPATCH ossl_blowfish128ofb64_functions[];
+extern const OSSL_DISPATCH ossl_blowfish128cfb64_functions[];
+#endif /* OPENSSL_NO_BF */
+#ifndef OPENSSL_NO_IDEA
+extern const OSSL_DISPATCH ossl_idea128ecb_functions[];
+extern const OSSL_DISPATCH ossl_idea128cbc_functions[];
+extern const OSSL_DISPATCH ossl_idea128ofb64_functions[];
+extern const OSSL_DISPATCH ossl_idea128cfb64_functions[];
+#endif /* OPENSSL_NO_IDEA */
+#ifndef OPENSSL_NO_CAST
+extern const OSSL_DISPATCH ossl_cast5128ecb_functions[];
+extern const OSSL_DISPATCH ossl_cast5128cbc_functions[];
+extern const OSSL_DISPATCH ossl_cast5128ofb64_functions[];
+extern const OSSL_DISPATCH ossl_cast5128cfb64_functions[];
+#endif /* OPENSSL_NO_CAST */
+#ifndef OPENSSL_NO_SEED
+extern const OSSL_DISPATCH ossl_seed128ecb_functions[];
+extern const OSSL_DISPATCH ossl_seed128cbc_functions[];
+extern const OSSL_DISPATCH ossl_seed128ofb128_functions[];
+extern const OSSL_DISPATCH ossl_seed128cfb128_functions[];
+#endif /* OPENSSL_NO_SEED */
+#ifndef OPENSSL_NO_SM4
+extern const OSSL_DISPATCH ossl_sm4128gcm_functions[];
+extern const OSSL_DISPATCH ossl_sm4128ccm_functions[];
+extern const OSSL_DISPATCH ossl_sm4128ecb_functions[];
+extern const OSSL_DISPATCH ossl_sm4128cbc_functions[];
+extern const OSSL_DISPATCH ossl_sm4128ctr_functions[];
+extern const OSSL_DISPATCH ossl_sm4128ofb128_functions[];
+extern const OSSL_DISPATCH ossl_sm4128cfb128_functions[];
+extern const OSSL_DISPATCH ossl_sm4128xts_functions[];
+#endif /* OPENSSL_NO_SM4 */
+#ifndef OPENSSL_NO_RC5
+extern const OSSL_DISPATCH ossl_rc5128ecb_functions[];
+extern const OSSL_DISPATCH ossl_rc5128cbc_functions[];
+extern const OSSL_DISPATCH ossl_rc5128ofb64_functions[];
+extern const OSSL_DISPATCH ossl_rc5128cfb64_functions[];
+#endif /* OPENSSL_NO_RC5 */
+#ifndef OPENSSL_NO_RC2
+extern const OSSL_DISPATCH ossl_rc2128ecb_functions[];
+extern const OSSL_DISPATCH ossl_rc2128cbc_functions[];
+extern const OSSL_DISPATCH ossl_rc240cbc_functions[];
+extern const OSSL_DISPATCH ossl_rc264cbc_functions[];
+extern const OSSL_DISPATCH ossl_rc2128cfb128_functions[];
+extern const OSSL_DISPATCH ossl_rc2128ofb128_functions[];
+#endif /* OPENSSL_NO_RC2 */
+#ifndef OPENSSL_NO_DES
+extern const OSSL_DISPATCH ossl_tdes_ede3_ecb_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede3_cbc_functions[];
+# ifndef FIPS_MODULE
+extern const OSSL_DISPATCH ossl_tdes_ede3_ofb_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede3_cfb_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede3_cfb8_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede3_cfb1_functions[];
+
+extern const OSSL_DISPATCH ossl_tdes_ede2_ecb_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede2_cbc_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede2_ofb_functions[];
+extern const OSSL_DISPATCH ossl_tdes_ede2_cfb_functions[];
+
+extern const OSSL_DISPATCH ossl_tdes_desx_cbc_functions[];
+extern const OSSL_DISPATCH ossl_tdes_wrap_cbc_functions[];
+
+extern const OSSL_DISPATCH ossl_des_ecb_functions[];
+extern const OSSL_DISPATCH ossl_des_cbc_functions[];
+extern const OSSL_DISPATCH ossl_des_ofb64_functions[];
+extern const OSSL_DISPATCH ossl_des_cfb64_functions[];
+extern const OSSL_DISPATCH ossl_des_cfb1_functions[];
+extern const OSSL_DISPATCH ossl_des_cfb8_functions[];
+# endif /* FIPS_MODULE */
+#endif /* OPENSSL_NO_DES */
+
+#ifndef OPENSSL_NO_RC4
+extern const OSSL_DISPATCH ossl_rc440_functions[];
+extern const OSSL_DISPATCH ossl_rc4128_functions[];
+# ifndef OPENSSL_NO_MD5
+extern const OSSL_DISPATCH ossl_rc4_hmac_ossl_md5_functions[];
+# endif /* OPENSSL_NO_MD5 */
+#endif /* OPENSSL_NO_RC4 */
+#ifndef OPENSSL_NO_CHACHA
+extern const OSSL_DISPATCH ossl_chacha20_functions[];
+# ifndef OPENSSL_NO_POLY1305
+extern const OSSL_DISPATCH ossl_chacha20_ossl_poly1305_functions[];
+# endif /* OPENSSL_NO_POLY1305 */
+#endif /* OPENSSL_NO_CHACHA */
+
+
+#ifndef OPENSSL_NO_SIV
+extern const OSSL_DISPATCH ossl_aes128siv_functions[];
+extern const OSSL_DISPATCH ossl_aes192siv_functions[];
+extern const OSSL_DISPATCH ossl_aes256siv_functions[];
+extern const OSSL_DISPATCH ossl_aes128gcm_siv_functions[];
+extern const OSSL_DISPATCH ossl_aes192gcm_siv_functions[];
+extern const OSSL_DISPATCH ossl_aes256gcm_siv_functions[];
+#endif /* OPENSSL_NO_SIV */
+
+/* MACs */
+extern const OSSL_DISPATCH ossl_blake2bmac_functions[];
+extern const OSSL_DISPATCH ossl_blake2smac_functions[];
+extern const OSSL_DISPATCH ossl_cmac_functions[];
+extern const OSSL_DISPATCH ossl_gmac_functions[];
+extern const OSSL_DISPATCH ossl_hmac_functions[];
+#ifdef FIPS_MODULE
+extern const OSSL_DISPATCH ossl_hmac_internal_functions[];
+extern const OSSL_DISPATCH ossl_kmac128_internal_functions[];
+extern const OSSL_DISPATCH ossl_kmac256_internal_functions[];
+#endif
+extern const OSSL_DISPATCH ossl_kmac128_functions[];
+extern const OSSL_DISPATCH ossl_kmac256_functions[];
+extern const OSSL_DISPATCH ossl_siphash_functions[];
+extern const OSSL_DISPATCH ossl_poly1305_functions[];
+
+/* KDFs / PRFs */
+extern const OSSL_DISPATCH ossl_kdf_pbkdf1_functions[];
+extern const OSSL_DISPATCH ossl_kdf_pbkdf2_functions[];
+extern const OSSL_DISPATCH ossl_kdf_pvk_functions[];
+extern const OSSL_DISPATCH ossl_kdf_pkcs12_functions[];
+#ifndef OPENSSL_NO_SCRYPT
+extern const OSSL_DISPATCH ossl_kdf_scrypt_functions[];
+#endif
+extern const OSSL_DISPATCH ossl_kdf_tls1_prf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_hkdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_sshkdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_sskdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_kbkdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_x942_kdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_krb5kdf_functions[];
+extern const OSSL_DISPATCH ossl_kdf_hmac_drbg_functions[];
+#ifndef OPENSSL_NO_ARGON2
+extern const OSSL_DISPATCH ossl_kdf_argon2i_functions[];
+extern const OSSL_DISPATCH ossl_kdf_argon2d_functions[];
+extern const OSSL_DISPATCH ossl_kdf_argon2id_functions[];
+#endif
+
+/* RNGs */
+extern const OSSL_DISPATCH ossl_test_rng_functions[];
+extern const OSSL_DISPATCH ossl_seed_src_functions[];
+extern const OSSL_DISPATCH ossl_jitter_functions[];
+extern const OSSL_DISPATCH ossl_crng_test_functions[];
+extern const OSSL_DISPATCH ossl_drbg_hash_functions[];
+extern const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[];
+extern const OSSL_DISPATCH ossl_drbg_ctr_functions[];
+extern const OSSL_DISPATCH crngt_functions[];
+
+/* Key management */
+extern const OSSL_DISPATCH ossl_dh_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_dhx_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_dsa_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_rsa_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_kdf_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[];
+#ifndef OPENSSL_NO_EC
+extern const OSSL_DISPATCH ossl_ec_keymgmt_functions[];
+# ifndef OPENSSL_NO_ECX
+extern const OSSL_DISPATCH ossl_x25519_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_x448_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ed448_keymgmt_functions[];
+# endif
+# ifndef OPENSSL_NO_SM2
+extern const OSSL_DISPATCH ossl_sm2_keymgmt_functions[];
+# endif
+#endif
+extern const OSSL_DISPATCH ossl_ml_dsa_44_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_keymgmt_functions[];
+#ifndef OPENSSL_NO_ML_KEM
+extern const OSSL_DISPATCH ossl_ml_kem_512_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_keymgmt_functions[];
+# ifndef OPENSSL_NO_EC
+# ifndef OPENSSL_NO_ECX
+extern const OSSL_DISPATCH ossl_mlx_x25519_kem_kmgmt_functions[];
+extern const OSSL_DISPATCH ossl_mlx_x448_kem_kmgmt_functions[];
+# endif
+extern const OSSL_DISPATCH ossl_mlx_p256_kem_kmgmt_functions[];
+extern const OSSL_DISPATCH ossl_mlx_p384_kem_kmgmt_functions[];
+# endif
+#endif
+#ifndef OPENSSL_NO_SLH_DSA
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_keymgmt_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_keymgmt_functions[];
+#endif /* OPENSSL_NO_SLH_DSA */
+
+/* Key Exchange */
+extern const OSSL_DISPATCH ossl_dh_keyexch_functions[];
+#ifndef OPENSSL_NO_EC
+extern const OSSL_DISPATCH ossl_ecdh_keyexch_functions[];
+# ifndef OPENSSL_NO_ECX
+extern const OSSL_DISPATCH ossl_x25519_keyexch_functions[];
+extern const OSSL_DISPATCH ossl_x448_keyexch_functions[];
+# endif
+#endif
+extern const OSSL_DISPATCH ossl_kdf_tls1_prf_keyexch_functions[];
+extern const OSSL_DISPATCH ossl_kdf_hkdf_keyexch_functions[];
+extern const OSSL_DISPATCH ossl_kdf_scrypt_keyexch_functions[];
+
+/* Signature */
+extern const OSSL_DISPATCH ossl_dsa_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha1_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha224_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha256_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha384_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha512_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha3_224_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha3_256_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha3_384_signature_functions[];
+extern const OSSL_DISPATCH ossl_dsa_sha3_512_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_signature_functions[];
+#ifndef OPENSSL_NO_MD5
+extern const OSSL_DISPATCH ossl_rsa_md5_signature_functions[];
+#endif
+#ifndef OPENSSL_NO_RMD160
+extern const OSSL_DISPATCH ossl_rsa_ripemd160_signature_functions[];
+#endif
+extern const OSSL_DISPATCH ossl_rsa_sha1_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha224_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha256_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha384_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha512_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha512_224_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha512_256_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha3_224_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha3_256_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha3_384_signature_functions[];
+extern const OSSL_DISPATCH ossl_rsa_sha3_512_signature_functions[];
+#ifndef OPENSSL_NO_SM3
+extern const OSSL_DISPATCH ossl_rsa_sm3_signature_functions[];
+#endif
+extern const OSSL_DISPATCH ossl_ed25519_signature_functions[];
+extern const OSSL_DISPATCH ossl_ed25519ph_signature_functions[];
+extern const OSSL_DISPATCH ossl_ed25519ctx_signature_functions[];
+extern const OSSL_DISPATCH ossl_ed448_signature_functions[];
+extern const OSSL_DISPATCH ossl_ed448ph_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha1_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha224_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha256_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha384_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha512_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha3_224_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha3_256_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha3_384_signature_functions[];
+extern const OSSL_DISPATCH ossl_ecdsa_sha3_512_signature_functions[];
+extern const OSSL_DISPATCH ossl_mac_legacy_hmac_signature_functions[];
+extern const OSSL_DISPATCH ossl_mac_legacy_siphash_signature_functions[];
+extern const OSSL_DISPATCH ossl_mac_legacy_poly1305_signature_functions[];
+extern const OSSL_DISPATCH ossl_mac_legacy_cmac_signature_functions[];
+extern const OSSL_DISPATCH ossl_sm2_signature_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_signature_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_signature_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_signature_functions[];
+#ifndef OPENSSL_NO_SLH_DSA
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_signature_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_signature_functions[];
+#endif /* OPENSSL_NO_SLH_DSA */
+
+/* Asym Cipher */
+extern const OSSL_DISPATCH ossl_rsa_asym_cipher_functions[];
+#ifndef OPENSSL_NO_SM2
+extern const OSSL_DISPATCH ossl_sm2_asym_cipher_functions[];
+#endif
+
+/* Asym Key encapsulation */
+extern const OSSL_DISPATCH ossl_rsa_asym_kem_functions[];
+#ifndef OPENSSL_NO_EC
+extern const OSSL_DISPATCH ossl_ec_asym_kem_functions[];
+# ifndef OPENSSL_NO_ECX
+extern const OSSL_DISPATCH ossl_ecx_asym_kem_functions[];
+# endif
+#endif
+#ifndef OPENSSL_NO_ML_KEM
+extern const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[];
+# ifndef OPENSSL_NO_EC
+extern const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[];
+# endif
+#endif
+
+/* Encoders */
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PKCS1_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_RSA_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_RSA_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_msblob_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_pvk_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsa_to_type_specific_keypair_pem_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PKCS1_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_rsapss_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_dh_to_DH_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_DH_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS3_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PKCS3_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_type_specific_params_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dh_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_dhx_to_DHX_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_DHX_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_X9_42_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_X9_42_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_type_specific_params_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dhx_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_dsa_to_DSA_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_DSA_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_type_specific_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_type_specific_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_msblob_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_pvk_encoder_functions[];
+extern const OSSL_DISPATCH ossl_dsa_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_ec_to_EC_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_EC_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_blob_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_X9_62_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_X9_62_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_type_specific_no_pub_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ec_to_text_encoder_functions[];
+
+#ifndef OPENSSL_NO_SM2
+extern const OSSL_DISPATCH ossl_sm2_to_SM2_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_SM2_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_blob_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_type_specific_no_pub_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_type_specific_no_pub_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_sm2_to_text_encoder_functions[];
+#endif
+
+extern const OSSL_DISPATCH ossl_ed25519_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed25519_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_ed448_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ed448_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_x25519_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x25519_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_x448_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_x448_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_44_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_65_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_dsa_87_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_PrivateKeyInfo_pem_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_OSSL_current_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_OSSL_current_der_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_128f_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_192f_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_sha2_256f_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_128f_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_192f_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256s_to_text_encoder_functions[];
+extern const OSSL_DISPATCH ossl_slh_dsa_shake_256f_to_text_encoder_functions[];
+
+/* Decoders */
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dh_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DH_der_to_dh_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_params_der_to_dhx_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DHX_der_to_dhx_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_DSA_der_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_msblob_to_dsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_pvk_to_dsa_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_no_pub_der_to_ec_decoder_functions[];
+extern const OSSL_DISPATCH ossl_EC_der_to_ec_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_x25519_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x25519_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_x448_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_x448_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ed25519_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed25519_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ed448_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ed448_decoder_functions[];
+
+#ifndef OPENSSL_NO_ML_KEM
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_512_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_512_decoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_512_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_768_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_768_decoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_768_to_text_encoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_kem_1024_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_kem_1024_decoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_EncryptedPrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_EncryptedPrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_PrivateKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_PrivateKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_SubjectPublicKeyInfo_der_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_SubjectPublicKeyInfo_pem_encoder_functions[];
+extern const OSSL_DISPATCH ossl_ml_kem_1024_to_text_encoder_functions[];
+#endif
+
+#ifndef OPENSSL_NO_SM2
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_sm2_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_sm2_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_no_pub_der_to_sm2_decoder_functions[];
+#endif
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_128s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_128f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_192s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_192f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_256s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_sha2_256f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_128s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_128f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_192s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_192f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_256s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_slh_dsa_shake_256f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_128s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_128f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_192s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_192f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_256s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_sha2_256f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_128s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_128f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_192s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_192f_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_256s_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_slh_dsa_shake_256f_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_type_specific_keypair_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_RSA_der_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_msblob_to_rsa_decoder_functions[];
+extern const OSSL_DISPATCH ossl_pvk_to_rsa_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_rsapss_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_rsapss_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_EncryptedPrivateKeyInfo_der_to_der_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_der_decoder_functions[];
+extern const OSSL_DISPATCH ossl_pem_to_der_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_file_store_functions[];
+extern const OSSL_DISPATCH ossl_winstore_store_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_44_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_44_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_65_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_65_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_PrivateKeyInfo_der_to_ml_dsa_87_decoder_functions[];
+extern const OSSL_DISPATCH ossl_SubjectPublicKeyInfo_der_to_ml_dsa_87_decoder_functions[];
+
+extern const OSSL_DISPATCH ossl_generic_skeymgmt_functions[];
+extern const OSSL_DISPATCH ossl_aes_skeymgmt_functions[];
diff --git a/crypto/openssl/providers/implementations/include/prov/kdfexchange.h b/crypto/openssl/providers/implementations/include/prov/kdfexchange.h
new file mode 100644
index 000000000000..cf08f785ee0c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/kdfexchange.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <openssl/crypto.h>
+#include "internal/refcount.h"
+
+struct kdf_data_st {
+ OSSL_LIB_CTX *libctx;
+ CRYPTO_REF_COUNT refcnt;
+};
+
+typedef struct kdf_data_st KDF_DATA;
+
+KDF_DATA *ossl_kdf_data_new(void *provctx);
+void ossl_kdf_data_free(KDF_DATA *kdfdata);
+int ossl_kdf_data_up_ref(KDF_DATA *kdfdata);
diff --git a/crypto/openssl/providers/implementations/include/prov/macsignature.h b/crypto/openssl/providers/implementations/include/prov/macsignature.h
new file mode 100644
index 000000000000..e13ff362ce00
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/macsignature.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <openssl/crypto.h>
+#include "internal/refcount.h"
+#include "prov/provider_util.h"
+
+struct mac_key_st {
+ OSSL_LIB_CTX *libctx;
+ CRYPTO_REF_COUNT refcnt;
+ unsigned char *priv_key;
+ size_t priv_key_len;
+ PROV_CIPHER cipher;
+ char *properties;
+ int cmac;
+};
+
+typedef struct mac_key_st MAC_KEY;
+
+MAC_KEY *ossl_mac_key_new(OSSL_LIB_CTX *libctx, int cmac);
+void ossl_mac_key_free(MAC_KEY *mackey);
+int ossl_mac_key_up_ref(MAC_KEY *mackey);
diff --git a/crypto/openssl/providers/implementations/include/prov/md5_sha1.h b/crypto/openssl/providers/implementations/include/prov/md5_sha1.h
new file mode 100644
index 000000000000..181267d6b19f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/md5_sha1.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROV_MD5_SHA1_H
+# define OSSL_PROV_MD5_SHA1_H
+
+# include <openssl/opensslconf.h>
+
+# ifndef OPENSSL_NO_MD5
+# include <openssl/e_os2.h>
+# include <stddef.h>
+# include <openssl/md5.h>
+# include <openssl/sha.h>
+
+# define MD5_SHA1_DIGEST_LENGTH (MD5_DIGEST_LENGTH + SHA_DIGEST_LENGTH)
+# define MD5_SHA1_CBLOCK MD5_CBLOCK
+
+typedef struct md5_sha1_st {
+ MD5_CTX md5;
+ SHA_CTX sha1;
+} MD5_SHA1_CTX;
+
+int ossl_md5_sha1_init(MD5_SHA1_CTX *mctx);
+int ossl_md5_sha1_update(MD5_SHA1_CTX *mctx, const void *data, size_t count);
+int ossl_md5_sha1_final(unsigned char *md, MD5_SHA1_CTX *mctx);
+int ossl_md5_sha1_ctrl(MD5_SHA1_CTX *mctx, int cmd, int mslen, void *ms);
+
+# endif /* OPENSSL_NO_MD5 */
+
+#endif /* OSSL_PROV_MD5_SHA1_H */
diff --git a/crypto/openssl/providers/implementations/include/prov/ml_dsa.h b/crypto/openssl/providers/implementations/include/prov/ml_dsa.h
new file mode 100644
index 000000000000..fca8a306253f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ml_dsa.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/ml_dsa.h"
+#include "prov/provider_ctx.h"
+
+ML_DSA_KEY *
+ossl_prov_ml_dsa_new(PROV_CTX *provctx, const char *propq, int evp_type);
diff --git a/crypto/openssl/providers/implementations/include/prov/ml_kem.h b/crypto/openssl/providers/implementations/include/prov/ml_kem.h
new file mode 100644
index 000000000000..b82ef1baa03a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/ml_kem.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "crypto/ml_kem.h"
+#include "prov/provider_ctx.h"
+
+ML_KEM_KEY *
+ossl_prov_ml_kem_new(PROV_CTX *provctx, const char *propq, int evp_type);
diff --git a/crypto/openssl/providers/implementations/include/prov/mlx_kem.h b/crypto/openssl/providers/implementations/include/prov/mlx_kem.h
new file mode 100644
index 000000000000..df8317cda08d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/mlx_kem.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_MLX_KEM_H
+# define OSSL_MLX_KEM_H
+# pragma once
+
+#include <openssl/evp.h>
+#include <openssl/ml_kem.h>
+#include <crypto/ml_kem.h>
+#include <crypto/ecx.h>
+
+typedef struct ecdh_vinfo_st {
+ const char *algorithm_name;
+ const char *group_name;
+ size_t pubkey_bytes;
+ size_t prvkey_bytes;
+ size_t shsec_bytes;
+ int ml_kem_slot;
+ int ml_kem_variant;
+} ECDH_VINFO;
+
+typedef struct mlx_key_st {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ const ML_KEM_VINFO *minfo;
+ const ECDH_VINFO *xinfo;
+ EVP_PKEY *mkey;
+ EVP_PKEY *xkey;
+ unsigned int state;
+} MLX_KEY;
+
+#define MLX_HAVE_NOKEYS 0
+#define MLX_HAVE_PUBKEY 1
+#define MLX_HAVE_PRVKEY 2
+
+/* Both key parts have whatever the ML-KEM component has */
+#define mlx_kem_have_pubkey(key) ((key)->state > 0)
+#define mlx_kem_have_prvkey(key) ((key)->state > 1)
+
+#endif
diff --git a/crypto/openssl/providers/implementations/include/prov/names.h b/crypto/openssl/providers/implementations/include/prov/names.h
new file mode 100644
index 000000000000..3b747ec92c04
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/names.h
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2021-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Macros for use as names and descriptions in our providers' OSSL_ALGORITHM.
+ *
+ * All the strings are formatted the same way:
+ *
+ * Our primary name[:other names][:numeric OID]
+ *
+ * 'other names' include historical OpenSSL names, NIST names, ASN.1 OBJECT
+ * IDENTIFIER names, and commonly known aliases.
+ *
+ * Where it matters, our primary names follow this format:
+ *
+ * ALGNAME[VERSION?][-SUBNAME[VERSION?]?][-SIZE?][-MODE?]
+ *
+ * VERSION is only present if there are multiple versions of
+ * an alg (MD2, MD4, MD5). It may be omitted if there is only
+ * one version (if a subsequent version is released in the future,
+ * we can always change the canonical name, and add the old name
+ * as an alias).
+ *
+ * SUBNAME may be present where we are combining multiple
+ * algorithms together, e.g. MD5-SHA1.
+ *
+ * SIZE is only present if multiple versions of an algorithm exist
+ * with different sizes (e.g. AES-128-CBC, AES-256-CBC)
+ *
+ * MODE is only present where applicable.
+ */
+
+/*-
+ * Symmetric ciphers
+ * -----------------
+ */
+#define PROV_NAMES_AES "AES:2.16.840.1.101.3.4.1"
+#define PROV_DESCS_AES "OpenSSL AES opaque secret key"
+#define PROV_NAMES_GENERIC "GENERIC-SECRET"
+#define PROV_DESCS_GENERIC "OpenSSL generic opaque secret key"
+
+#define PROV_NAMES_AES_256_ECB "AES-256-ECB:2.16.840.1.101.3.4.1.41"
+#define PROV_NAMES_AES_192_ECB "AES-192-ECB:2.16.840.1.101.3.4.1.21"
+#define PROV_NAMES_AES_128_ECB "AES-128-ECB:2.16.840.1.101.3.4.1.1"
+#define PROV_NAMES_AES_256_CBC "AES-256-CBC:AES256:2.16.840.1.101.3.4.1.42"
+#define PROV_NAMES_AES_192_CBC "AES-192-CBC:AES192:2.16.840.1.101.3.4.1.22"
+#define PROV_NAMES_AES_128_CBC "AES-128-CBC:AES128:2.16.840.1.101.3.4.1.2"
+#define PROV_NAMES_AES_256_CBC_CTS "AES-256-CBC-CTS"
+#define PROV_NAMES_AES_192_CBC_CTS "AES-192-CBC-CTS"
+#define PROV_NAMES_AES_128_CBC_CTS "AES-128-CBC-CTS"
+#define PROV_NAMES_AES_256_OFB "AES-256-OFB:2.16.840.1.101.3.4.1.43"
+#define PROV_NAMES_AES_192_OFB "AES-192-OFB:2.16.840.1.101.3.4.1.23"
+#define PROV_NAMES_AES_128_OFB "AES-128-OFB:2.16.840.1.101.3.4.1.3"
+#define PROV_NAMES_AES_256_CFB "AES-256-CFB:2.16.840.1.101.3.4.1.44"
+#define PROV_NAMES_AES_192_CFB "AES-192-CFB:2.16.840.1.101.3.4.1.24"
+#define PROV_NAMES_AES_128_CFB "AES-128-CFB:2.16.840.1.101.3.4.1.4"
+#define PROV_NAMES_AES_256_CFB1 "AES-256-CFB1"
+#define PROV_NAMES_AES_192_CFB1 "AES-192-CFB1"
+#define PROV_NAMES_AES_128_CFB1 "AES-128-CFB1"
+#define PROV_NAMES_AES_256_CFB8 "AES-256-CFB8"
+#define PROV_NAMES_AES_192_CFB8 "AES-192-CFB8"
+#define PROV_NAMES_AES_128_CFB8 "AES-128-CFB8"
+#define PROV_NAMES_AES_256_CTR "AES-256-CTR"
+#define PROV_NAMES_AES_192_CTR "AES-192-CTR"
+#define PROV_NAMES_AES_128_CTR "AES-128-CTR"
+#define PROV_NAMES_AES_256_XTS "AES-256-XTS:1.3.111.2.1619.0.1.2"
+#define PROV_NAMES_AES_128_XTS "AES-128-XTS:1.3.111.2.1619.0.1.1"
+#define PROV_NAMES_AES_256_GCM "AES-256-GCM:id-aes256-GCM:2.16.840.1.101.3.4.1.46"
+#define PROV_NAMES_AES_192_GCM "AES-192-GCM:id-aes192-GCM:2.16.840.1.101.3.4.1.26"
+#define PROV_NAMES_AES_128_GCM "AES-128-GCM:id-aes128-GCM:2.16.840.1.101.3.4.1.6"
+#define PROV_NAMES_AES_256_CCM "AES-256-CCM:id-aes256-CCM:2.16.840.1.101.3.4.1.47"
+#define PROV_NAMES_AES_192_CCM "AES-192-CCM:id-aes192-CCM:2.16.840.1.101.3.4.1.27"
+#define PROV_NAMES_AES_128_CCM "AES-128-CCM:id-aes128-CCM:2.16.840.1.101.3.4.1.7"
+#define PROV_NAMES_AES_256_WRAP "AES-256-WRAP:id-aes256-wrap:AES256-WRAP:2.16.840.1.101.3.4.1.45"
+#define PROV_NAMES_AES_192_WRAP "AES-192-WRAP:id-aes192-wrap:AES192-WRAP:2.16.840.1.101.3.4.1.25"
+#define PROV_NAMES_AES_128_WRAP "AES-128-WRAP:id-aes128-wrap:AES128-WRAP:2.16.840.1.101.3.4.1.5"
+#define PROV_NAMES_AES_256_WRAP_PAD "AES-256-WRAP-PAD:id-aes256-wrap-pad:AES256-WRAP-PAD:2.16.840.1.101.3.4.1.48"
+#define PROV_NAMES_AES_192_WRAP_PAD "AES-192-WRAP-PAD:id-aes192-wrap-pad:AES192-WRAP-PAD:2.16.840.1.101.3.4.1.28"
+#define PROV_NAMES_AES_128_WRAP_PAD "AES-128-WRAP-PAD:id-aes128-wrap-pad:AES128-WRAP-PAD:2.16.840.1.101.3.4.1.8"
+#define PROV_NAMES_AES_256_WRAP_INV "AES-256-WRAP-INV:AES256-WRAP-INV"
+#define PROV_NAMES_AES_192_WRAP_INV "AES-192-WRAP-INV:AES192-WRAP-INV"
+#define PROV_NAMES_AES_128_WRAP_INV "AES-128-WRAP-INV:AES128-WRAP-INV"
+#define PROV_NAMES_AES_256_WRAP_PAD_INV "AES-256-WRAP-PAD-INV:AES256-WRAP-PAD-INV"
+#define PROV_NAMES_AES_192_WRAP_PAD_INV "AES-192-WRAP-PAD-INV:AES192-WRAP-PAD-INV"
+#define PROV_NAMES_AES_128_WRAP_PAD_INV "AES-128-WRAP-PAD-INV:AES128-WRAP-PAD-INV"
+#define PROV_NAMES_AES_128_CBC_HMAC_SHA1 "AES-128-CBC-HMAC-SHA1"
+#define PROV_NAMES_AES_256_CBC_HMAC_SHA1 "AES-256-CBC-HMAC-SHA1"
+#define PROV_NAMES_AES_128_CBC_HMAC_SHA256 "AES-128-CBC-HMAC-SHA256"
+#define PROV_NAMES_AES_256_CBC_HMAC_SHA256 "AES-256-CBC-HMAC-SHA256"
+#define PROV_NAMES_DES_EDE3_ECB "DES-EDE3-ECB:DES-EDE3"
+#define PROV_NAMES_DES_EDE3_CBC "DES-EDE3-CBC:DES3:1.2.840.113549.3.7"
+#define PROV_NAMES_NULL "NULL"
+#define PROV_NAMES_AES_256_OCB "AES-256-OCB"
+#define PROV_NAMES_AES_192_OCB "AES-192-OCB"
+#define PROV_NAMES_AES_128_OCB "AES-128-OCB"
+#define PROV_NAMES_AES_128_SIV "AES-128-SIV"
+#define PROV_NAMES_AES_192_SIV "AES-192-SIV"
+#define PROV_NAMES_AES_256_SIV "AES-256-SIV"
+#define PROV_NAMES_AES_128_GCM_SIV "AES-128-GCM-SIV"
+#define PROV_NAMES_AES_192_GCM_SIV "AES-192-GCM-SIV"
+#define PROV_NAMES_AES_256_GCM_SIV "AES-256-GCM-SIV"
+#define PROV_NAMES_ARIA_256_GCM "ARIA-256-GCM:1.2.410.200046.1.1.36"
+#define PROV_NAMES_ARIA_192_GCM "ARIA-192-GCM:1.2.410.200046.1.1.35"
+#define PROV_NAMES_ARIA_128_GCM "ARIA-128-GCM:1.2.410.200046.1.1.34"
+#define PROV_NAMES_ARIA_256_CCM "ARIA-256-CCM:1.2.410.200046.1.1.39"
+#define PROV_NAMES_ARIA_192_CCM "ARIA-192-CCM:1.2.410.200046.1.1.38"
+#define PROV_NAMES_ARIA_128_CCM "ARIA-128-CCM:1.2.410.200046.1.1.37"
+#define PROV_NAMES_ARIA_256_ECB "ARIA-256-ECB:1.2.410.200046.1.1.11"
+#define PROV_NAMES_ARIA_192_ECB "ARIA-192-ECB:1.2.410.200046.1.1.6"
+#define PROV_NAMES_ARIA_128_ECB "ARIA-128-ECB:1.2.410.200046.1.1.1"
+#define PROV_NAMES_ARIA_256_CBC "ARIA-256-CBC:ARIA256:1.2.410.200046.1.1.12"
+#define PROV_NAMES_ARIA_192_CBC "ARIA-192-CBC:ARIA192:1.2.410.200046.1.1.7"
+#define PROV_NAMES_ARIA_128_CBC "ARIA-128-CBC:ARIA128:1.2.410.200046.1.1.2"
+#define PROV_NAMES_ARIA_256_OFB "ARIA-256-OFB:1.2.410.200046.1.1.14"
+#define PROV_NAMES_ARIA_192_OFB "ARIA-192-OFB:1.2.410.200046.1.1.9"
+#define PROV_NAMES_ARIA_128_OFB "ARIA-128-OFB:1.2.410.200046.1.1.4"
+#define PROV_NAMES_ARIA_256_CFB "ARIA-256-CFB:1.2.410.200046.1.1.13"
+#define PROV_NAMES_ARIA_192_CFB "ARIA-192-CFB:1.2.410.200046.1.1.8"
+#define PROV_NAMES_ARIA_128_CFB "ARIA-128-CFB:1.2.410.200046.1.1.3"
+#define PROV_NAMES_ARIA_256_CFB1 "ARIA-256-CFB1"
+#define PROV_NAMES_ARIA_192_CFB1 "ARIA-192-CFB1"
+#define PROV_NAMES_ARIA_128_CFB1 "ARIA-128-CFB1"
+#define PROV_NAMES_ARIA_256_CFB8 "ARIA-256-CFB8"
+#define PROV_NAMES_ARIA_192_CFB8 "ARIA-192-CFB8"
+#define PROV_NAMES_ARIA_128_CFB8 "ARIA-128-CFB8"
+#define PROV_NAMES_ARIA_256_CTR "ARIA-256-CTR:1.2.410.200046.1.1.15"
+#define PROV_NAMES_ARIA_192_CTR "ARIA-192-CTR:1.2.410.200046.1.1.10"
+#define PROV_NAMES_ARIA_128_CTR "ARIA-128-CTR:1.2.410.200046.1.1.5"
+#define PROV_NAMES_CAMELLIA_256_ECB "CAMELLIA-256-ECB:0.3.4401.5.3.1.9.41"
+#define PROV_NAMES_CAMELLIA_192_ECB "CAMELLIA-192-ECB:0.3.4401.5.3.1.9.21"
+#define PROV_NAMES_CAMELLIA_128_ECB "CAMELLIA-128-ECB:0.3.4401.5.3.1.9.1"
+#define PROV_NAMES_CAMELLIA_256_CBC "CAMELLIA-256-CBC:CAMELLIA256:1.2.392.200011.61.1.1.1.4"
+#define PROV_NAMES_CAMELLIA_192_CBC "CAMELLIA-192-CBC:CAMELLIA192:1.2.392.200011.61.1.1.1.3"
+#define PROV_NAMES_CAMELLIA_128_CBC "CAMELLIA-128-CBC:CAMELLIA128:1.2.392.200011.61.1.1.1.2"
+#define PROV_NAMES_CAMELLIA_256_CBC_CTS "CAMELLIA-256-CBC-CTS"
+#define PROV_NAMES_CAMELLIA_192_CBC_CTS "CAMELLIA-192-CBC-CTS"
+#define PROV_NAMES_CAMELLIA_128_CBC_CTS "CAMELLIA-128-CBC-CTS"
+#define PROV_NAMES_CAMELLIA_256_OFB "CAMELLIA-256-OFB:0.3.4401.5.3.1.9.43"
+#define PROV_NAMES_CAMELLIA_192_OFB "CAMELLIA-192-OFB:0.3.4401.5.3.1.9.23"
+#define PROV_NAMES_CAMELLIA_128_OFB "CAMELLIA-128-OFB:0.3.4401.5.3.1.9.3"
+#define PROV_NAMES_CAMELLIA_256_CFB "CAMELLIA-256-CFB:0.3.4401.5.3.1.9.44"
+#define PROV_NAMES_CAMELLIA_192_CFB "CAMELLIA-192-CFB:0.3.4401.5.3.1.9.24"
+#define PROV_NAMES_CAMELLIA_128_CFB "CAMELLIA-128-CFB:0.3.4401.5.3.1.9.4"
+#define PROV_NAMES_CAMELLIA_256_CFB1 "CAMELLIA-256-CFB1"
+#define PROV_NAMES_CAMELLIA_192_CFB1 "CAMELLIA-192-CFB1"
+#define PROV_NAMES_CAMELLIA_128_CFB1 "CAMELLIA-128-CFB1"
+#define PROV_NAMES_CAMELLIA_256_CFB8 "CAMELLIA-256-CFB8"
+#define PROV_NAMES_CAMELLIA_192_CFB8 "CAMELLIA-192-CFB8"
+#define PROV_NAMES_CAMELLIA_128_CFB8 "CAMELLIA-128-CFB8"
+#define PROV_NAMES_CAMELLIA_256_CTR "CAMELLIA-256-CTR:0.3.4401.5.3.1.9.49"
+#define PROV_NAMES_CAMELLIA_192_CTR "CAMELLIA-192-CTR:0.3.4401.5.3.1.9.29"
+#define PROV_NAMES_CAMELLIA_128_CTR "CAMELLIA-128-CTR:0.3.4401.5.3.1.9.9"
+#define PROV_NAMES_DES_EDE3_OFB "DES-EDE3-OFB"
+#define PROV_NAMES_DES_EDE3_CFB "DES-EDE3-CFB"
+#define PROV_NAMES_DES_EDE3_CFB8 "DES-EDE3-CFB8"
+#define PROV_NAMES_DES_EDE3_CFB1 "DES-EDE3-CFB1"
+#define PROV_NAMES_DES3_WRAP "DES3-WRAP:id-smime-alg-CMS3DESwrap:1.2.840.113549.1.9.16.3.6"
+#define PROV_NAMES_DES_EDE_ECB "DES-EDE-ECB:DES-EDE:1.3.14.3.2.17"
+#define PROV_NAMES_DES_EDE_CBC "DES-EDE-CBC"
+#define PROV_NAMES_DES_EDE_OFB "DES-EDE-OFB"
+#define PROV_NAMES_DES_EDE_CFB "DES-EDE-CFB"
+#define PROV_NAMES_SM4_ECB "SM4-ECB:1.2.156.10197.1.104.1"
+#define PROV_NAMES_SM4_CBC "SM4-CBC:SM4:1.2.156.10197.1.104.2"
+#define PROV_NAMES_SM4_CTR "SM4-CTR:1.2.156.10197.1.104.7"
+#define PROV_NAMES_SM4_OFB "SM4-OFB:SM4-OFB128:1.2.156.10197.1.104.3"
+#define PROV_NAMES_SM4_CFB "SM4-CFB:SM4-CFB128:1.2.156.10197.1.104.4"
+#define PROV_NAMES_SM4_GCM "SM4-GCM:1.2.156.10197.1.104.8"
+#define PROV_NAMES_SM4_CCM "SM4-CCM:1.2.156.10197.1.104.9"
+#define PROV_NAMES_SM4_XTS "SM4-XTS:1.2.156.10197.1.104.10"
+#define PROV_NAMES_ChaCha20 "ChaCha20"
+#define PROV_NAMES_ChaCha20_Poly1305 "ChaCha20-Poly1305"
+#define PROV_NAMES_CAST5_ECB "CAST5-ECB"
+#define PROV_NAMES_CAST5_CBC "CAST5-CBC:CAST-CBC:CAST:1.2.840.113533.7.66.10"
+#define PROV_NAMES_CAST5_OFB "CAST5-OFB"
+#define PROV_NAMES_CAST5_CFB "CAST5-CFB"
+#define PROV_NAMES_BF_ECB "BF-ECB"
+#define PROV_NAMES_BF_CBC "BF-CBC:BF:BLOWFISH:1.3.6.1.4.1.3029.1.2"
+#define PROV_NAMES_BF_OFB "BF-OFB"
+#define PROV_NAMES_BF_CFB "BF-CFB"
+#define PROV_NAMES_IDEA_ECB "IDEA-ECB"
+#define PROV_NAMES_IDEA_CBC "IDEA-CBC:IDEA:1.3.6.1.4.1.188.7.1.1.2"
+#define PROV_NAMES_IDEA_OFB "IDEA-OFB:IDEA-OFB64"
+#define PROV_NAMES_IDEA_CFB "IDEA-CFB:IDEA-CFB64"
+#define PROV_NAMES_SEED_ECB "SEED-ECB:1.2.410.200004.1.3"
+#define PROV_NAMES_SEED_CBC "SEED-CBC:SEED:1.2.410.200004.1.4"
+#define PROV_NAMES_SEED_OFB "SEED-OFB:SEED-OFB128:1.2.410.200004.1.6"
+#define PROV_NAMES_SEED_CFB "SEED-CFB:SEED-CFB128:1.2.410.200004.1.5"
+#define PROV_NAMES_RC2_ECB "RC2-ECB"
+#define PROV_NAMES_RC2_CBC "RC2-CBC:RC2:RC2-128:1.2.840.113549.3.2"
+#define PROV_NAMES_RC2_40_CBC "RC2-40-CBC:RC2-40"
+#define PROV_NAMES_RC2_64_CBC "RC2-64-CBC:RC2-64"
+#define PROV_NAMES_RC2_CFB "RC2-CFB"
+#define PROV_NAMES_RC2_OFB "RC2-OFB"
+#define PROV_NAMES_RC4 "RC4:1.2.840.113549.3.4"
+#define PROV_NAMES_RC4_40 "RC4-40"
+#define PROV_NAMES_RC4_HMAC_MD5 "RC4-HMAC-MD5"
+#define PROV_NAMES_RC5_ECB "RC5-ECB"
+#define PROV_NAMES_RC5_CBC "RC5-CBC:RC5:1.2.840.113549.3.8"
+#define PROV_NAMES_RC5_OFB "RC5-OFB"
+#define PROV_NAMES_RC5_CFB "RC5-CFB"
+#define PROV_NAMES_DESX_CBC "DESX-CBC:DESX"
+#define PROV_NAMES_DES_ECB "DES-ECB:1.3.14.3.2.6"
+#define PROV_NAMES_DES_CBC "DES-CBC:DES:1.3.14.3.2.7"
+#define PROV_NAMES_DES_OFB "DES-OFB:1.3.14.3.2.8"
+#define PROV_NAMES_DES_CFB "DES-CFB:1.3.14.3.2.9"
+#define PROV_NAMES_DES_CFB1 "DES-CFB1"
+#define PROV_NAMES_DES_CFB8 "DES-CFB8"
+
+/*-
+ * Digests
+ * -------
+ */
+#define PROV_NAMES_SHA1 "SHA1:SHA-1:SSL3-SHA1:1.3.14.3.2.26"
+#define PROV_NAMES_SHA2_224 "SHA2-224:SHA-224:SHA224:2.16.840.1.101.3.4.2.4"
+#define PROV_NAMES_SHA2_256 "SHA2-256:SHA-256:SHA256:2.16.840.1.101.3.4.2.1"
+#define PROV_NAMES_SHA2_256_192 "SHA2-256/192:SHA-256/192:SHA256-192"
+#define PROV_NAMES_SHA2_384 "SHA2-384:SHA-384:SHA384:2.16.840.1.101.3.4.2.2"
+#define PROV_NAMES_SHA2_512 "SHA2-512:SHA-512:SHA512:2.16.840.1.101.3.4.2.3"
+#define PROV_NAMES_SHA2_512_224 "SHA2-512/224:SHA-512/224:SHA512-224:2.16.840.1.101.3.4.2.5"
+#define PROV_NAMES_SHA2_512_256 "SHA2-512/256:SHA-512/256:SHA512-256:2.16.840.1.101.3.4.2.6"
+
+/* We agree with NIST here, so one name only */
+#define PROV_NAMES_SHA3_224 "SHA3-224:2.16.840.1.101.3.4.2.7"
+#define PROV_NAMES_SHA3_256 "SHA3-256:2.16.840.1.101.3.4.2.8"
+#define PROV_NAMES_SHA3_384 "SHA3-384:2.16.840.1.101.3.4.2.9"
+#define PROV_NAMES_SHA3_512 "SHA3-512:2.16.840.1.101.3.4.2.10"
+
+#define PROV_NAMES_KECCAK_224 "KECCAK-224"
+#define PROV_NAMES_KECCAK_256 "KECCAK-256"
+#define PROV_NAMES_KECCAK_384 "KECCAK-384"
+#define PROV_NAMES_KECCAK_512 "KECCAK-512"
+
+#define PROV_NAMES_SHAKE_128 "SHAKE-128:SHAKE128:2.16.840.1.101.3.4.2.11"
+#define PROV_NAMES_SHAKE_256 "SHAKE-256:SHAKE256:2.16.840.1.101.3.4.2.12"
+
+/*
+ * KECCAK-KMAC-128 and KECCAK-KMAC-256 as hashes are mostly useful for
+ * KMAC128 and KMAC256.
+ */
+#define PROV_NAMES_KECCAK_KMAC_128 "KECCAK-KMAC-128:KECCAK-KMAC128"
+#define PROV_NAMES_KECCAK_KMAC_256 "KECCAK-KMAC-256:KECCAK-KMAC256"
+/*
+ * https://blake2.net/ doesn't specify size variants, but mentions that
+ * Bouncy Castle uses the names BLAKE2b-160, BLAKE2b-256, BLAKE2b-384, and
+ * BLAKE2b-512
+ * If we assume that "2b" and "2s" are versions, that pattern fits with ours.
+ * We also add our historical names.
+ */
+#define PROV_NAMES_BLAKE2S_256 "BLAKE2S-256:BLAKE2s256:1.3.6.1.4.1.1722.12.2.2.8"
+#define PROV_NAMES_BLAKE2B_512 "BLAKE2B-512:BLAKE2b512:1.3.6.1.4.1.1722.12.2.1.16"
+#define PROV_NAMES_SM3 "SM3:1.2.156.10197.1.401"
+#define PROV_NAMES_MD5 "MD5:SSL3-MD5:1.2.840.113549.2.5"
+#define PROV_NAMES_MD5_SHA1 "MD5-SHA1"
+#define PROV_NAMES_MD2 "MD2:1.2.840.113549.2.2"
+#define PROV_NAMES_MD4 "MD4:1.2.840.113549.2.4"
+#define PROV_NAMES_MDC2 "MDC2:2.5.8.3.101"
+#define PROV_NAMES_WHIRLPOOL "WHIRLPOOL:1.0.10118.3.0.55"
+#define PROV_NAMES_RIPEMD_160 "RIPEMD-160:RIPEMD160:RIPEMD:RMD160:1.3.36.3.2.1"
+
+/*-
+ * KDFs / PRFs
+ * -----------
+ */
+#define PROV_NAMES_HKDF "HKDF"
+#define PROV_DESCS_HKDF_SIGN "OpenSSL HKDF via EVP_PKEY implementation"
+#define PROV_NAMES_TLS1_3_KDF "TLS13-KDF"
+#define PROV_NAMES_SSKDF "SSKDF"
+#define PROV_NAMES_PBKDF1 "PBKDF1"
+#define PROV_NAMES_PBKDF2 "PBKDF2:1.2.840.113549.1.5.12"
+#define PROV_NAMES_PVKKDF "PVKKDF"
+#define PROV_NAMES_SSHKDF "SSHKDF"
+#define PROV_NAMES_X963KDF "X963KDF:X942KDF-CONCAT"
+#define PROV_NAMES_X942KDF_ASN1 "X942KDF-ASN1:X942KDF"
+#define PROV_NAMES_TLS1_PRF "TLS1-PRF"
+#define PROV_DESCS_TLS1_PRF_SIGN "OpenSSL TLS1_PRF via EVP_PKEY implementation"
+#define PROV_NAMES_KBKDF "KBKDF"
+#define PROV_NAMES_PKCS12KDF "PKCS12KDF"
+#define PROV_NAMES_SCRYPT "SCRYPT:id-scrypt:1.3.6.1.4.1.11591.4.11"
+#define PROV_DESCS_SCRYPT_SIGN "OpenSSL SCRYPT via EVP_PKEY implementation"
+#define PROV_NAMES_KRB5KDF "KRB5KDF"
+#define PROV_NAMES_HMAC_DRBG_KDF "HMAC-DRBG-KDF"
+#define PROV_NAMES_ARGON2I "ARGON2I"
+#define PROV_NAMES_ARGON2D "ARGON2D"
+#define PROV_NAMES_ARGON2ID "ARGON2ID"
+
+/*-
+ * MACs
+ * ----
+ */
+#define PROV_NAMES_HMAC "HMAC"
+#define PROV_DESCS_HMAC_SIGN "OpenSSL HMAC via EVP_PKEY implementation"
+#define PROV_NAMES_CMAC "CMAC"
+#define PROV_DESCS_CMAC_SIGN "OpenSSL CMAC via EVP_PKEY implementation"
+#define PROV_NAMES_SIPHASH "SIPHASH"
+#define PROV_DESCS_SIPHASH_SIGN "OpenSSL SIPHASH via EVP_PKEY implementation"
+#define PROV_NAMES_POLY1305 "POLY1305"
+#define PROV_DESCS_POLY1305_SIGN "OpenSSL POLY1305 via EVP_PKEY implementation"
+#define PROV_NAMES_GMAC "GMAC:1.0.9797.3.4"
+#define PROV_NAMES_KMAC_128 "KMAC-128:KMAC128:2.16.840.1.101.3.4.2.19"
+#define PROV_NAMES_KMAC_256 "KMAC-256:KMAC256:2.16.840.1.101.3.4.2.20"
+#define PROV_NAMES_BLAKE2BMAC "BLAKE2BMAC:1.3.6.1.4.1.1722.12.2.1"
+#define PROV_NAMES_BLAKE2SMAC "BLAKE2SMAC:1.3.6.1.4.1.1722.12.2.2"
+
+/*-
+ * RANDs
+ * -----
+ */
+#define PROV_NAMES_CRNG_TEST "CRNG-TEST"
+#define PROV_NAMES_CTR_DRBG "CTR-DRBG"
+#define PROV_NAMES_HASH_DRBG "HASH-DRBG"
+#define PROV_NAMES_HMAC_DRBG "HMAC-DRBG"
+#define PROV_NAMES_TEST_RAND "TEST-RAND"
+#define PROV_NAMES_SEED_SRC "SEED-SRC"
+#define PROV_NAMES_JITTER "JITTER"
+
+/*-
+ * Asymmetric algos
+ * ----------------
+ */
+#define PROV_NAMES_EC "EC:id-ecPublicKey:1.2.840.10045.2.1"
+#define PROV_DESCS_EC "OpenSSL EC implementation"
+#define PROV_NAMES_ECDH "ECDH"
+#define PROV_DESCS_ECDH "OpenSSL ECDH implementation"
+#define PROV_NAMES_ECDSA "ECDSA"
+#define PROV_NAMES_ECDSA_SHA1 "ECDSA-SHA1:ECDSA-SHA-1:ecdsa-with-SHA1:1.2.840.10045.4.1"
+#define PROV_NAMES_ECDSA_SHA224 "ECDSA-SHA2-224:ECDSA-SHA224:ecdsa-with-SHA224:1.2.840.10045.4.3.1"
+#define PROV_NAMES_ECDSA_SHA256 "ECDSA-SHA2-256:ECDSA-SHA256:ecdsa-with-SHA256:1.2.840.10045.4.3.2"
+#define PROV_NAMES_ECDSA_SHA384 "ECDSA-SHA2-384:ECDSA-SHA384:ecdsa-with-SHA384:1.2.840.10045.4.3.3"
+#define PROV_NAMES_ECDSA_SHA512 "ECDSA-SHA2-512:ECDSA-SHA512:ecdsa-with-SHA512:1.2.840.10045.4.3.4"
+#define PROV_NAMES_ECDSA_SHA3_224 "ECDSA-SHA3-224:ecdsa_with_SHA3-224:id-ecdsa-with-sha3-224:2.16.840.1.101.3.4.3.9"
+#define PROV_NAMES_ECDSA_SHA3_256 "ECDSA-SHA3-256:ecdsa_with_SHA3-256:id-ecdsa-with-sha3-256:2.16.840.1.101.3.4.3.10"
+#define PROV_NAMES_ECDSA_SHA3_384 "ECDSA-SHA3-384:ecdsa_with_SHA3-384:id-ecdsa-with-sha3-384:2.16.840.1.101.3.4.3.11"
+#define PROV_NAMES_ECDSA_SHA3_512 "ECDSA-SHA3-512:ecdsa_with_SHA3-512:id-ecdsa-with-sha3-512:2.16.840.1.101.3.4.3.12"
+#define PROV_DESCS_ECDSA "OpenSSL ECDSA implementation"
+#define PROV_NAMES_X25519 "X25519:1.3.101.110"
+#define PROV_DESCS_X25519 "OpenSSL X25519 implementation"
+#define PROV_NAMES_X448 "X448:1.3.101.111"
+#define PROV_DESCS_X448 "OpenSSL X448 implementation"
+#define PROV_NAMES_ED25519 "ED25519:1.3.101.112"
+#define PROV_DESCS_ED25519 "OpenSSL ED25519 implementation"
+#define PROV_NAMES_ED25519ph "ED25519ph"
+#define PROV_DESCS_ED25519ph "OpenSSL ED25519ph implementation"
+#define PROV_NAMES_ED25519ctx "ED25519ctx"
+#define PROV_DESCS_ED25519ctx "OpenSSL ED25519ctx implementation"
+#define PROV_NAMES_ED448 "ED448:1.3.101.113"
+#define PROV_DESCS_ED448 "OpenSSL ED448 implementation"
+#define PROV_NAMES_ED448ph "ED448ph"
+#define PROV_DESCS_ED448ph "OpenSSL ED448ph implementation"
+#define PROV_NAMES_DH "DH:dhKeyAgreement:1.2.840.113549.1.3.1"
+#define PROV_DESCS_DH "OpenSSL PKCS#3 DH implementation"
+#define PROV_NAMES_DHX "DHX:X9.42 DH:dhpublicnumber:1.2.840.10046.2.1"
+#define PROV_DESCS_DHX "OpenSSL X9.42 DH implementation"
+#define PROV_NAMES_DSA "DSA:dsaEncryption:1.2.840.10040.4.1"
+#define PROV_NAMES_DSA_SHA1 "DSA-SHA1:DSA-SHA-1:dsaWithSHA1:1.2.840.10040.4.3"
+#define PROV_NAMES_DSA_SHA224 "DSA-SHA2-224:DSA-SHA224:dsa_with_SHA224:2.16.840.1.101.3.4.3.1"
+#define PROV_NAMES_DSA_SHA256 "DSA-SHA2-256:DSA-SHA256:dsa_with_SHA256:2.16.840.1.101.3.4.3.2"
+#define PROV_NAMES_DSA_SHA384 "DSA-SHA2-384:DSA-SHA384:dsa_with_SHA384:id-dsa-with-sha384:1.2.840.1.101.3.4.3.3"
+#define PROV_NAMES_DSA_SHA512 "DSA-SHA2-512:DSA-SHA512:dsa_with_SHA512:id-dsa-with-sha512:1.2.840.1.101.3.4.3.4"
+#define PROV_NAMES_DSA_SHA3_224 "DSA-SHA3-224:dsa_with_SHA3-224:id-dsa-with-sha3-224:2.16.840.1.101.3.4.3.5"
+#define PROV_NAMES_DSA_SHA3_256 "DSA-SHA3-256:dsa_with_SHA3-256:id-dsa-with-sha3-256:2.16.840.1.101.3.4.3.6"
+#define PROV_NAMES_DSA_SHA3_384 "DSA-SHA3-384:dsa_with_SHA3-384:id-dsa-with-sha3-384:2.16.840.1.101.3.4.3.7"
+#define PROV_NAMES_DSA_SHA3_512 "DSA-SHA3-512:dsa_with_SHA3-512:id-dsa-with-sha3-512:2.16.840.1.101.3.4.3.8"
+#define PROV_DESCS_DSA "OpenSSL DSA implementation"
+#define PROV_NAMES_RSA "RSA:rsaEncryption:1.2.840.113549.1.1.1"
+#define PROV_NAMES_RSA_MD2 "RSA-MD2:md2WithRSAEncryption:1.2.840.113549.1.1.2"
+#define PROV_NAMES_RSA_MD4 "RSA-MD4:md4WithEncryption:1.2.840.113549.1.1.3"
+#define PROV_NAMES_RSA_MD5 "RSA-MD5:md5WithRSAEncryption:1.2.840.113549.1.1.4"
+#define PROV_NAMES_RSA_RIPEMD160 "RSA-RIPEMD160:ripemd160WithRSA:1.3.36.3.3.1.2"
+#define PROV_NAMES_RSA_SHA1 "RSA-SHA1:RSA-SHA-1:sha1WithRSAEncryption:1.2.840.113549.1.1.5"
+#define PROV_NAMES_RSA_SHA256 "RSA-SHA2-256:RSA-SHA256:sha256WithRSAEncryption:1.2.840.113549.1.1.11"
+#define PROV_NAMES_RSA_SHA384 "RSA-SHA2-384:RSA-SHA384:sha384WithRSAEncryption:1.2.840.113549.1.1.12"
+#define PROV_NAMES_RSA_SHA512 "RSA-SHA2-512:RSA-SHA512:sha512WithRSAEncryption:1.2.840.113549.1.1.13"
+#define PROV_NAMES_RSA_SHA224 "RSA-SHA2-224:RSA-SHA224:sha224WithRSAEncryption:1.2.840.113549.1.1.14"
+#define PROV_NAMES_RSA_SHA512_224 "RSA-SHA2-512/224:RSA-SHA512-224:sha512-224WithRSAEncryption:1.2.840.113549.1.1.15"
+#define PROV_NAMES_RSA_SHA512_256 "RSA-SHA2-512/256:RSA-SHA512-256:sha512-256WithRSAEncryption:1.2.840.113549.1.1.16"
+#define PROV_NAMES_RSA_SM3 "RSA-SM3:sm3WithRSAEncryption:1.2.156.10197.1.504"
+#define PROV_NAMES_RSA_SHA3_224 "RSA-SHA3-224:id-rsassa-pkcs1-v1_5-with-sha3-224:2.16.840.1.101.3.4.3.13"
+#define PROV_NAMES_RSA_SHA3_256 "RSA-SHA3-256:id-rsassa-pkcs1-v1_5-with-sha3-256:2.16.840.1.101.3.4.3.14"
+#define PROV_NAMES_RSA_SHA3_384 "RSA-SHA3-384:id-rsassa-pkcs1-v1_5-with-sha3-384:2.16.840.1.101.3.4.3.15"
+#define PROV_NAMES_RSA_SHA3_512 "RSA-SHA3-512:id-rsassa-pkcs1-v1_5-with-sha3-512:2.16.840.1.101.3.4.3.16"
+#define PROV_DESCS_RSA "OpenSSL RSA implementation"
+#define PROV_NAMES_RSA_PSS "RSA-PSS:RSASSA-PSS:1.2.840.113549.1.1.10"
+#define PROV_DESCS_RSA_PSS "OpenSSL RSA-PSS implementation"
+#define PROV_NAMES_SM2 "SM2:1.2.156.10197.1.301"
+#define PROV_DESCS_SM2 "OpenSSL SM2 implementation"
+#define PROV_NAMES_ML_DSA_44 "ML-DSA-44:MLDSA44:2.16.840.1.101.3.4.3.17:id-ml-dsa-44"
+#define PROV_DESCS_ML_DSA_44 "OpenSSL ML-DSA-44 implementation"
+#define PROV_NAMES_ML_DSA_65 "ML-DSA-65:MLDSA65:2.16.840.1.101.3.4.3.18:id-ml-dsa-65"
+#define PROV_DESCS_ML_DSA_65 "OpenSSL ML-DSA-65 implementation"
+#define PROV_NAMES_ML_DSA_87 "ML-DSA-87:MLDSA87:2.16.840.1.101.3.4.3.19:id-ml-dsa-87"
+#define PROV_DESCS_ML_DSA_87 "OpenSSL ML-DSA-87 implementation"
+#define PROV_NAMES_ML_KEM_512 "ML-KEM-512:MLKEM512:id-alg-ml-kem-512:2.16.840.1.101.3.4.4.1"
+#define PROV_DESCS_ML_KEM_512 "OpenSSL ML-KEM-512 implementation"
+#define PROV_NAMES_ML_KEM_768 "ML-KEM-768:MLKEM768:id-alg-ml-kem-768:2.16.840.1.101.3.4.4.2"
+#define PROV_DESCS_ML_KEM_768 "OpenSSL ML-KEM-768 implementation"
+#define PROV_NAMES_ML_KEM_1024 "ML-KEM-1024:MLKEM1024:id-alg-ml-kem-1024:2.16.840.1.101.3.4.4.3"
+#define PROV_DESCS_ML_KEM_1024 "OpenSSL ML-KEM-1024 implementation"
+#define PROV_NAMES_X25519MLKEM768 "X25519MLKEM768"
+#define PROV_DESCS_X25519MLKEM768 "X25519+ML-KEM-768 TLS hybrid implementation"
+#define PROV_NAMES_X448MLKEM1024 "X448MLKEM1024"
+#define PROV_DESCS_X448MLKEM1024 "X448+ML-KEM-1024 TLS hybrid implementation"
+#define PROV_NAMES_SecP256r1MLKEM768 "SecP256r1MLKEM768"
+#define PROV_DESCS_SecP256r1MLKEM768 "P-256+ML-KEM-768 TLS hybrid implementation"
+#define PROV_NAMES_SecP384r1MLKEM1024 "SecP384r1MLKEM1024"
+#define PROV_DESCS_SecP384r1MLKEM1024 "P-384+ML-KEM-1024 TLS hybrid implementation"
+#define PROV_NAMES_SLH_DSA_SHA2_128S "SLH-DSA-SHA2-128s:id-slh-dsa-sha2-128s:2.16.840.1.101.3.4.3.20"
+#define PROV_NAMES_SLH_DSA_SHA2_128F "SLH-DSA-SHA2-128f:id-slh-dsa-sha2-128f:2.16.840.1.101.3.4.3.21"
+#define PROV_NAMES_SLH_DSA_SHA2_192S "SLH-DSA-SHA2-192s:id-slh-dsa-sha2-192s:2.16.840.1.101.3.4.3.22"
+#define PROV_NAMES_SLH_DSA_SHA2_192F "SLH-DSA-SHA2-192f:id-slh-dsa-sha2-192f:2.16.840.1.101.3.4.3.23"
+#define PROV_NAMES_SLH_DSA_SHA2_256S "SLH-DSA-SHA2-256s:id-slh-dsa-sha2-256s:2.16.840.1.101.3.4.3.24"
+#define PROV_NAMES_SLH_DSA_SHA2_256F "SLH-DSA-SHA2-256f:id-slh-dsa-sha2-256f:2.16.840.1.101.3.4.3.25"
+#define PROV_NAMES_SLH_DSA_SHAKE_128S "SLH-DSA-SHAKE-128s:id-slh-dsa-shake-128s:2.16.840.1.101.3.4.3.26"
+#define PROV_NAMES_SLH_DSA_SHAKE_128F "SLH-DSA-SHAKE-128f:id-slh-dsa-shake-128f:2.16.840.1.101.3.4.3.27"
+#define PROV_NAMES_SLH_DSA_SHAKE_192S "SLH-DSA-SHAKE-192s:id-slh-dsa-shake-192s:2.16.840.1.101.3.4.3.28"
+#define PROV_NAMES_SLH_DSA_SHAKE_192F "SLH-DSA-SHAKE-192f:id-slh-dsa-shake-192f:2.16.840.1.101.3.4.3.29"
+#define PROV_NAMES_SLH_DSA_SHAKE_256S "SLH-DSA-SHAKE-256s:id-slh-dsa-shake-256s:2.16.840.1.101.3.4.3.30"
+#define PROV_NAMES_SLH_DSA_SHAKE_256F "SLH-DSA-SHAKE-256f:id-slh-dsa-shake-256f:2.16.840.1.101.3.4.3.31"
+#define PROV_DESCS_SLH_DSA_SHA2_128S "OpenSSL SLH-DSA-SHA2-128s implementation"
+#define PROV_DESCS_SLH_DSA_SHA2_128F "OpenSSL SLH-DSA-SHA2-128f implementation"
+#define PROV_DESCS_SLH_DSA_SHA2_192S "OpenSSL SLH-DSA-SHA2-192s implementation"
+#define PROV_DESCS_SLH_DSA_SHA2_192F "OpenSSL SLH-DSA-SHA2-192f implementation"
+#define PROV_DESCS_SLH_DSA_SHA2_256S "OpenSSL SLH-DSA-SHA2-256s implementation"
+#define PROV_DESCS_SLH_DSA_SHA2_256F "OpenSSL SLH-DSA-SHA2-256f implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_128S "OpenSSL SLH-DSA-SHAKE-128s implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_128F "OpenSSL SLH-DSA-SHAKE-128f implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_192S "OpenSSL SLH-DSA-SHAKE-192s implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_192F "OpenSSL SLH-DSA-SHAKE-192f implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_256S "OpenSSL SLH-DSA-SHAKE-256s implementation"
+#define PROV_DESCS_SLH_DSA_SHAKE_256F "OpenSSL SLH-DSA-SHAKE-256f implementation"
diff --git a/crypto/openssl/providers/implementations/include/prov/seeding.h b/crypto/openssl/providers/implementations/include/prov/seeding.h
new file mode 100644
index 000000000000..af6cb79fb28f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/include/prov/seeding.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "prov/provider_ctx.h"
+#include "crypto/rand_pool.h"
+
+/* Hardware-based seeding functions. */
+size_t ossl_prov_acquire_entropy_from_tsc(RAND_POOL *pool);
+size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool);
+
+/*
+ * External seeding functions from the core dispatch table.
+ */
+int ossl_prov_seeding_from_dispatch(const OSSL_DISPATCH *fns);
+
+size_t ossl_prov_get_entropy(PROV_CTX *prov_ctx, unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len);
+void ossl_prov_cleanup_entropy(PROV_CTX *prov_ctx, unsigned char *buf,
+ size_t len);
+size_t ossl_prov_get_nonce(PROV_CTX *prov_ctx, unsigned char **pout,
+ size_t min_len, size_t max_len,
+ const void *salt, size_t salt_len);
+void ossl_prov_cleanup_nonce(PROV_CTX *prov_ctx, unsigned char *buf,
+ size_t len);
diff --git a/crypto/openssl/providers/implementations/kdfs/argon2.c b/crypto/openssl/providers/implementations/kdfs/argon2.c
new file mode 100644
index 000000000000..e3f9aa4f0f5c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/argon2.c
@@ -0,0 +1,1560 @@
+/*
+ * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ *
+ * RFC 9106 Argon2 (see https://www.rfc-editor.org/rfc/rfc9106.txt)
+ *
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/e_os2.h>
+#include <openssl/evp.h>
+#include <openssl/objects.h>
+#include <openssl/crypto.h>
+#include <openssl/kdf.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/thread.h>
+#include <openssl/proverr.h>
+#include "internal/thread.h"
+#include "internal/numbers.h"
+#include "internal/endian.h"
+#include "crypto/evp.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/blake2.h"
+
+#if defined(OPENSSL_NO_DEFAULT_THREAD_POOL) && defined(OPENSSL_NO_THREAD_POOL)
+# define ARGON2_NO_THREADS
+#endif
+
+#if !defined(OPENSSL_THREADS)
+# define ARGON2_NO_THREADS
+#endif
+
+#ifndef OPENSSL_NO_ARGON2
+
+# define ARGON2_MIN_LANES 1u
+# define ARGON2_MAX_LANES 0xFFFFFFu
+# define ARGON2_MIN_THREADS 1u
+# define ARGON2_MAX_THREADS 0xFFFFFFu
+# define ARGON2_SYNC_POINTS 4u
+# define ARGON2_MIN_OUT_LENGTH 4u
+# define ARGON2_MAX_OUT_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_MEMORY (2 * ARGON2_SYNC_POINTS)
+# define ARGON2_MIN(a, b) ((a) < (b) ? (a) : (b))
+# define ARGON2_MAX_MEMORY 0xFFFFFFFFu
+# define ARGON2_MIN_TIME 1u
+# define ARGON2_MAX_TIME 0xFFFFFFFFu
+# define ARGON2_MIN_PWD_LENGTH 0u
+# define ARGON2_MAX_PWD_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_AD_LENGTH 0u
+# define ARGON2_MAX_AD_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_SALT_LENGTH 8u
+# define ARGON2_MAX_SALT_LENGTH 0xFFFFFFFFu
+# define ARGON2_MIN_SECRET 0u
+# define ARGON2_MAX_SECRET 0xFFFFFFFFu
+# define ARGON2_BLOCK_SIZE 1024
+# define ARGON2_QWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 8)
+# define ARGON2_OWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 16)
+# define ARGON2_HWORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 32)
+# define ARGON2_512BIT_WORDS_IN_BLOCK ((ARGON2_BLOCK_SIZE) / 64)
+# define ARGON2_ADDRESSES_IN_BLOCK 128
+# define ARGON2_PREHASH_DIGEST_LENGTH 64
+# define ARGON2_PREHASH_SEED_LENGTH \
+ (ARGON2_PREHASH_DIGEST_LENGTH + (2 * sizeof(uint32_t)))
+
+# define ARGON2_DEFAULT_OUTLEN 64u
+# define ARGON2_DEFAULT_T_COST 3u
+# define ARGON2_DEFAULT_M_COST ARGON2_MIN_MEMORY
+# define ARGON2_DEFAULT_LANES 1u
+# define ARGON2_DEFAULT_THREADS 1u
+# define ARGON2_DEFAULT_VERSION ARGON2_VERSION_NUMBER
+
+# undef G
+# define G(a, b, c, d) \
+ do { \
+ a = a + b + 2 * mul_lower(a, b); \
+ d = rotr64(d ^ a, 32); \
+ c = c + d + 2 * mul_lower(c, d); \
+ b = rotr64(b ^ c, 24); \
+ a = a + b + 2 * mul_lower(a, b); \
+ d = rotr64(d ^ a, 16); \
+ c = c + d + 2 * mul_lower(c, d); \
+ b = rotr64(b ^ c, 63); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P
+# define PERMUTATION_P(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, \
+ v12, v13, v14, v15) \
+ do { \
+ G(v0, v4, v8, v12); \
+ G(v1, v5, v9, v13); \
+ G(v2, v6, v10, v14); \
+ G(v3, v7, v11, v15); \
+ G(v0, v5, v10, v15); \
+ G(v1, v6, v11, v12); \
+ G(v2, v7, v8, v13); \
+ G(v3, v4, v9, v14); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P_COLUMN
+# define PERMUTATION_P_COLUMN(x, i) \
+ do { \
+ uint64_t *base = &x[16 * i]; \
+ PERMUTATION_P( \
+ *base, *(base + 1), *(base + 2), *(base + 3), \
+ *(base + 4), *(base + 5), *(base + 6), *(base + 7), \
+ *(base + 8), *(base + 9), *(base + 10), *(base + 11), \
+ *(base + 12), *(base + 13), *(base + 14), *(base + 15) \
+ ); \
+ } while ((void)0, 0)
+
+# undef PERMUTATION_P_ROW
+# define PERMUTATION_P_ROW(x, i) \
+ do { \
+ uint64_t *base = &x[2 * i]; \
+ PERMUTATION_P( \
+ *base, *(base + 1), *(base + 16), *(base + 17), \
+ *(base + 32), *(base + 33), *(base + 48), *(base + 49), \
+ *(base + 64), *(base + 65), *(base + 80), *(base + 81), \
+ *(base + 96), *(base + 97), *(base + 112), *(base + 113) \
+ ); \
+ } while ((void)0, 0)
+
+typedef struct {
+ uint64_t v[ARGON2_QWORDS_IN_BLOCK];
+} BLOCK;
+
+typedef enum {
+ ARGON2_VERSION_10 = 0x10,
+ ARGON2_VERSION_13 = 0x13,
+ ARGON2_VERSION_NUMBER = ARGON2_VERSION_13
+} ARGON2_VERSION;
+
+typedef enum {
+ ARGON2_D = 0,
+ ARGON2_I = 1,
+ ARGON2_ID = 2
+} ARGON2_TYPE;
+
+typedef struct {
+ uint32_t pass;
+ uint32_t lane;
+ uint8_t slice;
+ uint32_t index;
+} ARGON2_POS;
+
+typedef struct {
+ void *provctx;
+ uint32_t outlen;
+ uint8_t *pwd;
+ uint32_t pwdlen;
+ uint8_t *salt;
+ uint32_t saltlen;
+ uint8_t *secret;
+ uint32_t secretlen;
+ uint8_t *ad;
+ uint32_t adlen;
+ uint32_t t_cost;
+ uint32_t m_cost;
+ uint32_t lanes;
+ uint32_t threads;
+ uint32_t version;
+ uint32_t early_clean;
+ ARGON2_TYPE type;
+ BLOCK *memory;
+ uint32_t passes;
+ uint32_t memory_blocks;
+ uint32_t segment_length;
+ uint32_t lane_length;
+ OSSL_LIB_CTX *libctx;
+ EVP_MD *md;
+ EVP_MAC *mac;
+ char *propq;
+} KDF_ARGON2;
+
+typedef struct {
+ ARGON2_POS pos;
+ KDF_ARGON2 *ctx;
+} ARGON2_THREAD_DATA;
+
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2i_new;
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2d_new;
+static OSSL_FUNC_kdf_newctx_fn kdf_argon2id_new;
+static OSSL_FUNC_kdf_freectx_fn kdf_argon2_free;
+static OSSL_FUNC_kdf_reset_fn kdf_argon2_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_argon2_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_argon2_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_argon2_set_ctx_params;
+
+static void kdf_argon2_init(KDF_ARGON2 *ctx, ARGON2_TYPE t);
+static void *kdf_argon2d_new(void *provctx);
+static void *kdf_argon2i_new(void *provctx);
+static void *kdf_argon2id_new(void *provctx);
+static void kdf_argon2_free(void *vctx);
+static int kdf_argon2_derive(void *vctx, unsigned char *out, size_t outlen,
+ const OSSL_PARAM params[]);
+static void kdf_argon2_reset(void *vctx);
+static int kdf_argon2_ctx_set_threads(KDF_ARGON2 *ctx, uint32_t threads);
+static int kdf_argon2_ctx_set_lanes(KDF_ARGON2 *ctx, uint32_t lanes);
+static int kdf_argon2_ctx_set_t_cost(KDF_ARGON2 *ctx, uint32_t t_cost);
+static int kdf_argon2_ctx_set_m_cost(KDF_ARGON2 *ctx, uint32_t m_cost);
+static int kdf_argon2_ctx_set_out_length(KDF_ARGON2 *ctx, uint32_t outlen);
+static int kdf_argon2_ctx_set_secret(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_pwd(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_salt(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_ctx_set_ad(KDF_ARGON2 *ctx, const OSSL_PARAM *p);
+static int kdf_argon2_set_ctx_params(void *vctx, const OSSL_PARAM params[]);
+static int kdf_argon2_get_ctx_params(void *vctx, OSSL_PARAM params[]);
+static int kdf_argon2_ctx_set_version(KDF_ARGON2 *ctx, uint32_t version);
+static const OSSL_PARAM *kdf_argon2_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx);
+static const OSSL_PARAM *kdf_argon2_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx);
+
+static ossl_inline uint64_t load64(const uint8_t *src);
+static ossl_inline void store32(uint8_t *dst, uint32_t w);
+static ossl_inline void store64(uint8_t *dst, uint64_t w);
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c);
+static ossl_inline uint64_t mul_lower(uint64_t x, uint64_t y);
+
+static void init_block_value(BLOCK *b, uint8_t in);
+static void copy_block(BLOCK *dst, const BLOCK *src);
+static void xor_block(BLOCK *dst, const BLOCK *src);
+static void load_block(BLOCK *dst, const void *input);
+static void store_block(void *output, const BLOCK *src);
+static void fill_first_blocks(uint8_t *blockhash, const KDF_ARGON2 *ctx);
+static void fill_block(const BLOCK *prev, const BLOCK *ref, BLOCK *next,
+ int with_xor);
+
+static void next_addresses(BLOCK *address_block, BLOCK *input_block,
+ const BLOCK *zero_block);
+static int data_indep_addressing(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice);
+static uint32_t index_alpha(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice, uint32_t index,
+ uint32_t pseudo_rand, int same_lane);
+
+static void fill_segment(const KDF_ARGON2 *ctx, uint32_t pass, uint32_t lane,
+ uint8_t slice);
+
+# if !defined(ARGON2_NO_THREADS)
+static uint32_t fill_segment_thr(void *thread_data);
+static int fill_mem_blocks_mt(KDF_ARGON2 *ctx);
+# endif
+
+static int fill_mem_blocks_st(KDF_ARGON2 *ctx);
+static ossl_inline int fill_memory_blocks(KDF_ARGON2 *ctx);
+
+static void initial_hash(uint8_t *blockhash, KDF_ARGON2 *ctx);
+static int initialize(KDF_ARGON2 *ctx);
+static void finalize(const KDF_ARGON2 *ctx, void *out);
+
+static int blake2b(EVP_MD *md, EVP_MAC *mac, void *out, size_t outlen,
+ const void *in, size_t inlen, const void *key,
+ size_t keylen);
+static int blake2b_long(EVP_MD *md, EVP_MAC *mac, unsigned char *out,
+ size_t outlen, const void *in, size_t inlen);
+
+static ossl_inline uint64_t load64(const uint8_t *src)
+{
+ return
+ (((uint64_t)src[0]) << 0)
+ | (((uint64_t)src[1]) << 8)
+ | (((uint64_t)src[2]) << 16)
+ | (((uint64_t)src[3]) << 24)
+ | (((uint64_t)src[4]) << 32)
+ | (((uint64_t)src[5]) << 40)
+ | (((uint64_t)src[6]) << 48)
+ | (((uint64_t)src[7]) << 56);
+}
+
+static ossl_inline void store32(uint8_t *dst, uint32_t w)
+{
+ dst[0] = (uint8_t)(w >> 0);
+ dst[1] = (uint8_t)(w >> 8);
+ dst[2] = (uint8_t)(w >> 16);
+ dst[3] = (uint8_t)(w >> 24);
+}
+
+static ossl_inline void store64(uint8_t *dst, uint64_t w)
+{
+ dst[0] = (uint8_t)(w >> 0);
+ dst[1] = (uint8_t)(w >> 8);
+ dst[2] = (uint8_t)(w >> 16);
+ dst[3] = (uint8_t)(w >> 24);
+ dst[4] = (uint8_t)(w >> 32);
+ dst[5] = (uint8_t)(w >> 40);
+ dst[6] = (uint8_t)(w >> 48);
+ dst[7] = (uint8_t)(w >> 56);
+}
+
+static ossl_inline uint64_t rotr64(const uint64_t w, const unsigned int c)
+{
+ return (w >> c) | (w << (64 - c));
+}
+
+static ossl_inline uint64_t mul_lower(uint64_t x, uint64_t y)
+{
+ const uint64_t m = 0xFFFFFFFFUL;
+ return (x & m) * (y & m);
+}
+
+static void init_block_value(BLOCK *b, uint8_t in)
+{
+ memset(b->v, in, sizeof(b->v));
+}
+
+static void copy_block(BLOCK *dst, const BLOCK *src)
+{
+ memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_QWORDS_IN_BLOCK);
+}
+
+static void xor_block(BLOCK *dst, const BLOCK *src)
+{
+ int i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ dst->v[i] ^= src->v[i];
+}
+
+static void load_block(BLOCK *dst, const void *input)
+{
+ unsigned i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
+}
+
+static void store_block(void *output, const BLOCK *src)
+{
+ unsigned i;
+
+ for (i = 0; i < ARGON2_QWORDS_IN_BLOCK; ++i)
+ store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
+}
+
+static void fill_first_blocks(uint8_t *blockhash, const KDF_ARGON2 *ctx)
+{
+ uint32_t l;
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+
+ /*
+ * Make the first and second block in each lane as G(H0||0||i)
+ * or G(H0||1||i).
+ */
+ for (l = 0; l < ctx->lanes; ++l) {
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, l);
+ blake2b_long(ctx->md, ctx->mac, blockhash_bytes, ARGON2_BLOCK_SIZE,
+ blockhash, ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&ctx->memory[l * ctx->lane_length + 0],
+ blockhash_bytes);
+ store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
+ blake2b_long(ctx->md, ctx->mac, blockhash_bytes, ARGON2_BLOCK_SIZE,
+ blockhash, ARGON2_PREHASH_SEED_LENGTH);
+ load_block(&ctx->memory[l * ctx->lane_length + 1],
+ blockhash_bytes);
+ }
+ OPENSSL_cleanse(blockhash_bytes, ARGON2_BLOCK_SIZE);
+}
+
+static void fill_block(const BLOCK *prev, const BLOCK *ref,
+ BLOCK *next, int with_xor)
+{
+ BLOCK blockR, tmp;
+ unsigned i;
+
+ copy_block(&blockR, ref);
+ xor_block(&blockR, prev);
+ copy_block(&tmp, &blockR);
+
+ if (with_xor)
+ xor_block(&tmp, next);
+
+ for (i = 0; i < 8; ++i)
+ PERMUTATION_P_COLUMN(blockR.v, i);
+
+ for (i = 0; i < 8; ++i)
+ PERMUTATION_P_ROW(blockR.v, i);
+
+ copy_block(next, &tmp);
+ xor_block(next, &blockR);
+}
+
+static void next_addresses(BLOCK *address_block, BLOCK *input_block,
+ const BLOCK *zero_block)
+{
+ input_block->v[6]++;
+ fill_block(zero_block, input_block, address_block, 0);
+ fill_block(zero_block, address_block, address_block, 0);
+}
+
+static int data_indep_addressing(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice)
+{
+ switch (ctx->type) {
+ case ARGON2_I:
+ return 1;
+ case ARGON2_ID:
+ return (pass == 0) && (slice < ARGON2_SYNC_POINTS / 2);
+ case ARGON2_D:
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Pass 0 (pass = 0):
+ * This lane: all already finished segments plus already constructed blocks
+ * in this segment
+ * Other lanes: all already finished segments
+ *
+ * Pass 1+:
+ * This lane: (SYNC_POINTS - 1) last segments plus already constructed
+ * blocks in this segment
+ * Other lanes: (SYNC_POINTS - 1) last segments
+ */
+static uint32_t index_alpha(const KDF_ARGON2 *ctx, uint32_t pass,
+ uint8_t slice, uint32_t index,
+ uint32_t pseudo_rand, int same_lane)
+{
+ uint32_t ref_area_sz;
+ uint64_t rel_pos;
+ uint32_t start_pos, abs_pos;
+
+ start_pos = 0;
+ switch (pass) {
+ case 0:
+ if (slice == 0)
+ ref_area_sz = index - 1;
+ else if (same_lane)
+ ref_area_sz = slice * ctx->segment_length + index - 1;
+ else
+ ref_area_sz = slice * ctx->segment_length +
+ ((index == 0) ? (-1) : 0);
+ break;
+ default:
+ if (same_lane)
+ ref_area_sz = ctx->lane_length - ctx->segment_length + index - 1;
+ else
+ ref_area_sz = ctx->lane_length - ctx->segment_length +
+ ((index == 0) ? (-1) : 0);
+ if (slice != ARGON2_SYNC_POINTS - 1)
+ start_pos = (slice + 1) * ctx->segment_length;
+ break;
+ }
+
+ rel_pos = pseudo_rand;
+ rel_pos = rel_pos * rel_pos >> 32;
+ rel_pos = ref_area_sz - 1 - (ref_area_sz * rel_pos >> 32);
+ abs_pos = (start_pos + rel_pos) % ctx->lane_length;
+
+ return abs_pos;
+}
+
+static void fill_segment(const KDF_ARGON2 *ctx, uint32_t pass, uint32_t lane,
+ uint8_t slice)
+{
+ BLOCK *ref_block = NULL, *curr_block = NULL;
+ BLOCK address_block, input_block, zero_block;
+ uint64_t rnd, ref_index, ref_lane;
+ uint32_t prev_offset;
+ uint32_t start_idx;
+ uint32_t j;
+ uint32_t curr_offset; /* Offset of the current block */
+
+ memset(&input_block, 0, sizeof(BLOCK));
+
+ if (ctx == NULL)
+ return;
+
+ if (data_indep_addressing(ctx, pass, slice)) {
+ init_block_value(&zero_block, 0);
+ init_block_value(&input_block, 0);
+
+ input_block.v[0] = pass;
+ input_block.v[1] = lane;
+ input_block.v[2] = slice;
+ input_block.v[3] = ctx->memory_blocks;
+ input_block.v[4] = ctx->passes;
+ input_block.v[5] = ctx->type;
+ }
+
+ start_idx = 0;
+
+ /* We've generated the first two blocks. Generate the 1st block of addrs. */
+ if ((pass == 0) && (slice == 0)) {
+ start_idx = 2;
+ if (data_indep_addressing(ctx, pass, slice))
+ next_addresses(&address_block, &input_block, &zero_block);
+ }
+
+ curr_offset = lane * ctx->lane_length + slice * ctx->segment_length
+ + start_idx;
+
+ if ((curr_offset % ctx->lane_length) == 0)
+ prev_offset = curr_offset + ctx->lane_length - 1;
+ else
+ prev_offset = curr_offset - 1;
+
+ for (j = start_idx; j < ctx->segment_length; ++j, ++curr_offset, ++prev_offset) {
+ if (curr_offset % ctx->lane_length == 1)
+ prev_offset = curr_offset - 1;
+
+ /* Taking pseudo-random value from the previous block. */
+ if (data_indep_addressing(ctx, pass, slice)) {
+ if (j % ARGON2_ADDRESSES_IN_BLOCK == 0)
+ next_addresses(&address_block, &input_block, &zero_block);
+ rnd = address_block.v[j % ARGON2_ADDRESSES_IN_BLOCK];
+ } else {
+ rnd = ctx->memory[prev_offset].v[0];
+ }
+
+ /* Computing the lane of the reference block */
+ ref_lane = ((rnd >> 32)) % ctx->lanes;
+ /* Can not reference other lanes yet */
+ if ((pass == 0) && (slice == 0))
+ ref_lane = lane;
+
+ /* Computing the number of possible reference block within the lane. */
+ ref_index = index_alpha(ctx, pass, slice, j, rnd & 0xFFFFFFFF,
+ ref_lane == lane);
+
+ /* Creating a new block */
+ ref_block = ctx->memory + ctx->lane_length * ref_lane + ref_index;
+ curr_block = ctx->memory + curr_offset;
+ if (ARGON2_VERSION_10 == ctx->version) {
+ /* Version 1.2.1 and earlier: overwrite, not XOR */
+ fill_block(ctx->memory + prev_offset, ref_block, curr_block, 0);
+ continue;
+ }
+
+ fill_block(ctx->memory + prev_offset, ref_block, curr_block,
+ pass == 0 ? 0 : 1);
+ }
+}
+
+# if !defined(ARGON2_NO_THREADS)
+
+static uint32_t fill_segment_thr(void *thread_data)
+{
+ ARGON2_THREAD_DATA *my_data;
+
+ my_data = (ARGON2_THREAD_DATA *) thread_data;
+ fill_segment(my_data->ctx, my_data->pos.pass, my_data->pos.lane,
+ my_data->pos.slice);
+
+ return 0;
+}
+
+static int fill_mem_blocks_mt(KDF_ARGON2 *ctx)
+{
+ uint32_t r, s, l, ll;
+ void **t;
+ ARGON2_THREAD_DATA *t_data;
+
+ t = OPENSSL_zalloc(sizeof(void *)*ctx->lanes);
+ t_data = OPENSSL_zalloc(ctx->lanes * sizeof(ARGON2_THREAD_DATA));
+
+ if (t == NULL || t_data == NULL)
+ goto fail;
+
+ for (r = 0; r < ctx->passes; ++r) {
+ for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {
+ for (l = 0; l < ctx->lanes; ++l) {
+ ARGON2_POS p;
+ if (l >= ctx->threads) {
+ if (ossl_crypto_thread_join(t[l - ctx->threads], NULL) == 0)
+ goto fail;
+ if (ossl_crypto_thread_clean(t[l - ctx->threads]) == 0)
+ goto fail;
+ t[l] = NULL;
+ }
+
+ p.pass = r;
+ p.lane = l;
+ p.slice = (uint8_t)s;
+ p.index = 0;
+
+ t_data[l].ctx = ctx;
+ memcpy(&(t_data[l].pos), &p, sizeof(ARGON2_POS));
+ t[l] = ossl_crypto_thread_start(ctx->libctx, &fill_segment_thr,
+ (void *) &t_data[l]);
+ if (t[l] == NULL) {
+ for (ll = 0; ll < l; ++ll) {
+ if (ossl_crypto_thread_join(t[ll], NULL) == 0)
+ goto fail;
+ if (ossl_crypto_thread_clean(t[ll]) == 0)
+ goto fail;
+ t[ll] = NULL;
+ }
+ goto fail;
+ }
+ }
+ for (l = ctx->lanes - ctx->threads; l < ctx->lanes; ++l) {
+ if (ossl_crypto_thread_join(t[l], NULL) == 0)
+ goto fail;
+ if (ossl_crypto_thread_clean(t[l]) == 0)
+ goto fail;
+ t[l] = NULL;
+ }
+ }
+ }
+
+ OPENSSL_free(t_data);
+ OPENSSL_free(t);
+
+ return 1;
+
+fail:
+ if (t_data != NULL)
+ OPENSSL_free(t_data);
+ if (t != NULL)
+ OPENSSL_free(t);
+ return 0;
+}
+
+# endif /* !defined(ARGON2_NO_THREADS) */
+
+static int fill_mem_blocks_st(KDF_ARGON2 *ctx)
+{
+ uint32_t r, s, l;
+
+ for (r = 0; r < ctx->passes; ++r)
+ for (s = 0; s < ARGON2_SYNC_POINTS; ++s)
+ for (l = 0; l < ctx->lanes; ++l)
+ fill_segment(ctx, r, l, s);
+ return 1;
+}
+
+static ossl_inline int fill_memory_blocks(KDF_ARGON2 *ctx)
+{
+# if !defined(ARGON2_NO_THREADS)
+ return ctx->threads == 1 ? fill_mem_blocks_st(ctx) : fill_mem_blocks_mt(ctx);
+# else
+ return ctx->threads == 1 ? fill_mem_blocks_st(ctx) : 0;
+# endif
+}
+
+static void initial_hash(uint8_t *blockhash, KDF_ARGON2 *ctx)
+{
+ EVP_MD_CTX *mdctx;
+ uint8_t value[sizeof(uint32_t)];
+ unsigned int tmp;
+ uint32_t args[7];
+
+ if (ctx == NULL || blockhash == NULL)
+ return;
+
+ args[0] = ctx->lanes;
+ args[1] = ctx->outlen;
+ args[2] = ctx->m_cost;
+ args[3] = ctx->t_cost;
+ args[4] = ctx->version;
+ args[5] = (uint32_t) ctx->type;
+ args[6] = ctx->pwdlen;
+
+ mdctx = EVP_MD_CTX_create();
+ if (mdctx == NULL || EVP_DigestInit_ex(mdctx, ctx->md, NULL) != 1)
+ goto fail;
+
+ for (tmp = 0; tmp < sizeof(args) / sizeof(uint32_t); ++tmp) {
+ store32((uint8_t *) &value, args[tmp]);
+ if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+ goto fail;
+ }
+
+ if (ctx->pwd != NULL) {
+ if (EVP_DigestUpdate(mdctx, ctx->pwd, ctx->pwdlen) != 1)
+ goto fail;
+ if (ctx->early_clean) {
+ OPENSSL_cleanse(ctx->pwd, ctx->pwdlen);
+ ctx->pwdlen = 0;
+ }
+ }
+
+ store32((uint8_t *) &value, ctx->saltlen);
+
+ if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+ goto fail;
+
+ if (ctx->salt != NULL)
+ if (EVP_DigestUpdate(mdctx, ctx->salt, ctx->saltlen) != 1)
+ goto fail;
+
+ store32((uint8_t *) &value, ctx->secretlen);
+ if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+ goto fail;
+
+ if (ctx->secret != NULL) {
+ if (EVP_DigestUpdate(mdctx, ctx->secret, ctx->secretlen) != 1)
+ goto fail;
+ if (ctx->early_clean) {
+ OPENSSL_cleanse(ctx->secret, ctx->secretlen);
+ ctx->secretlen = 0;
+ }
+ }
+
+ store32((uint8_t *) &value, ctx->adlen);
+ if (EVP_DigestUpdate(mdctx, &value, sizeof(value)) != 1)
+ goto fail;
+
+ if (ctx->ad != NULL)
+ if (EVP_DigestUpdate(mdctx, ctx->ad, ctx->adlen) != 1)
+ goto fail;
+
+ tmp = ARGON2_PREHASH_DIGEST_LENGTH;
+ if (EVP_DigestFinal_ex(mdctx, blockhash, &tmp) != 1)
+ goto fail;
+
+fail:
+ EVP_MD_CTX_destroy(mdctx);
+}
+
+static int initialize(KDF_ARGON2 *ctx)
+{
+ uint8_t blockhash[ARGON2_PREHASH_SEED_LENGTH];
+
+ if (ctx == NULL)
+ return 0;
+
+ if (ctx->memory_blocks * sizeof(BLOCK) / sizeof(BLOCK) != ctx->memory_blocks)
+ return 0;
+
+ if (ctx->type != ARGON2_D)
+ ctx->memory = OPENSSL_secure_zalloc(ctx->memory_blocks *
+ sizeof(BLOCK));
+ else
+ ctx->memory = OPENSSL_zalloc(ctx->memory_blocks *
+ sizeof(BLOCK));
+
+ if (ctx->memory == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE,
+ "cannot allocate required memory");
+ return 0;
+ }
+
+ initial_hash(blockhash, ctx);
+ OPENSSL_cleanse(blockhash + ARGON2_PREHASH_DIGEST_LENGTH,
+ ARGON2_PREHASH_SEED_LENGTH - ARGON2_PREHASH_DIGEST_LENGTH);
+ fill_first_blocks(blockhash, ctx);
+ OPENSSL_cleanse(blockhash, ARGON2_PREHASH_SEED_LENGTH);
+
+ return 1;
+}
+
+static void finalize(const KDF_ARGON2 *ctx, void *out)
+{
+ BLOCK blockhash;
+ uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
+ uint32_t last_block_in_lane;
+ uint32_t l;
+
+ if (ctx == NULL)
+ return;
+
+ copy_block(&blockhash, ctx->memory + ctx->lane_length - 1);
+
+ /* XOR the last blocks */
+ for (l = 1; l < ctx->lanes; ++l) {
+ last_block_in_lane = l * ctx->lane_length + (ctx->lane_length - 1);
+ xor_block(&blockhash, ctx->memory + last_block_in_lane);
+ }
+
+ /* Hash the result */
+ store_block(blockhash_bytes, &blockhash);
+ blake2b_long(ctx->md, ctx->mac, out, ctx->outlen, blockhash_bytes,
+ ARGON2_BLOCK_SIZE);
+ OPENSSL_cleanse(blockhash.v, ARGON2_BLOCK_SIZE);
+ OPENSSL_cleanse(blockhash_bytes, ARGON2_BLOCK_SIZE);
+
+ if (ctx->type != ARGON2_D)
+ OPENSSL_secure_clear_free(ctx->memory,
+ ctx->memory_blocks * sizeof(BLOCK));
+ else
+ OPENSSL_clear_free(ctx->memory,
+ ctx->memory_blocks * sizeof(BLOCK));
+}
+
+static int blake2b_mac(EVP_MAC *mac, void *out, size_t outlen, const void *in,
+ size_t inlen, const void *key, size_t keylen)
+{
+ int ret = 0;
+ size_t par_n = 0, out_written;
+ EVP_MAC_CTX *ctx = NULL;
+ OSSL_PARAM par[3];
+
+ if ((ctx = EVP_MAC_CTX_new(mac)) == NULL)
+ goto fail;
+
+ par[par_n++] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_KEY,
+ (void *) key, keylen);
+ par[par_n++] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen);
+ par[par_n++] = OSSL_PARAM_construct_end();
+
+ ret = EVP_MAC_CTX_set_params(ctx, par) == 1
+ && EVP_MAC_init(ctx, NULL, 0, NULL) == 1
+ && EVP_MAC_update(ctx, in, inlen) == 1
+ && EVP_MAC_final(ctx, out, (size_t *) &out_written, outlen) == 1;
+
+fail:
+ EVP_MAC_CTX_free(ctx);
+ return ret;
+}
+
+static int blake2b_md(EVP_MD *md, void *out, size_t outlen, const void *in,
+ size_t inlen)
+{
+ int ret = 0;
+ EVP_MD_CTX *ctx = NULL;
+ OSSL_PARAM par[2];
+
+ if ((ctx = EVP_MD_CTX_create()) == NULL)
+ return 0;
+
+ par[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_SIZE, &outlen);
+ par[1] = OSSL_PARAM_construct_end();
+
+ ret = EVP_DigestInit_ex2(ctx, md, par) == 1
+ && EVP_DigestUpdate(ctx, in, inlen) == 1
+ && EVP_DigestFinal_ex(ctx, out, NULL) == 1;
+
+ EVP_MD_CTX_free(ctx);
+ return ret;
+}
+
+static int blake2b(EVP_MD *md, EVP_MAC *mac, void *out, size_t outlen,
+ const void *in, size_t inlen, const void *key, size_t keylen)
+{
+ if (out == NULL || outlen == 0)
+ return 0;
+
+ if (key == NULL || keylen == 0)
+ return blake2b_md(md, out, outlen, in, inlen);
+
+ return blake2b_mac(mac, out, outlen, in, inlen, key, keylen);
+}
+
+static int blake2b_long(EVP_MD *md, EVP_MAC *mac, unsigned char *out,
+ size_t outlen, const void *in, size_t inlen)
+{
+ int ret = 0;
+ EVP_MD_CTX *ctx = NULL;
+ uint32_t outlen_curr;
+ uint8_t outbuf[BLAKE2B_OUTBYTES];
+ uint8_t inbuf[BLAKE2B_OUTBYTES];
+ uint8_t outlen_bytes[sizeof(uint32_t)] = {0};
+ OSSL_PARAM par[2];
+ size_t outlen_md;
+
+ if (out == NULL || outlen == 0)
+ return 0;
+
+ /* Ensure little-endian byte order */
+ store32(outlen_bytes, (uint32_t)outlen);
+
+ if ((ctx = EVP_MD_CTX_create()) == NULL)
+ return 0;
+
+ outlen_md = (outlen <= BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES;
+ par[0] = OSSL_PARAM_construct_size_t(OSSL_DIGEST_PARAM_SIZE, &outlen_md);
+ par[1] = OSSL_PARAM_construct_end();
+
+ ret = EVP_DigestInit_ex2(ctx, md, par) == 1
+ && EVP_DigestUpdate(ctx, outlen_bytes, sizeof(outlen_bytes)) == 1
+ && EVP_DigestUpdate(ctx, in, inlen) == 1
+ && EVP_DigestFinal_ex(ctx, (outlen > BLAKE2B_OUTBYTES) ? outbuf : out,
+ NULL) == 1;
+
+ if (ret == 0)
+ goto fail;
+
+ if (outlen > BLAKE2B_OUTBYTES) {
+ memcpy(out, outbuf, BLAKE2B_OUTBYTES / 2);
+ out += BLAKE2B_OUTBYTES / 2;
+ outlen_curr = (uint32_t) outlen - BLAKE2B_OUTBYTES / 2;
+
+ while (outlen_curr > BLAKE2B_OUTBYTES) {
+ memcpy(inbuf, outbuf, BLAKE2B_OUTBYTES);
+ if (blake2b(md, mac, outbuf, BLAKE2B_OUTBYTES, inbuf,
+ BLAKE2B_OUTBYTES, NULL, 0) != 1)
+ goto fail;
+ memcpy(out, outbuf, BLAKE2B_OUTBYTES / 2);
+ out += BLAKE2B_OUTBYTES / 2;
+ outlen_curr -= BLAKE2B_OUTBYTES / 2;
+ }
+
+ memcpy(inbuf, outbuf, BLAKE2B_OUTBYTES);
+ if (blake2b(md, mac, outbuf, outlen_curr, inbuf, BLAKE2B_OUTBYTES,
+ NULL, 0) != 1)
+ goto fail;
+ memcpy(out, outbuf, outlen_curr);
+ }
+ ret = 1;
+
+fail:
+ EVP_MD_CTX_free(ctx);
+ return ret;
+}
+
+static void kdf_argon2_init(KDF_ARGON2 *c, ARGON2_TYPE type)
+{
+ OSSL_LIB_CTX *libctx;
+
+ libctx = c->libctx;
+ memset(c, 0, sizeof(*c));
+
+ c->libctx = libctx;
+ c->outlen = ARGON2_DEFAULT_OUTLEN;
+ c->t_cost = ARGON2_DEFAULT_T_COST;
+ c->m_cost = ARGON2_DEFAULT_M_COST;
+ c->lanes = ARGON2_DEFAULT_LANES;
+ c->threads = ARGON2_DEFAULT_THREADS;
+ c->version = ARGON2_DEFAULT_VERSION;
+ c->type = type;
+}
+
+static void *kdf_argon2d_new(void *provctx)
+{
+ KDF_ARGON2 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+ kdf_argon2_init(ctx, ARGON2_D);
+ return ctx;
+}
+
+static void *kdf_argon2i_new(void *provctx)
+{
+ KDF_ARGON2 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+ kdf_argon2_init(ctx, ARGON2_I);
+ return ctx;
+}
+
+static void *kdf_argon2id_new(void *provctx)
+{
+ KDF_ARGON2 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+ kdf_argon2_init(ctx, ARGON2_ID);
+ return ctx;
+}
+
+static void kdf_argon2_free(void *vctx)
+{
+ KDF_ARGON2 *ctx = (KDF_ARGON2 *)vctx;
+
+ if (ctx == NULL)
+ return;
+
+ if (ctx->pwd != NULL)
+ OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+
+ if (ctx->salt != NULL)
+ OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+
+ if (ctx->secret != NULL)
+ OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+
+ if (ctx->ad != NULL)
+ OPENSSL_clear_free(ctx->ad, ctx->adlen);
+
+ EVP_MD_free(ctx->md);
+ EVP_MAC_free(ctx->mac);
+
+ OPENSSL_free(ctx->propq);
+
+ memset(ctx, 0, sizeof(*ctx));
+
+ OPENSSL_free(ctx);
+}
+
+static int kdf_argon2_derive(void *vctx, unsigned char *out, size_t outlen,
+ const OSSL_PARAM params[])
+{
+ KDF_ARGON2 *ctx;
+ uint32_t memory_blocks, segment_length;
+
+ ctx = (KDF_ARGON2 *)vctx;
+
+ if (!ossl_prov_is_running() || !kdf_argon2_set_ctx_params(vctx, params))
+ return 0;
+
+ if (ctx->mac == NULL)
+ ctx->mac = EVP_MAC_fetch(ctx->libctx, "blake2bmac", ctx->propq);
+ if (ctx->mac == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MAC,
+ "cannot fetch blake2bmac");
+ return 0;
+ }
+
+ if (ctx->md == NULL)
+ ctx->md = EVP_MD_fetch(ctx->libctx, "blake2b512", ctx->propq);
+ if (ctx->md == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST,
+ "cannot fetch blake2b512");
+ return 0;
+ }
+
+ if (ctx->salt == NULL || ctx->saltlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ if (outlen != ctx->outlen) {
+ if (OSSL_PARAM_locate((OSSL_PARAM *)params, "size") != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (!kdf_argon2_ctx_set_out_length(ctx, (uint32_t) outlen))
+ return 0;
+ }
+
+ switch (ctx->type) {
+ case ARGON2_D:
+ case ARGON2_I:
+ case ARGON2_ID:
+ break;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MODE, "invalid Argon2 type");
+ return 0;
+ }
+
+ if (ctx->threads > 1) {
+# ifdef ARGON2_NO_THREADS
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+ "requested %u threads, single-threaded mode supported only",
+ ctx->threads);
+ return 0;
+# else
+ if (ctx->threads > ossl_get_avail_threads(ctx->libctx)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+ "requested %u threads, available: %u",
+ ctx->threads, ossl_get_avail_threads(ctx->libctx));
+ return 0;
+ }
+# endif
+ if (ctx->threads > ctx->lanes) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+ "requested more threads (%u) than lanes (%u)",
+ ctx->threads, ctx->lanes);
+ return 0;
+ }
+ }
+
+ if (ctx->m_cost < 8 * ctx->lanes) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE,
+ "m_cost must be greater or equal than 8 times the number of lanes");
+ return 0;
+ }
+
+ memory_blocks = ctx->m_cost;
+ if (memory_blocks < 2 * ARGON2_SYNC_POINTS * ctx->lanes)
+ memory_blocks = 2 * ARGON2_SYNC_POINTS * ctx->lanes;
+
+ /* Ensure that all segments have equal length */
+ segment_length = memory_blocks / (ctx->lanes * ARGON2_SYNC_POINTS);
+ memory_blocks = segment_length * (ctx->lanes * ARGON2_SYNC_POINTS);
+
+ ctx->memory = NULL;
+ ctx->memory_blocks = memory_blocks;
+ ctx->segment_length = segment_length;
+ ctx->passes = ctx->t_cost;
+ ctx->lane_length = segment_length * ARGON2_SYNC_POINTS;
+
+ if (initialize(ctx) != 1)
+ return 0;
+
+ if (fill_memory_blocks(ctx) != 1)
+ return 0;
+
+ finalize(ctx, out);
+
+ return 1;
+}
+
+static void kdf_argon2_reset(void *vctx)
+{
+ OSSL_LIB_CTX *libctx;
+ KDF_ARGON2 *ctx;
+ ARGON2_TYPE type;
+
+ ctx = (KDF_ARGON2 *) vctx;
+ type = ctx->type;
+ libctx = ctx->libctx;
+
+ EVP_MD_free(ctx->md);
+ EVP_MAC_free(ctx->mac);
+
+ OPENSSL_free(ctx->propq);
+
+ if (ctx->pwd != NULL)
+ OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+
+ if (ctx->salt != NULL)
+ OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+
+ if (ctx->secret != NULL)
+ OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+
+ if (ctx->ad != NULL)
+ OPENSSL_clear_free(ctx->ad, ctx->adlen);
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->libctx = libctx;
+ kdf_argon2_init(ctx, type);
+}
+
+static int kdf_argon2_ctx_set_threads(KDF_ARGON2 *ctx, uint32_t threads)
+{
+ if (threads < ARGON2_MIN_THREADS) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+ "min threads: %u", ARGON2_MIN_THREADS);
+ return 0;
+ }
+
+ if (threads > ARGON2_MAX_THREADS) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_THREAD_POOL_SIZE,
+ "max threads: %u", ARGON2_MAX_THREADS);
+ return 0;
+ }
+
+ ctx->threads = threads;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_lanes(KDF_ARGON2 *ctx, uint32_t lanes)
+{
+ if (lanes > ARGON2_MAX_LANES) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER,
+ "max lanes: %u", ARGON2_MAX_LANES);
+ return 0;
+ }
+
+ if (lanes < ARGON2_MIN_LANES) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER,
+ "min lanes: %u", ARGON2_MIN_LANES);
+ return 0;
+ }
+
+ ctx->lanes = lanes;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_t_cost(KDF_ARGON2 *ctx, uint32_t t_cost)
+{
+ /* ARGON2_MAX_MEMORY == max m_cost value, so skip check */
+
+ if (t_cost < ARGON2_MIN_TIME) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT,
+ "min: %u", ARGON2_MIN_TIME);
+ return 0;
+ }
+
+ ctx->t_cost = t_cost;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_m_cost(KDF_ARGON2 *ctx, uint32_t m_cost)
+{
+ /* ARGON2_MAX_MEMORY == max m_cost value, so skip check */
+
+ if (m_cost < ARGON2_MIN_MEMORY) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MEMORY_SIZE, "min: %u",
+ ARGON2_MIN_MEMORY);
+ return 0;
+ }
+
+ ctx->m_cost = m_cost;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_out_length(KDF_ARGON2 *ctx, uint32_t outlen)
+{
+ /*
+ * ARGON2_MAX_OUT_LENGTH == max outlen value, so upper bounds checks
+ * are always satisfied; to suppress compiler if statement tautology
+ * warnings, these checks are skipped.
+ */
+
+ if (outlen < ARGON2_MIN_OUT_LENGTH) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH, "min: %u",
+ ARGON2_MIN_OUT_LENGTH);
+ return 0;
+ }
+
+ ctx->outlen = outlen;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_secret(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+ size_t buflen;
+
+ if (p->data == NULL)
+ return 0;
+
+ if (ctx->secret != NULL) {
+ OPENSSL_clear_free(ctx->secret, ctx->secretlen);
+ ctx->secret = NULL;
+ ctx->secretlen = 0U;
+ }
+
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->secret, 0, &buflen))
+ return 0;
+
+ if (buflen > ARGON2_MAX_SECRET) {
+ OPENSSL_free(ctx->secret);
+ ctx->secret = NULL;
+ ctx->secretlen = 0U;
+ return 0;
+ }
+
+ ctx->secretlen = (uint32_t) buflen;
+ return 1;
+}
+
+static int kdf_argon2_ctx_set_pwd(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+ size_t buflen;
+
+ if (p->data == NULL)
+ return 0;
+
+ if (ctx->pwd != NULL) {
+ OPENSSL_clear_free(ctx->pwd, ctx->pwdlen);
+ ctx->pwd = NULL;
+ ctx->pwdlen = 0U;
+ }
+
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->pwd, 0, &buflen))
+ return 0;
+
+ if (buflen > ARGON2_MAX_PWD_LENGTH) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "max: %u",
+ ARGON2_MAX_PWD_LENGTH);
+ goto fail;
+ }
+
+ ctx->pwdlen = (uint32_t) buflen;
+ return 1;
+
+fail:
+ OPENSSL_free(ctx->pwd);
+ ctx->pwd = NULL;
+ ctx->pwdlen = 0U;
+ return 0;
+}
+
+static int kdf_argon2_ctx_set_salt(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+ size_t buflen;
+
+ if (p->data == NULL)
+ return 0;
+
+ if (ctx->salt != NULL) {
+ OPENSSL_clear_free(ctx->salt, ctx->saltlen);
+ ctx->salt = NULL;
+ ctx->saltlen = 0U;
+ }
+
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0, &buflen))
+ return 0;
+
+ if (buflen < ARGON2_MIN_SALT_LENGTH) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "min: %u",
+ ARGON2_MIN_SALT_LENGTH);
+ goto fail;
+ }
+
+ if (buflen > ARGON2_MAX_SALT_LENGTH) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH, "max: %u",
+ ARGON2_MAX_SALT_LENGTH);
+ goto fail;
+ }
+
+ ctx->saltlen = (uint32_t) buflen;
+ return 1;
+
+fail:
+ OPENSSL_free(ctx->salt);
+ ctx->salt = NULL;
+ ctx->saltlen = 0U;
+ return 0;
+}
+
+static int kdf_argon2_ctx_set_ad(KDF_ARGON2 *ctx, const OSSL_PARAM *p)
+{
+ size_t buflen;
+
+ if (p->data == NULL)
+ return 0;
+
+ if (ctx->ad != NULL) {
+ OPENSSL_clear_free(ctx->ad, ctx->adlen);
+ ctx->ad = NULL;
+ ctx->adlen = 0U;
+ }
+
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->ad, 0, &buflen))
+ return 0;
+
+ if (buflen > ARGON2_MAX_AD_LENGTH) {
+ OPENSSL_free(ctx->ad);
+ ctx->ad = NULL;
+ ctx->adlen = 0U;
+ return 0;
+ }
+
+ ctx->adlen = (uint32_t) buflen;
+ return 1;
+}
+
+static void kdf_argon2_ctx_set_flag_early_clean(KDF_ARGON2 *ctx, uint32_t f)
+{
+ ctx->early_clean = !!(f);
+}
+
+static int kdf_argon2_ctx_set_version(KDF_ARGON2 *ctx, uint32_t version)
+{
+ switch (version) {
+ case ARGON2_VERSION_10:
+ case ARGON2_VERSION_13:
+ ctx->version = version;
+ return 1;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_MODE,
+ "invalid Argon2 version");
+ return 0;
+ }
+}
+
+static int set_property_query(KDF_ARGON2 *ctx, const char *propq)
+{
+ OPENSSL_free(ctx->propq);
+ ctx->propq = NULL;
+ if (propq != NULL) {
+ ctx->propq = OPENSSL_strdup(propq);
+ if (ctx->propq == NULL)
+ return 0;
+ }
+ EVP_MD_free(ctx->md);
+ ctx->md = NULL;
+ EVP_MAC_free(ctx->mac);
+ ctx->mac = NULL;
+ return 1;
+}
+
+static int kdf_argon2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_ARGON2 *ctx;
+ uint32_t u32_value;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ ctx = (KDF_ARGON2 *) vctx;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!kdf_argon2_ctx_set_pwd(ctx, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
+ if (!kdf_argon2_ctx_set_salt(ctx, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET)) != NULL)
+ if (!kdf_argon2_ctx_set_secret(ctx, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_AD)) != NULL)
+ if (!kdf_argon2_ctx_set_ad(ctx, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_out_length(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_t_cost(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_THREADS)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_threads(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_LANES)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_lanes(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_MEMCOST)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_m_cost(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_EARLY_CLEAN)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ kdf_argon2_ctx_set_flag_early_clean(ctx, u32_value);
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ARGON2_VERSION)) != NULL) {
+ if (!OSSL_PARAM_get_uint32(p, &u32_value))
+ return 0;
+ if (!kdf_argon2_ctx_set_version(ctx, u32_value))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES)) != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || !set_property_query(ctx, p->data))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_argon2_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_ARGON2_AD, NULL, 0),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ITER, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_THREADS, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_LANES, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_MEMCOST, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_EARLY_CLEAN, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_ARGON2_VERSION, NULL),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return known_settable_ctx_params;
+}
+
+static int kdf_argon2_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ (void) vctx;
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+
+ return -2;
+}
+
+static const OSSL_PARAM *kdf_argon2_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_argon2i_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2i_new },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_kdf_argon2d_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2d_new },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_kdf_argon2id_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_argon2id_new },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_argon2_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_argon2_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_argon2_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_argon2_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_argon2_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_argon2_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+#endif
diff --git a/crypto/openssl/providers/implementations/kdfs/build.info b/crypto/openssl/providers/implementations/kdfs/build.info
new file mode 100644
index 000000000000..3b7687b8f3a5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/build.info
@@ -0,0 +1,46 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$TLS1_PRF_GOAL=../../libdefault.a ../../libfips.a
+$HKDF_GOAL=../../libdefault.a ../../libfips.a
+$KBKDF_GOAL=../../libdefault.a ../../libfips.a
+$KRB5KDF_GOAL=../../libdefault.a
+$PBKDF1_GOAL=../../liblegacy.a
+$PBKDF2_GOAL=../../libdefault.a ../../libfips.a
+$PVKKDF_GOAL=../../liblegacy.a
+$PKCS12KDF_GOAL=../../libdefault.a
+$SSKDF_GOAL=../../libdefault.a ../../libfips.a
+$SCRYPT_GOAL=../../libdefault.a
+$SSHKDF_GOAL=../../libdefault.a ../../libfips.a
+$X942KDF_GOAL=../../libdefault.a ../../libfips.a
+$HMAC_DRBG_KDF_GOAL=../../libdefault.a
+$ARGON2_GOAL=../../libdefault.a
+
+SOURCE[$TLS1_PRF_GOAL]=tls1_prf.c
+
+SOURCE[$HKDF_GOAL]=hkdf.c
+
+SOURCE[$KBKDF_GOAL]=kbkdf.c
+
+SOURCE[$KRB5KDF_GOAL]=krb5kdf.c
+
+SOURCE[$PBKDF1_GOAL]=pbkdf1.c
+
+SOURCE[$PBKDF2_GOAL]=pbkdf2.c
+# Extra code to satisfy the FIPS and non-FIPS separation.
+# When the PBKDF2 moves to legacy, this can be removed.
+SOURCE[$PBKDF2_GOAL]=pbkdf2_fips.c
+
+SOURCE[$PBKDF1_GOAL]=pvkkdf.c
+
+SOURCE[$PKCS12KDF_GOAL]=pkcs12kdf.c
+
+SOURCE[$SSKDF_GOAL]=sskdf.c
+
+SOURCE[$SCRYPT_GOAL]=scrypt.c
+SOURCE[$SSHKDF_GOAL]=sshkdf.c
+SOURCE[$X942KDF_GOAL]=x942kdf.c
+DEPEND[x942kdf.o]=../../common/include/prov/der_wrap.h
+
+SOURCE[$HMAC_DRBG_KDF_GOAL]=hmacdrbg_kdf.c
+SOURCE[$ARGON2_GOAL]=argon2.c
diff --git a/crypto/openssl/providers/implementations/kdfs/hkdf.c b/crypto/openssl/providers/implementations/kdfs/hkdf.c
new file mode 100644
index 000000000000..6e3e8b5c0f66
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/hkdf.c
@@ -0,0 +1,954 @@
+/*
+ * Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * HMAC low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "internal/packet.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+#include "internal/e_os.h"
+#include "internal/params.h"
+
+#define HKDF_MAXBUF 2048
+#define HKDF_MAXINFO (32*1024)
+
+static OSSL_FUNC_kdf_newctx_fn kdf_hkdf_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_hkdf_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_hkdf_free;
+static OSSL_FUNC_kdf_reset_fn kdf_hkdf_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_hkdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_hkdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_hkdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_hkdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_hkdf_get_ctx_params;
+static OSSL_FUNC_kdf_derive_fn kdf_tls1_3_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_3_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_3_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_3_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_3_get_ctx_params;
+
+static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len);
+static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *ikm, size_t ikm_len,
+ unsigned char *prk, size_t prk_len);
+static int HKDF_Expand(const EVP_MD *evp_md,
+ const unsigned char *prk, size_t prk_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len);
+
+/* Settable context parameters that are common across HKDF and the TLS KDF */
+#define HKDF_COMMON_SETTABLES \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0), \
+ OSSL_PARAM_int(OSSL_KDF_PARAM_MODE, NULL), \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0)
+
+/* Gettable context parameters that are common across HKDF and the TLS KDF */
+#define HKDF_COMMON_GETTABLES \
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0)
+
+typedef struct {
+ void *provctx;
+ int mode;
+ PROV_DIGEST digest;
+ unsigned char *salt;
+ size_t salt_len;
+ unsigned char *key;
+ size_t key_len;
+ unsigned char *prefix;
+ size_t prefix_len;
+ unsigned char *label;
+ size_t label_len;
+ unsigned char *data;
+ size_t data_len;
+ unsigned char *info;
+ size_t info_len;
+ OSSL_FIPS_IND_DECLARE
+} KDF_HKDF;
+
+static void *kdf_hkdf_new(void *provctx)
+{
+ KDF_HKDF *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) {
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ }
+ return ctx;
+}
+
+static void kdf_hkdf_free(void *vctx)
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+
+ if (ctx != NULL) {
+ kdf_hkdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_hkdf_reset(void *vctx)
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+ void *provctx = ctx->provctx;
+
+ ossl_prov_digest_reset(&ctx->digest);
+#ifdef OPENSSL_PEDANTIC_ZEROIZATION
+ OPENSSL_clear_free(ctx->salt, ctx->salt_len);
+#else
+ OPENSSL_free(ctx->salt);
+#endif
+ OPENSSL_free(ctx->prefix);
+ OPENSSL_free(ctx->label);
+ OPENSSL_clear_free(ctx->data, ctx->data_len);
+ OPENSSL_clear_free(ctx->key, ctx->key_len);
+ OPENSSL_clear_free(ctx->info, ctx->info_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static void *kdf_hkdf_dup(void *vctx)
+{
+ const KDF_HKDF *src = (const KDF_HKDF *)vctx;
+ KDF_HKDF *dest;
+
+ dest = kdf_hkdf_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->salt, src->salt_len, &dest->salt,
+ &dest->salt_len)
+ || !ossl_prov_memdup(src->key, src->key_len,
+ &dest->key , &dest->key_len)
+ || !ossl_prov_memdup(src->prefix, src->prefix_len,
+ &dest->prefix, &dest->prefix_len)
+ || !ossl_prov_memdup(src->label, src->label_len,
+ &dest->label, &dest->label_len)
+ || !ossl_prov_memdup(src->data, src->data_len,
+ &dest->data, &dest->data_len)
+ || !ossl_prov_memdup(src->info, src->info_len,
+ &dest->info, &dest->info_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->mode = src->mode;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ kdf_hkdf_free(dest);
+ return NULL;
+}
+
+static size_t kdf_hkdf_size(KDF_HKDF *ctx)
+{
+ int sz;
+ const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+
+ if (ctx->mode != EVP_KDF_HKDF_MODE_EXTRACT_ONLY)
+ return SIZE_MAX;
+
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ sz = EVP_MD_get_size(md);
+ if (sz <= 0)
+ return 0;
+
+ return sz;
+}
+
+#ifdef FIPS_MODULE
+static int fips_hkdf_key_check_passed(KDF_HKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->key_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "HKDF", "Key size",
+ ossl_fips_config_hkdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int kdf_hkdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_hkdf_set_ctx_params(ctx, params))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ if (ctx->key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ if (keylen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ switch (ctx->mode) {
+ case EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND:
+ default:
+ return HKDF(libctx, md, ctx->salt, ctx->salt_len,
+ ctx->key, ctx->key_len, ctx->info, ctx->info_len, key, keylen);
+
+ case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
+ return HKDF_Extract(libctx, md, ctx->salt, ctx->salt_len,
+ ctx->key, ctx->key_len, key, keylen);
+
+ case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
+ return HKDF_Expand(md, ctx->key, ctx->key_len, ctx->info,
+ ctx->info_len, key, keylen);
+ }
+}
+
+static int hkdf_common_set_ctx_params(KDF_HKDF *ctx, const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ const OSSL_PARAM *p;
+ int n;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ const EVP_MD *md = NULL;
+
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE)) != NULL) {
+ if (p->data_type == OSSL_PARAM_UTF8_STRING) {
+ if (OPENSSL_strcasecmp(p->data, "EXTRACT_AND_EXPAND") == 0) {
+ ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND;
+ } else if (OPENSSL_strcasecmp(p->data, "EXTRACT_ONLY") == 0) {
+ ctx->mode = EVP_KDF_HKDF_MODE_EXTRACT_ONLY;
+ } else if (OPENSSL_strcasecmp(p->data, "EXPAND_ONLY") == 0) {
+ ctx->mode = EVP_KDF_HKDF_MODE_EXPAND_ONLY;
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+ } else if (OSSL_PARAM_get_int(p, &n)) {
+ if (n != EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND
+ && n != EVP_KDF_HKDF_MODE_EXTRACT_ONLY
+ && n != EVP_KDF_HKDF_MODE_EXPAND_ONLY) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+ ctx->mode = n;
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) {
+ OPENSSL_clear_free(ctx->key, ctx->key_len);
+ ctx->key = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->key, 0,
+ &ctx->key_len))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) {
+ OPENSSL_free(ctx->salt);
+ ctx->salt = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->salt, 0,
+ &ctx->salt_len))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int kdf_hkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!hkdf_common_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO,
+ &ctx->info, &ctx->info_len,
+ HKDF_MAXINFO) == 0)
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL)
+ if (!fips_hkdf_key_check_passed(ctx))
+ return 0;
+#endif
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_hkdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ HKDF_COMMON_SETTABLES,
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int hkdf_common_get_ctx_params(KDF_HKDF *ctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+ size_t sz = kdf_hkdf_size(ctx);
+
+ if (sz == 0)
+ return 0;
+ if (!OSSL_PARAM_set_size_t(p, sz))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_INFO)) != NULL) {
+ if (ctx->info == NULL || ctx->info_len == 0)
+ p->return_size = 0;
+ else if (!OSSL_PARAM_set_octet_string(p, ctx->info, ctx->info_len))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int kdf_hkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!hkdf_common_get_ctx_params(ctx, params))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_hkdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ HKDF_COMMON_GETTABLES,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_hkdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_hkdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_hkdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_hkdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_hkdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_hkdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+/*
+ * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
+ * Section 2 (https://tools.ietf.org/html/rfc5869#section-2) and
+ * "Cryptographic Extraction and Key Derivation: The HKDF Scheme"
+ * Section 4.2 (https://eprint.iacr.org/2010/264.pdf).
+ *
+ * From the paper:
+ * The scheme HKDF is specified as:
+ * HKDF(XTS, SKM, CTXinfo, L) = K(1) | K(2) | ... | K(t)
+ *
+ * where:
+ * SKM is source key material
+ * XTS is extractor salt (which may be null or constant)
+ * CTXinfo is context information (may be null)
+ * L is the number of key bits to be produced by KDF
+ * k is the output length in bits of the hash function used with HMAC
+ * t = ceil(L/k)
+ * the value K(t) is truncated to its first d = L mod k bits.
+ *
+ * From RFC 5869:
+ * 2.2. Step 1: Extract
+ * HKDF-Extract(salt, IKM) -> PRK
+ * 2.3. Step 2: Expand
+ * HKDF-Expand(PRK, info, L) -> OKM
+ */
+static int HKDF(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *ikm, size_t ikm_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len)
+{
+ unsigned char prk[EVP_MAX_MD_SIZE];
+ int ret, sz;
+ size_t prk_len;
+
+ sz = EVP_MD_get_size(evp_md);
+ if (sz <= 0)
+ return 0;
+ prk_len = (size_t)sz;
+
+ /* Step 1: HKDF-Extract(salt, IKM) -> PRK */
+ if (!HKDF_Extract(libctx, evp_md,
+ salt, salt_len, ikm, ikm_len, prk, prk_len))
+ return 0;
+
+ /* Step 2: HKDF-Expand(PRK, info, L) -> OKM */
+ ret = HKDF_Expand(evp_md, prk, prk_len, info, info_len, okm, okm_len);
+ OPENSSL_cleanse(prk, sizeof(prk));
+
+ return ret;
+}
+
+/*
+ * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
+ * Section 2.2 (https://tools.ietf.org/html/rfc5869#section-2.2).
+ *
+ * 2.2. Step 1: Extract
+ *
+ * HKDF-Extract(salt, IKM) -> PRK
+ *
+ * Options:
+ * Hash a hash function; HashLen denotes the length of the
+ * hash function output in octets
+ *
+ * Inputs:
+ * salt optional salt value (a non-secret random value);
+ * if not provided, it is set to a string of HashLen zeros.
+ * IKM input keying material
+ *
+ * Output:
+ * PRK a pseudorandom key (of HashLen octets)
+ *
+ * The output PRK is calculated as follows:
+ *
+ * PRK = HMAC-Hash(salt, IKM)
+ */
+static int HKDF_Extract(OSSL_LIB_CTX *libctx, const EVP_MD *evp_md,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *ikm, size_t ikm_len,
+ unsigned char *prk, size_t prk_len)
+{
+ int sz = EVP_MD_get_size(evp_md);
+
+ if (sz <= 0)
+ return 0;
+ if (prk_len != (size_t)sz) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE);
+ return 0;
+ }
+ /* calc: PRK = HMAC-Hash(salt, IKM) */
+ return
+ EVP_Q_mac(libctx, "HMAC", NULL, EVP_MD_get0_name(evp_md), NULL, salt,
+ salt_len, ikm, ikm_len, prk, EVP_MD_get_size(evp_md), NULL)
+ != NULL;
+}
+
+/*
+ * Refer to "HMAC-based Extract-and-Expand Key Derivation Function (HKDF)"
+ * Section 2.3 (https://tools.ietf.org/html/rfc5869#section-2.3).
+ *
+ * 2.3. Step 2: Expand
+ *
+ * HKDF-Expand(PRK, info, L) -> OKM
+ *
+ * Options:
+ * Hash a hash function; HashLen denotes the length of the
+ * hash function output in octets
+ *
+ * Inputs:
+ * PRK a pseudorandom key of at least HashLen octets
+ * (usually, the output from the extract step)
+ * info optional context and application specific information
+ * (can be a zero-length string)
+ * L length of output keying material in octets
+ * (<= 255*HashLen)
+ *
+ * Output:
+ * OKM output keying material (of L octets)
+ *
+ * The output OKM is calculated as follows:
+ *
+ * N = ceil(L/HashLen)
+ * T = T(1) | T(2) | T(3) | ... | T(N)
+ * OKM = first L octets of T
+ *
+ * where:
+ * T(0) = empty string (zero length)
+ * T(1) = HMAC-Hash(PRK, T(0) | info | 0x01)
+ * T(2) = HMAC-Hash(PRK, T(1) | info | 0x02)
+ * T(3) = HMAC-Hash(PRK, T(2) | info | 0x03)
+ * ...
+ *
+ * (where the constant concatenated to the end of each T(n) is a
+ * single octet.)
+ */
+static int HKDF_Expand(const EVP_MD *evp_md,
+ const unsigned char *prk, size_t prk_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *okm, size_t okm_len)
+{
+ HMAC_CTX *hmac;
+ int ret = 0, sz;
+ unsigned int i;
+ unsigned char prev[EVP_MAX_MD_SIZE];
+ size_t done_len = 0, dig_len, n;
+
+ sz = EVP_MD_get_size(evp_md);
+ if (sz <= 0)
+ return 0;
+ dig_len = (size_t)sz;
+
+ /* calc: N = ceil(L/HashLen) */
+ n = okm_len / dig_len;
+ if (okm_len % dig_len)
+ n++;
+
+ if (n > 255 || okm == NULL)
+ return 0;
+
+ if ((hmac = HMAC_CTX_new()) == NULL)
+ return 0;
+
+ if (!HMAC_Init_ex(hmac, prk, prk_len, evp_md, NULL))
+ goto err;
+
+ for (i = 1; i <= n; i++) {
+ size_t copy_len;
+ const unsigned char ctr = i;
+
+ /* calc: T(i) = HMAC-Hash(PRK, T(i - 1) | info | i) */
+ if (i > 1) {
+ if (!HMAC_Init_ex(hmac, NULL, 0, NULL, NULL))
+ goto err;
+
+ if (!HMAC_Update(hmac, prev, dig_len))
+ goto err;
+ }
+
+ if (!HMAC_Update(hmac, info, info_len))
+ goto err;
+
+ if (!HMAC_Update(hmac, &ctr, 1))
+ goto err;
+
+ if (!HMAC_Final(hmac, prev, NULL))
+ goto err;
+
+ copy_len = (dig_len > okm_len - done_len) ?
+ okm_len - done_len :
+ dig_len;
+
+ memcpy(okm + done_len, prev, copy_len);
+
+ done_len += copy_len;
+ }
+ ret = 1;
+
+ err:
+ OPENSSL_cleanse(prev, sizeof(prev));
+ HMAC_CTX_free(hmac);
+ return ret;
+}
+
+/*
+ * TLS uses slight variations of the above and for FIPS validation purposes,
+ * they need to be present here.
+ * Refer to RFC 8446 section 7 for specific details.
+ */
+
+/*
+ * Given a |secret|; a |label| of length |labellen|; and |data| of length
+ * |datalen| (e.g. typically a hash of the handshake messages), derive a new
+ * secret |outlen| bytes long and store it in the location pointed to be |out|.
+ * The |data| value may be zero length. Returns 1 on success and 0 on failure.
+ */
+static int prov_tls13_hkdf_expand(const EVP_MD *md,
+ const unsigned char *key, size_t keylen,
+ const unsigned char *prefix, size_t prefixlen,
+ const unsigned char *label, size_t labellen,
+ const unsigned char *data, size_t datalen,
+ unsigned char *out, size_t outlen)
+{
+ size_t hkdflabellen;
+ unsigned char hkdflabel[HKDF_MAXBUF];
+ WPACKET pkt;
+
+ /*
+ * 2 bytes for length of derived secret + 1 byte for length of combined
+ * prefix and label + bytes for the label itself + 1 byte length of hash
+ * + bytes for the hash itself. We've got the maximum the KDF can handle
+ * which should always be sufficient.
+ */
+ if (!WPACKET_init_static_len(&pkt, hkdflabel, sizeof(hkdflabel), 0)
+ || !WPACKET_put_bytes_u16(&pkt, outlen)
+ || !WPACKET_start_sub_packet_u8(&pkt)
+ || !WPACKET_memcpy(&pkt, prefix, prefixlen)
+ || !WPACKET_memcpy(&pkt, label, labellen)
+ || !WPACKET_close(&pkt)
+ || !WPACKET_sub_memcpy_u8(&pkt, data, (data == NULL) ? 0 : datalen)
+ || !WPACKET_get_total_written(&pkt, &hkdflabellen)
+ || !WPACKET_finish(&pkt)) {
+ WPACKET_cleanup(&pkt);
+ return 0;
+ }
+
+ return HKDF_Expand(md, key, keylen, hkdflabel, hkdflabellen,
+ out, outlen);
+}
+
+static int prov_tls13_hkdf_generate_secret(OSSL_LIB_CTX *libctx,
+ const EVP_MD *md,
+ const unsigned char *prevsecret,
+ size_t prevsecretlen,
+ const unsigned char *insecret,
+ size_t insecretlen,
+ const unsigned char *prefix,
+ size_t prefixlen,
+ const unsigned char *label,
+ size_t labellen,
+ unsigned char *out, size_t outlen)
+{
+ size_t mdlen;
+ int ret;
+ unsigned char preextractsec[EVP_MAX_MD_SIZE];
+ /* Always filled with zeros */
+ static const unsigned char default_zeros[EVP_MAX_MD_SIZE];
+
+ ret = EVP_MD_get_size(md);
+ /* Ensure cast to size_t is safe */
+ if (ret <= 0)
+ return 0;
+ mdlen = (size_t)ret;
+
+ if (insecret == NULL) {
+ insecret = default_zeros;
+ insecretlen = mdlen;
+ }
+ if (prevsecret == NULL) {
+ prevsecret = default_zeros;
+ prevsecretlen = mdlen;
+ } else {
+ EVP_MD_CTX *mctx = EVP_MD_CTX_new();
+ unsigned char hash[EVP_MAX_MD_SIZE];
+
+ /* The pre-extract derive step uses a hash of no messages */
+ if (mctx == NULL
+ || EVP_DigestInit_ex(mctx, md, NULL) <= 0
+ || EVP_DigestFinal_ex(mctx, hash, NULL) <= 0) {
+ EVP_MD_CTX_free(mctx);
+ return 0;
+ }
+ EVP_MD_CTX_free(mctx);
+
+ /* Generate the pre-extract secret */
+ if (!prov_tls13_hkdf_expand(md, prevsecret, prevsecretlen,
+ prefix, prefixlen, label, labellen,
+ hash, mdlen, preextractsec, mdlen))
+ return 0;
+ prevsecret = preextractsec;
+ prevsecretlen = mdlen;
+ }
+
+ ret = HKDF_Extract(libctx, md, prevsecret, prevsecretlen,
+ insecret, insecretlen, out, outlen);
+
+ if (prevsecret == preextractsec)
+ OPENSSL_cleanse(preextractsec, mdlen);
+ return ret;
+}
+
+#ifdef FIPS_MODULE
+static int fips_tls1_3_digest_check_passed(KDF_HKDF *ctx, const EVP_MD *md)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ /*
+ * Perform digest check
+ *
+ * According to RFC 8446 appendix B.4, the valid hash functions are
+ * specified in FIPS 180-4. However, it only lists SHA2-256 and SHA2-384 in
+ * the table. ACVP also only lists the same set of hash functions.
+ */
+ int digest_unapproved = !EVP_MD_is_a(md, SN_sha256)
+ && !EVP_MD_is_a(md, SN_sha384);
+
+ if (digest_unapproved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "TLS13 KDF", "Digest",
+ ossl_fips_config_tls13_kdf_digest_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Calculate the correct length of the secret key.
+ *
+ * RFC 8446:
+ * If a given secret is not available, then the 0-value consisting of a
+ * string of Hash.length bytes set to zeros is used.
+ */
+static size_t fips_tls1_3_key_size(KDF_HKDF *ctx)
+{
+ const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+ size_t key_size = 0;
+
+ if (ctx->key != NULL)
+ key_size = ctx->key_len;
+ else if (md != NULL)
+ key_size = EVP_MD_size(md);
+
+ return key_size;
+}
+
+static int fips_tls1_3_key_check_passed(KDF_HKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(fips_tls1_3_key_size(ctx));
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1,
+ libctx, "TLS13 KDF", "Key size",
+ ossl_fips_config_tls13_kdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int kdf_tls1_3_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_tls1_3_set_ctx_params(ctx, params))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+
+ switch (ctx->mode) {
+ default:
+ return 0;
+
+ case EVP_KDF_HKDF_MODE_EXTRACT_ONLY:
+ return prov_tls13_hkdf_generate_secret(PROV_LIBCTX_OF(ctx->provctx),
+ md,
+ ctx->salt, ctx->salt_len,
+ ctx->key, ctx->key_len,
+ ctx->prefix, ctx->prefix_len,
+ ctx->label, ctx->label_len,
+ key, keylen);
+
+ case EVP_KDF_HKDF_MODE_EXPAND_ONLY:
+ return prov_tls13_hkdf_expand(md, ctx->key, ctx->key_len,
+ ctx->prefix, ctx->prefix_len,
+ ctx->label, ctx->label_len,
+ ctx->data, ctx->data_len,
+ key, keylen);
+ }
+}
+
+static int kdf_tls1_3_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_HKDF *ctx = vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!hkdf_common_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->mode == EVP_KDF_HKDF_MODE_EXTRACT_AND_EXPAND) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PREFIX)) != NULL) {
+ OPENSSL_free(ctx->prefix);
+ ctx->prefix = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->prefix, 0,
+ &ctx->prefix_len))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_LABEL)) != NULL) {
+ OPENSSL_free(ctx->label);
+ ctx->label = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->label, 0,
+ &ctx->label_len))
+ return 0;
+ }
+
+ OPENSSL_clear_free(ctx->data, ctx->data_len);
+ ctx->data = NULL;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DATA)) != NULL
+ && !OSSL_PARAM_get_octet_string(p, (void **)&ctx->data, 0,
+ &ctx->data_len))
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+
+ if (!fips_tls1_3_digest_check_passed(ctx, md))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL)
+ if (!fips_tls1_3_key_check_passed(ctx))
+ return 0;
+#endif
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_3_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ HKDF_COMMON_SETTABLES,
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PREFIX, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_LABEL, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_DATA, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_tls1_3_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_HKDF *ctx = (KDF_HKDF *)vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!hkdf_common_get_ctx_params(ctx, params))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_3_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ HKDF_COMMON_GETTABLES,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_tls1_3_kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_hkdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_hkdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_hkdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_hkdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_3_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_3_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_3_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_tls1_3_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c b/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c
new file mode 100644
index 000000000000..9ed214c3da46
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/hmacdrbg_kdf.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/kdf.h>
+#include <openssl/proverr.h>
+#include <openssl/core_names.h>
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/hmac_drbg.h"
+#include "prov/provider_ctx.h"
+
+static OSSL_FUNC_kdf_newctx_fn hmac_drbg_kdf_new;
+static OSSL_FUNC_kdf_dupctx_fn hmac_drbg_kdf_dup;
+static OSSL_FUNC_kdf_freectx_fn hmac_drbg_kdf_free;
+static OSSL_FUNC_kdf_reset_fn hmac_drbg_kdf_reset;
+static OSSL_FUNC_kdf_derive_fn hmac_drbg_kdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn hmac_drbg_kdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn hmac_drbg_kdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn hmac_drbg_kdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn hmac_drbg_kdf_get_ctx_params;
+
+typedef struct {
+ PROV_DRBG_HMAC base;
+ void *provctx;
+ unsigned char *entropy, *nonce;
+ size_t entropylen, noncelen;
+ int init;
+} KDF_HMAC_DRBG;
+
+static void *hmac_drbg_kdf_new(void *provctx)
+{
+ KDF_HMAC_DRBG *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_MALLOC_FAILURE);
+ return NULL;
+ }
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void hmac_drbg_kdf_reset(void *vctx)
+{
+ KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
+ PROV_DRBG_HMAC *drbg = &ctx->base;
+ void *provctx = ctx->provctx;
+
+ EVP_MAC_CTX_free(drbg->ctx);
+ ossl_prov_digest_reset(&drbg->digest);
+ OPENSSL_clear_free(ctx->entropy, ctx->entropylen);
+ OPENSSL_clear_free(ctx->nonce, ctx->noncelen);
+ OPENSSL_cleanse(ctx, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static void hmac_drbg_kdf_free(void *vctx)
+{
+ KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
+
+ if (ctx != NULL) {
+ hmac_drbg_kdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static int ossl_drbg_hmac_dup(PROV_DRBG_HMAC *dst, const PROV_DRBG_HMAC *src) {
+ if (src->ctx != NULL) {
+ dst->ctx = EVP_MAC_CTX_dup(src->ctx);
+ if (dst->ctx == NULL)
+ return 0;
+ }
+ if (!ossl_prov_digest_copy(&dst->digest, &src->digest))
+ return 0;
+ memcpy(dst->K, src->K, sizeof(dst->K));
+ memcpy(dst->V, src->V, sizeof(dst->V));
+ dst->blocklen = src->blocklen;
+ return 1;
+}
+
+static void *hmac_drbg_kdf_dup(void *vctx)
+{
+ const KDF_HMAC_DRBG *src = (const KDF_HMAC_DRBG *)vctx;
+ KDF_HMAC_DRBG *dst;
+
+ dst = hmac_drbg_kdf_new(src->provctx);
+ if (dst != NULL) {
+ if (!ossl_drbg_hmac_dup(&dst->base, &src->base)
+ || !ossl_prov_memdup(src->entropy, src->entropylen,
+ &dst->entropy , &dst->entropylen)
+ || !ossl_prov_memdup(src->nonce, src->noncelen,
+ &dst->nonce, &dst->noncelen))
+ goto err;
+ dst->init = src->init;
+ }
+ return dst;
+
+ err:
+ hmac_drbg_kdf_free(dst);
+ return NULL;
+}
+
+static int hmac_drbg_kdf_derive(void *vctx, unsigned char *out, size_t outlen,
+ const OSSL_PARAM params[])
+{
+ KDF_HMAC_DRBG *ctx = (KDF_HMAC_DRBG *)vctx;
+ PROV_DRBG_HMAC *drbg = &ctx->base;
+
+ if (!ossl_prov_is_running()
+ || !hmac_drbg_kdf_set_ctx_params(vctx, params))
+ return 0;
+ if (!ctx->init) {
+ if (ctx->entropy == NULL
+ || ctx->entropylen == 0
+ || ctx->nonce == NULL
+ || ctx->noncelen == 0
+ || !ossl_drbg_hmac_init(drbg, ctx->entropy, ctx->entropylen,
+ ctx->nonce, ctx->noncelen, NULL, 0))
+ return 0;
+ ctx->init = 1;
+ }
+
+ return ossl_drbg_hmac_generate(drbg, out, outlen, NULL, 0);
+}
+
+static int hmac_drbg_kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx;
+ PROV_DRBG_HMAC *drbg = &hmac->base;
+ const char *name;
+ const EVP_MD *md;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_MAC);
+ if (p != NULL) {
+ if (drbg->ctx == NULL)
+ return 0;
+ name = EVP_MAC_get0_name(EVP_MAC_CTX_get0_mac(drbg->ctx));
+ if (!OSSL_PARAM_set_utf8_string(p, name))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_DIGEST);
+ if (p != NULL) {
+ md = ossl_prov_digest_md(&drbg->digest);
+ if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md)))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *hmac_drbg_kdf_gettable_ctx_params(
+ ossl_unused void *vctx, ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int hmac_drbg_kdf_set_ctx_params(void *vctx,
+ const OSSL_PARAM params[])
+{
+ KDF_HMAC_DRBG *hmac = (KDF_HMAC_DRBG *)vctx;
+ PROV_DRBG_HMAC *drbg = &hmac->base;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(hmac->provctx);
+ const EVP_MD *md;
+ const OSSL_PARAM *p;
+ void *ptr = NULL;
+ size_t size = 0;
+ int md_size;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_HMACDRBG_ENTROPY);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size))
+ return 0;
+ OPENSSL_free(hmac->entropy);
+ hmac->entropy = ptr;
+ hmac->entropylen = size;
+ hmac->init = 0;
+ ptr = NULL;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_HMACDRBG_NONCE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size))
+ return 0;
+ OPENSSL_free(hmac->nonce);
+ hmac->nonce = ptr;
+ hmac->noncelen = size;
+ hmac->init = 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST);
+ if (p != NULL) {
+ if (!ossl_prov_digest_load_from_params(&drbg->digest, params, libctx))
+ return 0;
+
+ /* Confirm digest is allowed. Allow all digests that are not XOF */
+ md = ossl_prov_digest_md(&drbg->digest);
+ if (md != NULL) {
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+ md_size = EVP_MD_get_size(md);
+ if (md_size <= 0)
+ return 0;
+ drbg->blocklen = (size_t)md_size;
+ }
+ return ossl_prov_macctx_load_from_params(&drbg->ctx, params,
+ "HMAC", NULL, NULL, libctx);
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *hmac_drbg_kdf_settable_ctx_params(
+ ossl_unused void *vctx, ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_HMACDRBG_ENTROPY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_HMACDRBG_NONCE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_hmac_drbg_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))hmac_drbg_kdf_new },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))hmac_drbg_kdf_free },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))hmac_drbg_kdf_dup },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))hmac_drbg_kdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))hmac_drbg_kdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))hmac_drbg_kdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS,
+ (void(*)(void))hmac_drbg_kdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))hmac_drbg_kdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS,
+ (void(*)(void))hmac_drbg_kdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/kbkdf.c b/crypto/openssl/providers/implementations/kdfs/kbkdf.c
new file mode 100644
index 000000000000..9ba834ac36d6
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/kbkdf.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2019 Red Hat, Inc.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This implements https://csrc.nist.gov/publications/detail/sp/800-108/final
+ * section 5.1 ("counter mode") and section 5.2 ("feedback mode") in both HMAC
+ * and CMAC. That document does not name the KDFs it defines; the name is
+ * derived from
+ * https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Key-Derivation
+ *
+ * Note that section 5.3 ("double-pipeline mode") is not implemented, though
+ * it would be possible to do so in the future.
+ *
+ * These versions all assume the counter is used. It would be relatively
+ * straightforward to expose a configuration handle should the need arise.
+ *
+ * Variable names attempt to match those of SP800-108.
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+
+#include "internal/cryptlib.h"
+#include "crypto/evp.h"
+#include "internal/numbers.h"
+#include "internal/endian.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+#include "prov/securitycheck.h"
+#include "internal/e_os.h"
+#include "internal/params.h"
+
+#define ossl_min(a, b) ((a) < (b)) ? (a) : (b)
+
+typedef enum {
+ COUNTER = 0,
+ FEEDBACK
+} kbkdf_mode;
+
+/* Our context structure. */
+typedef struct {
+ void *provctx;
+ kbkdf_mode mode;
+ EVP_MAC_CTX *ctx_init;
+
+ /* Names are lowercased versions of those found in SP800-108. */
+ int r;
+ unsigned char *ki;
+ size_t ki_len;
+ unsigned char *label;
+ size_t label_len;
+ unsigned char *context;
+ size_t context_len;
+ unsigned char *iv;
+ size_t iv_len;
+ int use_l;
+ int is_kmac;
+ int use_separator;
+ OSSL_FIPS_IND_DECLARE
+} KBKDF;
+
+/* Definitions needed for typechecking. */
+static OSSL_FUNC_kdf_newctx_fn kbkdf_new;
+static OSSL_FUNC_kdf_dupctx_fn kbkdf_dup;
+static OSSL_FUNC_kdf_freectx_fn kbkdf_free;
+static OSSL_FUNC_kdf_reset_fn kbkdf_reset;
+static OSSL_FUNC_kdf_derive_fn kbkdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kbkdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kbkdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kbkdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kbkdf_get_ctx_params;
+
+/* Not all platforms have htobe32(). */
+static uint32_t be32(uint32_t host)
+{
+ uint32_t big = 0;
+ DECLARE_IS_ENDIAN;
+
+ if (!IS_LITTLE_ENDIAN)
+ return host;
+
+ big |= (host & 0xff000000) >> 24;
+ big |= (host & 0x00ff0000) >> 8;
+ big |= (host & 0x0000ff00) << 8;
+ big |= (host & 0x000000ff) << 24;
+ return big;
+}
+
+static void init(KBKDF *ctx)
+{
+ ctx->r = 32;
+ ctx->use_l = 1;
+ ctx->use_separator = 1;
+ ctx->is_kmac = 0;
+}
+
+static void *kbkdf_new(void *provctx)
+{
+ KBKDF *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ init(ctx);
+ return ctx;
+}
+
+static void kbkdf_free(void *vctx)
+{
+ KBKDF *ctx = (KBKDF *)vctx;
+
+ if (ctx != NULL) {
+ kbkdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kbkdf_reset(void *vctx)
+{
+ KBKDF *ctx = (KBKDF *)vctx;
+ void *provctx = ctx->provctx;
+
+ EVP_MAC_CTX_free(ctx->ctx_init);
+ OPENSSL_clear_free(ctx->context, ctx->context_len);
+ OPENSSL_clear_free(ctx->label, ctx->label_len);
+ OPENSSL_clear_free(ctx->ki, ctx->ki_len);
+ OPENSSL_clear_free(ctx->iv, ctx->iv_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+ init(ctx);
+}
+
+static void *kbkdf_dup(void *vctx)
+{
+ const KBKDF *src = (const KBKDF *)vctx;
+ KBKDF *dest;
+
+ dest = kbkdf_new(src->provctx);
+ if (dest != NULL) {
+ dest->ctx_init = EVP_MAC_CTX_dup(src->ctx_init);
+ if (dest->ctx_init == NULL
+ || !ossl_prov_memdup(src->ki, src->ki_len,
+ &dest->ki, &dest->ki_len)
+ || !ossl_prov_memdup(src->label, src->label_len,
+ &dest->label, &dest->label_len)
+ || !ossl_prov_memdup(src->context, src->context_len,
+ &dest->context, &dest->context_len)
+ || !ossl_prov_memdup(src->iv, src->iv_len,
+ &dest->iv, &dest->iv_len))
+ goto err;
+ dest->mode = src->mode;
+ dest->r = src->r;
+ dest->use_l = src->use_l;
+ dest->use_separator = src->use_separator;
+ dest->is_kmac = src->is_kmac;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ kbkdf_free(dest);
+ return NULL;
+}
+
+#ifdef FIPS_MODULE
+static int fips_kbkdf_key_check_passed(KBKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->ki_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "KBKDF", "Key size",
+ ossl_fips_config_kbkdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+/* SP800-108 section 5.1 or section 5.2 depending on mode. */
+static int derive(EVP_MAC_CTX *ctx_init, kbkdf_mode mode, unsigned char *iv,
+ size_t iv_len, unsigned char *label, size_t label_len,
+ unsigned char *context, size_t context_len,
+ unsigned char *k_i, size_t h, uint32_t l, int has_separator,
+ unsigned char *ko, size_t ko_len, int r)
+{
+ int ret = 0;
+ EVP_MAC_CTX *ctx = NULL;
+ size_t written = 0, to_write, k_i_len = iv_len;
+ const unsigned char zero = 0;
+ uint32_t counter, i;
+ /*
+ * From SP800-108:
+ * The fixed input data is a concatenation of a Label,
+ * a separation indicator 0x00, the Context, and L.
+ * One or more of these fixed input data fields may be omitted.
+ *
+ * has_separator == 0 means that the separator is omitted.
+ * Passing a value of l == 0 means that L is omitted.
+ * The Context and L are omitted automatically if a NULL buffer is passed.
+ */
+ int has_l = (l != 0);
+
+ /* Setup K(0) for feedback mode. */
+ if (iv_len > 0)
+ memcpy(k_i, iv, iv_len);
+
+ for (counter = 1; written < ko_len; counter++) {
+ i = be32(counter);
+
+ ctx = EVP_MAC_CTX_dup(ctx_init);
+ if (ctx == NULL)
+ goto done;
+
+ /* Perform feedback, if appropriate. */
+ if (mode == FEEDBACK && !EVP_MAC_update(ctx, k_i, k_i_len))
+ goto done;
+
+ if (!EVP_MAC_update(ctx, 4 - (r / 8) + (unsigned char *)&i, r / 8)
+ || !EVP_MAC_update(ctx, label, label_len)
+ || (has_separator && !EVP_MAC_update(ctx, &zero, 1))
+ || !EVP_MAC_update(ctx, context, context_len)
+ || (has_l && !EVP_MAC_update(ctx, (unsigned char *)&l, 4))
+ || !EVP_MAC_final(ctx, k_i, NULL, h))
+ goto done;
+
+ to_write = ko_len - written;
+ memcpy(ko + written, k_i, ossl_min(to_write, h));
+ written += h;
+
+ k_i_len = h;
+ EVP_MAC_CTX_free(ctx);
+ ctx = NULL;
+ }
+
+ ret = 1;
+done:
+ EVP_MAC_CTX_free(ctx);
+ return ret;
+}
+
+/* This must be run before the key is set */
+static int kmac_init(EVP_MAC_CTX *ctx, const unsigned char *custom, size_t customlen)
+{
+ OSSL_PARAM params[2];
+
+ if (custom == NULL || customlen == 0)
+ return 1;
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_CUSTOM,
+ (void *)custom, customlen);
+ params[1] = OSSL_PARAM_construct_end();
+ return EVP_MAC_CTX_set_params(ctx, params) > 0;
+}
+
+static int kmac_derive(EVP_MAC_CTX *ctx, unsigned char *out, size_t outlen,
+ const unsigned char *context, size_t contextlen)
+{
+ OSSL_PARAM params[2];
+
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE, &outlen);
+ params[1] = OSSL_PARAM_construct_end();
+ return EVP_MAC_CTX_set_params(ctx, params) > 0
+ && EVP_MAC_update(ctx, context, contextlen)
+ && EVP_MAC_final(ctx, out, NULL, outlen);
+}
+
+static int kbkdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KBKDF *ctx = (KBKDF *)vctx;
+ int ret = 0;
+ unsigned char *k_i = NULL;
+ uint32_t l = 0;
+ size_t h = 0;
+ uint64_t counter_max;
+
+ if (!ossl_prov_is_running() || !kbkdf_set_ctx_params(ctx, params))
+ return 0;
+
+ /* label, context, and iv are permitted to be empty. Check everything
+ * else. */
+ if (ctx->ctx_init == NULL) {
+ if (ctx->ki_len == 0 || ctx->ki == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+ /* Could either be missing MAC or missing message digest or missing
+ * cipher - arbitrarily, I pick this one. */
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MAC);
+ return 0;
+ }
+
+ /* Fail if the output length is zero */
+ if (keylen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ if (ctx->is_kmac) {
+ ret = kmac_derive(ctx->ctx_init, key, keylen,
+ ctx->context, ctx->context_len);
+ goto done;
+ }
+
+ h = EVP_MAC_CTX_get_mac_size(ctx->ctx_init);
+ if (h == 0)
+ goto done;
+
+ if (ctx->iv_len != 0 && ctx->iv_len != h) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ goto done;
+ }
+
+ if (ctx->mode == COUNTER) {
+ /* Fail if keylen is too large for r */
+ counter_max = (uint64_t)1 << (uint64_t)ctx->r;
+ if ((uint64_t)(keylen / h) >= counter_max) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ goto done;
+ }
+ }
+
+ if (ctx->use_l != 0)
+ l = be32(keylen * 8);
+
+ k_i = OPENSSL_zalloc(h);
+ if (k_i == NULL)
+ goto done;
+
+ ret = derive(ctx->ctx_init, ctx->mode, ctx->iv, ctx->iv_len, ctx->label,
+ ctx->label_len, ctx->context, ctx->context_len, k_i, h, l,
+ ctx->use_separator, key, keylen, ctx->r);
+done:
+ if (ret != 1)
+ OPENSSL_cleanse(key, keylen);
+ OPENSSL_clear_free(k_i, h);
+ return ret;
+}
+
+static int kbkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ KBKDF *ctx = (KBKDF *)vctx;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!ossl_prov_macctx_load_from_params(&ctx->ctx_init, params, NULL,
+ NULL, NULL, libctx))
+ return 0;
+ if (ctx->ctx_init != NULL) {
+ ctx->is_kmac = 0;
+ if (EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init),
+ OSSL_MAC_NAME_KMAC128)
+ || EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init),
+ OSSL_MAC_NAME_KMAC256)) {
+ ctx->is_kmac = 1;
+ } else if (!EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init),
+ OSSL_MAC_NAME_HMAC)
+ && !EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->ctx_init),
+ OSSL_MAC_NAME_CMAC)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MAC);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MODE);
+ if (p != NULL
+ && OPENSSL_strncasecmp("counter", p->data, p->data_size) == 0) {
+ ctx->mode = COUNTER;
+ } else if (p != NULL
+ && OPENSSL_strncasecmp("feedback", p->data, p->data_size) == 0) {
+ ctx->mode = FEEDBACK;
+ } else if (p != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY);
+ if (p != NULL) {
+ if (ossl_param_get1_octet_string(p, OSSL_KDF_PARAM_KEY,
+ &ctx->ki, &ctx->ki_len) == 0)
+ return 0;
+#ifdef FIPS_MODULE
+ if (!fips_kbkdf_key_check_passed(ctx))
+ return 0;
+#endif
+ }
+
+ if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SALT,
+ &ctx->label, &ctx->label_len) == 0)
+ return 0;
+
+ if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO,
+ &ctx->context, &ctx->context_len,
+ 0) == 0)
+ return 0;
+
+ if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SEED,
+ &ctx->iv, &ctx->iv_len) == 0)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_USE_L);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_l))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_R);
+ if (p != NULL) {
+ int new_r = 0;
+
+ if (!OSSL_PARAM_get_int(p, &new_r))
+ return 0;
+ if (new_r != 8 && new_r != 16 && new_r != 24 && new_r != 32)
+ return 0;
+ ctx->r = new_r;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_separator))
+ return 0;
+
+ /* Set up digest context, if we can. */
+ if (ctx->ctx_init != NULL && ctx->ki_len != 0) {
+ if ((ctx->is_kmac && !kmac_init(ctx->ctx_init, ctx->label, ctx->label_len))
+ || !EVP_MAC_init(ctx->ctx_init, ctx->ki, ctx->ki_len, NULL))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *kbkdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_USE_L, NULL),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_USE_SEPARATOR, NULL),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_KBKDF_R, NULL),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END,
+ };
+ return known_settable_ctx_params;
+}
+
+static int kbkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+#ifdef FIPS_MODULE
+ KBKDF *ctx = (KBKDF *)vctx;
+#endif
+ OSSL_PARAM *p;
+
+ /* KBKDF can produce results as large as you like. */
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, SIZE_MAX))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kbkdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_kbkdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kbkdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kbkdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kbkdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kbkdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kbkdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kbkdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kbkdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kbkdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kbkdf_get_ctx_params },
+ OSSL_DISPATCH_END,
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/krb5kdf.c b/crypto/openssl/providers/implementations/kdfs/krb5kdf.c
new file mode 100644
index 000000000000..13623ec7302e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/krb5kdf.c
@@ -0,0 +1,497 @@
+/*
+ * Copyright 2018-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DES low level APIs are deprecated for public use, but still ok for internal
+ * use. We access the DES_set_odd_parity(3) function here.
+ */
+#include "internal/deprecated.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <openssl/core_names.h>
+#include <openssl/des.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/proverr.h>
+
+#include "internal/cryptlib.h"
+#include "crypto/evp.h"
+#include "internal/numbers.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+
+/* KRB5 KDF defined in RFC 3961, Section 5.1 */
+
+static OSSL_FUNC_kdf_newctx_fn krb5kdf_new;
+static OSSL_FUNC_kdf_dupctx_fn krb5kdf_dup;
+static OSSL_FUNC_kdf_freectx_fn krb5kdf_free;
+static OSSL_FUNC_kdf_reset_fn krb5kdf_reset;
+static OSSL_FUNC_kdf_derive_fn krb5kdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn krb5kdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn krb5kdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn krb5kdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn krb5kdf_get_ctx_params;
+
+static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *constant, size_t constant_len,
+ unsigned char *okey, size_t okey_len);
+
+typedef struct {
+ void *provctx;
+ PROV_CIPHER cipher;
+ unsigned char *key;
+ size_t key_len;
+ unsigned char *constant;
+ size_t constant_len;
+} KRB5KDF_CTX;
+
+static void *krb5kdf_new(void *provctx)
+{
+ KRB5KDF_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
+ return NULL;
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void krb5kdf_free(void *vctx)
+{
+ KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+
+ if (ctx != NULL) {
+ krb5kdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void krb5kdf_reset(void *vctx)
+{
+ KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+ void *provctx = ctx->provctx;
+
+ ossl_prov_cipher_reset(&ctx->cipher);
+ OPENSSL_clear_free(ctx->key, ctx->key_len);
+ OPENSSL_clear_free(ctx->constant, ctx->constant_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static int krb5kdf_set_membuf(unsigned char **dst, size_t *dst_len,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*dst, *dst_len);
+ *dst = NULL;
+ *dst_len = 0;
+ return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len);
+}
+
+static void *krb5kdf_dup(void *vctx)
+{
+ const KRB5KDF_CTX *src = (const KRB5KDF_CTX *)vctx;
+ KRB5KDF_CTX *dest;
+
+ dest = krb5kdf_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->key, src->key_len,
+ &dest->key, &dest->key_len)
+ || !ossl_prov_memdup(src->constant, src->constant_len,
+ &dest->constant , &dest->constant_len)
+ || !ossl_prov_cipher_copy(&dest->cipher, &src->cipher))
+ goto err;
+ }
+ return dest;
+
+ err:
+ krb5kdf_free(dest);
+ return NULL;
+}
+
+static int krb5kdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+ const EVP_CIPHER *cipher;
+ ENGINE *engine;
+
+ if (!ossl_prov_is_running() || !krb5kdf_set_ctx_params(ctx, params))
+ return 0;
+
+ cipher = ossl_prov_cipher_cipher(&ctx->cipher);
+ if (cipher == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER);
+ return 0;
+ }
+ if (ctx->key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ if (ctx->constant == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CONSTANT);
+ return 0;
+ }
+ engine = ossl_prov_cipher_engine(&ctx->cipher);
+ return KRB5KDF(cipher, engine, ctx->key, ctx->key_len,
+ ctx->constant, ctx->constant_len,
+ key, keylen);
+}
+
+static int krb5kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KRB5KDF_CTX *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_prov_cipher_load_from_params(&ctx->cipher, params, provctx))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL)
+ if (!krb5kdf_set_membuf(&ctx->key, &ctx->key_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CONSTANT))
+ != NULL)
+ if (!krb5kdf_set_membuf(&ctx->constant, &ctx->constant_len, p))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *krb5kdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_CONSTANT, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int krb5kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KRB5KDF_CTX *ctx = (KRB5KDF_CTX *)vctx;
+ const EVP_CIPHER *cipher;
+ size_t len;
+ OSSL_PARAM *p;
+
+ cipher = ossl_prov_cipher_cipher(&ctx->cipher);
+ if (cipher)
+ len = EVP_CIPHER_get_key_length(cipher);
+ else
+ len = EVP_MAX_KEY_LENGTH;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, len);
+ return -2;
+}
+
+static const OSSL_PARAM *krb5kdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_krb5kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))krb5kdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))krb5kdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))krb5kdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))krb5kdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))krb5kdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))krb5kdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS,
+ (void(*)(void))krb5kdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))krb5kdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS,
+ (void(*)(void))krb5kdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+#ifndef OPENSSL_NO_DES
+/*
+ * DES3 is a special case, it requires a random-to-key function and its
+ * input truncated to 21 bytes of the 24 produced by the cipher.
+ * See RFC3961 6.3.1
+ */
+static int fixup_des3_key(unsigned char *key)
+{
+ unsigned char *cblock;
+ int i, j;
+
+ for (i = 2; i >= 0; i--) {
+ cblock = &key[i * 8];
+ memmove(cblock, &key[i * 7], 7);
+ cblock[7] = 0;
+ for (j = 0; j < 7; j++)
+ cblock[7] |= (cblock[j] & 1) << (j + 1);
+ DES_set_odd_parity((DES_cblock *)cblock);
+ }
+
+ /* fail if keys are such that triple des degrades to single des */
+ if (CRYPTO_memcmp(&key[0], &key[8], 8) == 0 ||
+ CRYPTO_memcmp(&key[8], &key[16], 8) == 0) {
+ return 0;
+ }
+
+ return 1;
+}
+#endif
+
+/*
+ * N-fold(K) where blocksize is N, and constant_len is K
+ * Note: Here |= denotes concatenation
+ *
+ * L = lcm(N,K)
+ * R = L/K
+ *
+ * for r: 1 -> R
+ * s |= constant rot 13*(r-1))
+ *
+ * block = 0
+ * for k: 1 -> K
+ * block += s[N(k-1)..(N-1)k] (ones'-complement addition)
+ *
+ * Optimizing for space we compute:
+ * for each l in L-1 -> 0:
+ * s[l] = (constant rot 13*(l/K))[l%k]
+ * block[l % N] += s[l] (with carry)
+ * finally add carry if any
+ */
+static void n_fold(unsigned char *block, unsigned int blocksize,
+ const unsigned char *constant, size_t constant_len)
+{
+ unsigned int tmp, gcd, remainder, lcm, carry;
+ int b, l;
+
+ if (constant_len == blocksize) {
+ memcpy(block, constant, constant_len);
+ return;
+ }
+
+ /* Least Common Multiple of lengths: LCM(a,b)*/
+ gcd = blocksize;
+ remainder = constant_len;
+ /* Calculate Great Common Divisor first GCD(a,b) */
+ while (remainder != 0) {
+ tmp = gcd % remainder;
+ gcd = remainder;
+ remainder = tmp;
+ }
+ /* resulting a is the GCD, LCM(a,b) = |a*b|/GCD(a,b) */
+ lcm = blocksize * constant_len / gcd;
+
+ /* now spread out the bits */
+ memset(block, 0, blocksize);
+
+ /* last to first to be able to bring carry forward */
+ carry = 0;
+ for (l = lcm - 1; l >= 0; l--) {
+ unsigned int rotbits, rshift, rbyte;
+
+ /* destination byte in block is l % N */
+ b = l % blocksize;
+ /* Our virtual s buffer is R = L/K long (K = constant_len) */
+ /* So we rotate backwards from R-1 to 0 (none) rotations */
+ rotbits = 13 * (l / constant_len);
+ /* find the byte on s where rotbits falls onto */
+ rbyte = l - (rotbits / 8);
+ /* calculate how much shift on that byte */
+ rshift = rotbits & 0x07;
+ /* rbyte % constant_len gives us the unrotated byte in the
+ * constant buffer, get also the previous byte then
+ * appropriately shift them to get the rotated byte we need */
+ tmp = (constant[(rbyte-1) % constant_len] << (8 - rshift)
+ | constant[rbyte % constant_len] >> rshift)
+ & 0xff;
+ /* add with carry to any value placed by previous passes */
+ tmp += carry + block[b];
+ block[b] = tmp & 0xff;
+ /* save any carry that may be left */
+ carry = tmp >> 8;
+ }
+
+ /* if any carry is left at the end, add it through the number */
+ for (b = blocksize - 1; b >= 0 && carry != 0; b--) {
+ carry += block[b];
+ block[b] = carry & 0xff;
+ carry >>= 8;
+ }
+}
+
+static int cipher_init(EVP_CIPHER_CTX *ctx,
+ const EVP_CIPHER *cipher, ENGINE *engine,
+ const unsigned char *key, size_t key_len)
+{
+ int klen, ret;
+
+ ret = EVP_EncryptInit_ex(ctx, cipher, engine, NULL, NULL);
+ if (!ret)
+ goto out;
+ /* set the key len for the odd variable key len cipher */
+ klen = EVP_CIPHER_CTX_get_key_length(ctx);
+ if (key_len != (size_t)klen) {
+ ret = EVP_CIPHER_CTX_set_key_length(ctx, key_len);
+ if (ret <= 0) {
+ ret = 0;
+ goto out;
+ }
+ }
+ ret = EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL);
+ if (!ret)
+ goto out;
+ /* we never want padding, either the length requested is a multiple of
+ * the cipher block size or we are passed a cipher that can cope with
+ * partial blocks via techniques like cipher text stealing */
+ ret = EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (!ret)
+ goto out;
+
+out:
+ return ret;
+}
+
+static int KRB5KDF(const EVP_CIPHER *cipher, ENGINE *engine,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *constant, size_t constant_len,
+ unsigned char *okey, size_t okey_len)
+{
+ EVP_CIPHER_CTX *ctx = NULL;
+ unsigned char block[EVP_MAX_BLOCK_LENGTH * 2];
+ unsigned char *plainblock, *cipherblock;
+ size_t blocksize;
+ size_t cipherlen;
+ size_t osize;
+#ifndef OPENSSL_NO_DES
+ int des3_no_fixup = 0;
+#endif
+ int ret;
+
+ if (key_len != okey_len) {
+#ifndef OPENSSL_NO_DES
+ /* special case for 3des, where the caller may be requesting
+ * the random raw key, instead of the fixed up key */
+ if (EVP_CIPHER_get_nid(cipher) == NID_des_ede3_cbc &&
+ key_len == 24 && okey_len == 21) {
+ des3_no_fixup = 1;
+ } else {
+#endif
+ ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_OUTPUT_BUFFER_SIZE);
+ return 0;
+#ifndef OPENSSL_NO_DES
+ }
+#endif
+ }
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL)
+ return 0;
+
+ ret = cipher_init(ctx, cipher, engine, key, key_len);
+ if (!ret)
+ goto out;
+
+ /* Initialize input block */
+ blocksize = EVP_CIPHER_CTX_get_block_size(ctx);
+
+ if (blocksize == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER);
+ ret = 0;
+ goto out;
+ }
+
+ if (constant_len > blocksize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CONSTANT_LENGTH);
+ ret = 0;
+ goto out;
+ }
+
+ n_fold(block, blocksize, constant, constant_len);
+ plainblock = block;
+ cipherblock = block + EVP_MAX_BLOCK_LENGTH;
+
+ for (osize = 0; osize < okey_len; osize += cipherlen) {
+ int olen;
+
+ ret = EVP_EncryptUpdate(ctx, cipherblock, &olen,
+ plainblock, blocksize);
+ if (!ret)
+ goto out;
+ cipherlen = olen;
+ ret = EVP_EncryptFinal_ex(ctx, cipherblock, &olen);
+ if (!ret)
+ goto out;
+ if (olen != 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_WRONG_FINAL_BLOCK_LENGTH);
+ ret = 0;
+ goto out;
+ }
+
+ /* write cipherblock out */
+ if (cipherlen > okey_len - osize)
+ cipherlen = okey_len - osize;
+ memcpy(okey + osize, cipherblock, cipherlen);
+
+ if (okey_len > osize + cipherlen) {
+ /* we need to reinitialize cipher context per spec */
+ ret = EVP_CIPHER_CTX_reset(ctx);
+ if (!ret)
+ goto out;
+ ret = cipher_init(ctx, cipher, engine, key, key_len);
+ if (!ret)
+ goto out;
+
+ /* also swap block offsets so last ciphertext becomes new
+ * plaintext */
+ plainblock = cipherblock;
+ if (cipherblock == block) {
+ cipherblock += EVP_MAX_BLOCK_LENGTH;
+ } else {
+ cipherblock = block;
+ }
+ }
+ }
+
+#ifndef OPENSSL_NO_DES
+ if (EVP_CIPHER_get_nid(cipher) == NID_des_ede3_cbc && !des3_no_fixup) {
+ ret = fixup_des3_key(okey);
+ if (!ret) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ goto out;
+ }
+ }
+#endif
+
+ ret = 1;
+
+out:
+ EVP_CIPHER_CTX_free(ctx);
+ OPENSSL_cleanse(block, EVP_MAX_BLOCK_LENGTH * 2);
+ return ret;
+}
+
diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf1.c b/crypto/openssl/providers/implementations/kdfs/pbkdf1.c
new file mode 100644
index 000000000000..1b7e4d8a2eab
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pbkdf1.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright 1999-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/trace.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+
+static OSSL_FUNC_kdf_newctx_fn kdf_pbkdf1_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_pbkdf1_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_pbkdf1_free;
+static OSSL_FUNC_kdf_reset_fn kdf_pbkdf1_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_pbkdf1_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pbkdf1_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf1_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf1_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf1_get_ctx_params;
+
+typedef struct {
+ void *provctx;
+ PROV_DIGEST digest;
+ unsigned char *pass;
+ size_t pass_len;
+ unsigned char *salt;
+ size_t salt_len;
+ uint64_t iter;
+} KDF_PBKDF1;
+
+/*
+ * PKCS5 PBKDF1 compatible key/IV generation as specified in:
+ * https://tools.ietf.org/html/rfc8018#page-10
+ */
+
+static int kdf_pbkdf1_do_derive(const unsigned char *pass, size_t passlen,
+ const unsigned char *salt, size_t saltlen,
+ uint64_t iter, const EVP_MD *md_type,
+ unsigned char *out, size_t n)
+{
+ uint64_t i;
+ int mdsize, ret = 0;
+ unsigned char md_tmp[EVP_MAX_MD_SIZE];
+ EVP_MD_CTX *ctx = NULL;
+
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB);
+ goto err;
+ }
+
+ if (!EVP_DigestInit_ex(ctx, md_type, NULL)
+ || !EVP_DigestUpdate(ctx, pass, passlen)
+ || !EVP_DigestUpdate(ctx, salt, saltlen)
+ || !EVP_DigestFinal_ex(ctx, md_tmp, NULL))
+ goto err;
+ mdsize = EVP_MD_size(md_type);
+ if (mdsize <= 0)
+ goto err;
+ if (n > (size_t)mdsize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE);
+ goto err;
+ }
+
+ for (i = 1; i < iter; i++) {
+ if (!EVP_DigestInit_ex(ctx, md_type, NULL))
+ goto err;
+ if (!EVP_DigestUpdate(ctx, md_tmp, mdsize))
+ goto err;
+ if (!EVP_DigestFinal_ex(ctx, md_tmp, NULL))
+ goto err;
+ }
+
+ memcpy(out, md_tmp, n);
+ ret = 1;
+err:
+ OPENSSL_cleanse(md_tmp, EVP_MAX_MD_SIZE);
+ EVP_MD_CTX_free(ctx);
+ return ret;
+}
+
+static void *kdf_pbkdf1_new(void *provctx)
+{
+ KDF_PBKDF1 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void kdf_pbkdf1_cleanup(KDF_PBKDF1 *ctx)
+{
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_free(ctx->salt);
+ OPENSSL_clear_free(ctx->pass, ctx->pass_len);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static void kdf_pbkdf1_free(void *vctx)
+{
+ KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx;
+
+ if (ctx != NULL) {
+ kdf_pbkdf1_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_pbkdf1_reset(void *vctx)
+{
+ KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx;
+ void *provctx = ctx->provctx;
+
+ kdf_pbkdf1_cleanup(ctx);
+ ctx->provctx = provctx;
+}
+
+static void *kdf_pbkdf1_dup(void *vctx)
+{
+ const KDF_PBKDF1 *src = (const KDF_PBKDF1 *)vctx;
+ KDF_PBKDF1 *dest;
+
+ dest = kdf_pbkdf1_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt, &dest->salt_len)
+ || !ossl_prov_memdup(src->pass, src->pass_len,
+ &dest->pass , &dest->pass_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->iter = src->iter;
+ }
+ return dest;
+
+ err:
+ kdf_pbkdf1_free(dest);
+ return NULL;
+}
+
+static int kdf_pbkdf1_set_membuf(unsigned char **buffer, size_t *buflen,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*buffer, *buflen);
+ *buffer = NULL;
+ *buflen = 0;
+
+ if (p->data_size == 0) {
+ if ((*buffer = OPENSSL_malloc(1)) == NULL)
+ return 0;
+ } else if (p->data != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
+ return 0;
+ }
+ return 1;
+}
+
+static int kdf_pbkdf1_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_PBKDF1 *ctx = (KDF_PBKDF1 *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_pbkdf1_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->pass == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
+ return 0;
+ }
+
+ if (ctx->salt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ return kdf_pbkdf1_do_derive(ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len,
+ ctx->iter, md, key, keylen);
+}
+
+static int kdf_pbkdf1_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_PBKDF1 *ctx = vctx;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!kdf_pbkdf1_set_membuf(&ctx->pass, &ctx->pass_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
+ if (!kdf_pbkdf1_set_membuf(&ctx->salt, &ctx->salt_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL)
+ if (!OSSL_PARAM_get_uint64(p, &ctx->iter))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_pbkdf1_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_pbkdf1_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+ return -2;
+}
+
+static const OSSL_PARAM *kdf_pbkdf1_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_pbkdf1_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pbkdf1_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pbkdf1_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pbkdf1_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pbkdf1_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pbkdf1_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pbkdf1_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pbkdf1_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pbkdf1_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2.c b/crypto/openssl/providers/implementations/kdfs/pbkdf2.c
new file mode 100644
index 000000000000..b38331406412
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright 2018-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * HMAC low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+#include "pbkdf2.h"
+
+/* Constants specified in SP800-132 */
+#define KDF_PBKDF2_MIN_KEY_LEN_BITS 112
+#define KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO 0xFFFFFFFF
+#define KDF_PBKDF2_MIN_ITERATIONS 1000
+#define KDF_PBKDF2_MIN_SALT_LEN (128 / 8)
+
+static OSSL_FUNC_kdf_newctx_fn kdf_pbkdf2_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_pbkdf2_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_pbkdf2_free;
+static OSSL_FUNC_kdf_reset_fn kdf_pbkdf2_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_pbkdf2_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pbkdf2_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pbkdf2_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pbkdf2_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pbkdf2_get_ctx_params;
+
+typedef struct {
+ void *provctx;
+ unsigned char *pass;
+ size_t pass_len;
+ unsigned char *salt;
+ size_t salt_len;
+ uint64_t iter;
+ PROV_DIGEST digest;
+ int lower_bound_checks;
+ OSSL_FIPS_IND_DECLARE
+} KDF_PBKDF2;
+
+static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen,
+ const unsigned char *salt, int saltlen, uint64_t iter,
+ const EVP_MD *digest, unsigned char *key,
+ size_t keylen, int lower_bound_checks);
+
+static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx);
+
+static void *kdf_pbkdf2_new_no_init(void *provctx)
+{
+ KDF_PBKDF2 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx);
+ return ctx;
+}
+
+static void *kdf_pbkdf2_new(void *provctx)
+{
+ KDF_PBKDF2 *ctx = kdf_pbkdf2_new_no_init(provctx);
+
+ if (ctx != NULL)
+ kdf_pbkdf2_init(ctx);
+ return ctx;
+}
+
+static void kdf_pbkdf2_cleanup(KDF_PBKDF2 *ctx)
+{
+ ossl_prov_digest_reset(&ctx->digest);
+#ifdef OPENSSL_PEDANTIC_ZEROIZATION
+ OPENSSL_clear_free(ctx->salt, ctx->salt_len);
+#else
+ OPENSSL_free(ctx->salt);
+#endif
+ OPENSSL_clear_free(ctx->pass, ctx->pass_len);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static void kdf_pbkdf2_free(void *vctx)
+{
+ KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx;
+
+ if (ctx != NULL) {
+ kdf_pbkdf2_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_pbkdf2_reset(void *vctx)
+{
+ KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx;
+ void *provctx = ctx->provctx;
+
+ kdf_pbkdf2_cleanup(ctx);
+ ctx->provctx = provctx;
+ kdf_pbkdf2_init(ctx);
+}
+
+static void *kdf_pbkdf2_dup(void *vctx)
+{
+ const KDF_PBKDF2 *src = (const KDF_PBKDF2 *)vctx;
+ KDF_PBKDF2 *dest;
+
+ /* We need a new PBKDF2 object but uninitialised since we're filling it */
+ dest = kdf_pbkdf2_new_no_init(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt, &dest->salt_len)
+ || !ossl_prov_memdup(src->pass, src->pass_len,
+ &dest->pass, &dest->pass_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->iter = src->iter;
+ dest->lower_bound_checks = src->lower_bound_checks;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ kdf_pbkdf2_free(dest);
+ return NULL;
+}
+
+static void kdf_pbkdf2_init(KDF_PBKDF2 *ctx)
+{
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ SN_sha1, 0);
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ /* This is an error, but there is no way to indicate such directly */
+ ossl_prov_digest_reset(&ctx->digest);
+ ctx->iter = PKCS5_DEFAULT_ITER;
+ ctx->lower_bound_checks = ossl_kdf_pbkdf2_default_checks;
+}
+
+static int pbkdf2_set_membuf(unsigned char **buffer, size_t *buflen,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*buffer, *buflen);
+ *buffer = NULL;
+ *buflen = 0;
+
+ if (p->data_size == 0) {
+ if ((*buffer = OPENSSL_malloc(1)) == NULL)
+ return 0;
+ } else if (p->data != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
+ return 0;
+ }
+ return 1;
+}
+
+static int pbkdf2_lower_bound_check_passed(int saltlen, uint64_t iter,
+ size_t keylen, int *error,
+ const char **desc)
+{
+ if ((keylen * 8) < KDF_PBKDF2_MIN_KEY_LEN_BITS) {
+ *error = PROV_R_KEY_SIZE_TOO_SMALL;
+ if (desc != NULL)
+ *desc = "Key size";
+ return 0;
+ }
+ if (saltlen < KDF_PBKDF2_MIN_SALT_LEN) {
+ *error = PROV_R_INVALID_SALT_LENGTH;
+ if (desc != NULL)
+ *desc = "Salt size";
+ return 0;
+ }
+ if (iter < KDF_PBKDF2_MIN_ITERATIONS) {
+ *error = PROV_R_INVALID_ITERATION_COUNT;
+ if (desc != NULL)
+ *desc = "Iteration count";
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef FIPS_MODULE
+static int fips_lower_bound_check_passed(KDF_PBKDF2 *ctx, size_t keylen)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int error = 0;
+ const char *desc = NULL;
+ int approved = pbkdf2_lower_bound_check_passed(ctx->salt_len, ctx->iter,
+ keylen, &error, &desc);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0, libctx,
+ "PBKDF2", desc,
+ ossl_fips_config_pbkdf2_lower_bound_check)) {
+ ERR_raise(ERR_LIB_PROV, error);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int kdf_pbkdf2_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_PBKDF2 *ctx = (KDF_PBKDF2 *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_pbkdf2_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->pass == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
+ return 0;
+ }
+
+ if (ctx->salt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ return pbkdf2_derive(ctx, (char *)ctx->pass, ctx->pass_len,
+ ctx->salt, ctx->salt_len, ctx->iter,
+ md, key, keylen, ctx->lower_bound_checks);
+}
+
+static int kdf_pbkdf2_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_PBKDF2 *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+ int pkcs5;
+ uint64_t iter, min_iter;
+ const EVP_MD *md;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ return 0;
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PKCS5)) != NULL) {
+ if (!OSSL_PARAM_get_int(p, &pkcs5))
+ return 0;
+ ctx->lower_bound_checks = pkcs5 == 0;
+#ifdef FIPS_MODULE
+ ossl_FIPS_IND_set_settable(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE0,
+ ctx->lower_bound_checks);
+#endif
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!pbkdf2_set_membuf(&ctx->pass, &ctx->pass_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) {
+ if (ctx->lower_bound_checks != 0
+ && p->data_size < KDF_PBKDF2_MIN_SALT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+ return 0;
+ }
+ if (!pbkdf2_set_membuf(&ctx->salt, &ctx->salt_len, p))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL) {
+ if (!OSSL_PARAM_get_uint64(p, &iter))
+ return 0;
+ min_iter = ctx->lower_bound_checks != 0 ? KDF_PBKDF2_MIN_ITERATIONS : 1;
+ if (iter < min_iter) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_ITERATION_COUNT);
+ return 0;
+ }
+ ctx->iter = iter;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_pbkdf2_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_PKCS5, NULL),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_pbkdf2_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM((KDF_PBKDF2 *) vctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_pbkdf2_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_pbkdf2_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pbkdf2_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pbkdf2_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pbkdf2_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pbkdf2_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pbkdf2_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pbkdf2_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pbkdf2_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pbkdf2_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pbkdf2_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+/*
+ * This is an implementation of PKCS#5 v2.0 password based encryption key
+ * derivation function PBKDF2. SHA1 version verified against test vectors
+ * posted by Peter Gutmann to the PKCS-TNG mailing list.
+ *
+ * The constraints specified by SP800-132 have been added i.e.
+ * - Check the range of the key length.
+ * - Minimum iteration count of 1000.
+ * - Randomly-generated portion of the salt shall be at least 128 bits.
+ */
+static int pbkdf2_derive(KDF_PBKDF2 *ctx, const char *pass, size_t passlen,
+ const unsigned char *salt, int saltlen, uint64_t iter,
+ const EVP_MD *digest, unsigned char *key,
+ size_t keylen, int lower_bound_checks)
+{
+ int ret = 0;
+ unsigned char digtmp[EVP_MAX_MD_SIZE], *p, itmp[4];
+ int cplen, k, tkeylen, mdlen;
+ uint64_t j;
+ unsigned long i = 1;
+ HMAC_CTX *hctx_tpl = NULL, *hctx = NULL;
+
+ mdlen = EVP_MD_get_size(digest);
+ if (mdlen <= 0)
+ return 0;
+
+ /*
+ * This check should always be done because keylen / mdlen >= (2^32 - 1)
+ * results in an overflow of the loop counter 'i'.
+ */
+ if ((keylen / mdlen) >= KDF_PBKDF2_MAX_KEY_LEN_DIGEST_RATIO) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+#ifdef FIPS_MODULE
+ if (!fips_lower_bound_check_passed(ctx, keylen))
+ return 0;
+#else
+ if (lower_bound_checks) {
+ int error = 0;
+ int passed = pbkdf2_lower_bound_check_passed(saltlen, iter, keylen,
+ &error, NULL);
+
+ if (!passed) {
+ ERR_raise(ERR_LIB_PROV, error);
+ return 0;
+ }
+ }
+#endif
+
+ hctx_tpl = HMAC_CTX_new();
+ if (hctx_tpl == NULL)
+ return 0;
+ p = key;
+ tkeylen = keylen;
+ if (!HMAC_Init_ex(hctx_tpl, pass, passlen, digest, NULL))
+ goto err;
+ hctx = HMAC_CTX_new();
+ if (hctx == NULL)
+ goto err;
+ while (tkeylen) {
+ if (tkeylen > mdlen)
+ cplen = mdlen;
+ else
+ cplen = tkeylen;
+ /*
+ * We are unlikely to ever use more than 256 blocks (5120 bits!) but
+ * just in case...
+ */
+ itmp[0] = (unsigned char)((i >> 24) & 0xff);
+ itmp[1] = (unsigned char)((i >> 16) & 0xff);
+ itmp[2] = (unsigned char)((i >> 8) & 0xff);
+ itmp[3] = (unsigned char)(i & 0xff);
+ if (!HMAC_CTX_copy(hctx, hctx_tpl))
+ goto err;
+ if (!HMAC_Update(hctx, salt, saltlen)
+ || !HMAC_Update(hctx, itmp, 4)
+ || !HMAC_Final(hctx, digtmp, NULL))
+ goto err;
+ memcpy(p, digtmp, cplen);
+ for (j = 1; j < iter; j++) {
+ if (!HMAC_CTX_copy(hctx, hctx_tpl))
+ goto err;
+ if (!HMAC_Update(hctx, digtmp, mdlen)
+ || !HMAC_Final(hctx, digtmp, NULL))
+ goto err;
+ for (k = 0; k < cplen; k++)
+ p[k] ^= digtmp[k];
+ }
+ tkeylen -= cplen;
+ i++;
+ p += cplen;
+ }
+ ret = 1;
+
+err:
+ HMAC_CTX_free(hctx);
+ HMAC_CTX_free(hctx_tpl);
+ return ret;
+}
diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2.h b/crypto/openssl/providers/implementations/kdfs/pbkdf2.h
new file mode 100644
index 000000000000..7759c03136d5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Available in pbkdfe_fips.c, and compiled with different values depending
+ * on we're in the FIPS module or not.
+ */
+extern const int ossl_kdf_pbkdf2_default_checks;
diff --git a/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c b/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c
new file mode 100644
index 000000000000..e43ef16455f1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pbkdf2_fips.c
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2019-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "pbkdf2.h"
+
+/*
+ * For backwards compatibility reasons,
+ * Extra checks are done by default in fips mode only.
+ */
+#ifdef FIPS_MODULE
+const int ossl_kdf_pbkdf2_default_checks = 1;
+#else
+const int ossl_kdf_pbkdf2_default_checks = 0;
+#endif /* FIPS_MODULE */
diff --git a/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c b/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c
new file mode 100644
index 000000000000..b4ca4fff475d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pkcs12kdf.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright 1999-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/trace.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+
+static OSSL_FUNC_kdf_newctx_fn kdf_pkcs12_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_pkcs12_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_pkcs12_free;
+static OSSL_FUNC_kdf_reset_fn kdf_pkcs12_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_pkcs12_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pkcs12_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pkcs12_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pkcs12_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pkcs12_get_ctx_params;
+
+typedef struct {
+ void *provctx;
+ PROV_DIGEST digest;
+ unsigned char *pass;
+ size_t pass_len;
+ unsigned char *salt;
+ size_t salt_len;
+ uint64_t iter;
+ int id;
+} KDF_PKCS12;
+
+/* PKCS12 compatible key/IV generation */
+
+static int pkcs12kdf_derive(const unsigned char *pass, size_t passlen,
+ const unsigned char *salt, size_t saltlen,
+ int id, uint64_t iter, const EVP_MD *md_type,
+ unsigned char *out, size_t n)
+{
+ unsigned char *B = NULL, *D = NULL, *I = NULL, *p = NULL, *Ai = NULL;
+ size_t Slen, Plen, Ilen;
+ size_t i, j, k, u, v;
+ uint64_t iter_cnt;
+ int ret = 0, ui, vi;
+ EVP_MD_CTX *ctx = NULL;
+
+ ctx = EVP_MD_CTX_new();
+ if (ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB);
+ goto end;
+ }
+ vi = EVP_MD_get_block_size(md_type);
+ ui = EVP_MD_get_size(md_type);
+ if (ui <= 0 || vi <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_SIZE);
+ goto end;
+ }
+ u = (size_t)ui;
+ v = (size_t)vi;
+ D = OPENSSL_malloc(v);
+ Ai = OPENSSL_malloc(u);
+ B = OPENSSL_malloc(v + 1);
+ Slen = v * ((saltlen + v - 1) / v);
+ if (passlen != 0)
+ Plen = v * ((passlen + v - 1) / v);
+ else
+ Plen = 0;
+ Ilen = Slen + Plen;
+ I = OPENSSL_malloc(Ilen);
+ if (D == NULL || Ai == NULL || B == NULL || I == NULL)
+ goto end;
+ for (i = 0; i < v; i++)
+ D[i] = id;
+ p = I;
+ for (i = 0; i < Slen; i++)
+ *p++ = salt[i % saltlen];
+ for (i = 0; i < Plen; i++)
+ *p++ = pass[i % passlen];
+ for (;;) {
+ if (!EVP_DigestInit_ex(ctx, md_type, NULL)
+ || !EVP_DigestUpdate(ctx, D, v)
+ || !EVP_DigestUpdate(ctx, I, Ilen)
+ || !EVP_DigestFinal_ex(ctx, Ai, NULL))
+ goto end;
+ for (iter_cnt = 1; iter_cnt < iter; iter_cnt++) {
+ if (!EVP_DigestInit_ex(ctx, md_type, NULL)
+ || !EVP_DigestUpdate(ctx, Ai, u)
+ || !EVP_DigestFinal_ex(ctx, Ai, NULL))
+ goto end;
+ }
+ memcpy(out, Ai, n < u ? n : u);
+ if (u >= n) {
+ ret = 1;
+ break;
+ }
+ n -= u;
+ out += u;
+ for (j = 0; j < v; j++)
+ B[j] = Ai[j % u];
+ for (j = 0; j < Ilen; j += v) {
+ unsigned char *Ij = I + j;
+ uint16_t c = 1;
+
+ /* Work out Ij = Ij + B + 1 */
+ for (k = v; k > 0;) {
+ k--;
+ c += Ij[k] + B[k];
+ Ij[k] = (unsigned char)c;
+ c >>= 8;
+ }
+ }
+ }
+
+ end:
+ OPENSSL_free(Ai);
+ OPENSSL_free(B);
+ OPENSSL_free(D);
+ OPENSSL_free(I);
+ EVP_MD_CTX_free(ctx);
+ return ret;
+}
+
+static void *kdf_pkcs12_new(void *provctx)
+{
+ KDF_PKCS12 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void kdf_pkcs12_cleanup(KDF_PKCS12 *ctx)
+{
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_free(ctx->salt);
+ OPENSSL_clear_free(ctx->pass, ctx->pass_len);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static void kdf_pkcs12_free(void *vctx)
+{
+ KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
+
+ if (ctx != NULL) {
+ kdf_pkcs12_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_pkcs12_reset(void *vctx)
+{
+ KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
+ void *provctx = ctx->provctx;
+
+ kdf_pkcs12_cleanup(ctx);
+ ctx->provctx = provctx;
+}
+
+static void *kdf_pkcs12_dup(void *vctx)
+{
+ const KDF_PKCS12 *src = (const KDF_PKCS12 *)vctx;
+ KDF_PKCS12 *dest;
+
+ dest = kdf_pkcs12_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt, &dest->salt_len)
+ || !ossl_prov_memdup(src->pass, src->pass_len,
+ &dest->pass , &dest->pass_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->iter = src->iter;
+ dest->id = src->id;
+ }
+ return dest;
+
+ err:
+ kdf_pkcs12_free(dest);
+ return NULL;
+}
+
+static int pkcs12kdf_set_membuf(unsigned char **buffer, size_t *buflen,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*buffer, *buflen);
+ *buffer = NULL;
+ *buflen = 0;
+
+ if (p->data_size == 0) {
+ if ((*buffer = OPENSSL_malloc(1)) == NULL)
+ return 0;
+ } else if (p->data != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
+ return 0;
+ }
+ return 1;
+}
+
+static int kdf_pkcs12_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_PKCS12 *ctx = (KDF_PKCS12 *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_pkcs12_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->pass == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
+ return 0;
+ }
+
+ if (ctx->salt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ return pkcs12kdf_derive(ctx->pass, ctx->pass_len, ctx->salt, ctx->salt_len,
+ ctx->id, ctx->iter, md, key, keylen);
+}
+
+static int kdf_pkcs12_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_PKCS12 *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!pkcs12kdf_set_membuf(&ctx->pass, &ctx->pass_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
+ if (!pkcs12kdf_set_membuf(&ctx->salt, &ctx->salt_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PKCS12_ID)) != NULL)
+ if (!OSSL_PARAM_get_int(p, &ctx->id))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_ITER)) != NULL)
+ if (!OSSL_PARAM_get_uint64(p, &ctx->iter))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_pkcs12_settable_ctx_params(
+ ossl_unused void *ctx, ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_uint64(OSSL_KDF_PARAM_ITER, NULL),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_PKCS12_ID, NULL),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_pkcs12_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+ return -2;
+}
+
+static const OSSL_PARAM *kdf_pkcs12_gettable_ctx_params(
+ ossl_unused void *ctx, ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_pkcs12_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pkcs12_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pkcs12_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pkcs12_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pkcs12_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pkcs12_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pkcs12_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pkcs12_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pkcs12_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/pvkkdf.c b/crypto/openssl/providers/implementations/kdfs/pvkkdf.c
new file mode 100644
index 000000000000..f60092d77173
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/pvkkdf.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include <openssl/err.h>
+#include "internal/numbers.h" /* SIZE_MAX */
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+
+static OSSL_FUNC_kdf_newctx_fn kdf_pvk_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_pvk_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_pvk_free;
+static OSSL_FUNC_kdf_reset_fn kdf_pvk_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_pvk_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_pvk_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_pvk_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_pvk_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_pvk_get_ctx_params;
+
+typedef struct {
+ void *provctx;
+ unsigned char *pass;
+ size_t pass_len;
+ unsigned char *salt;
+ size_t salt_len;
+ PROV_DIGEST digest;
+} KDF_PVK;
+
+static void kdf_pvk_init(KDF_PVK *ctx);
+
+static void *kdf_pvk_new(void *provctx)
+{
+ KDF_PVK *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->provctx = provctx;
+ kdf_pvk_init(ctx);
+ return ctx;
+}
+
+static void kdf_pvk_cleanup(KDF_PVK *ctx)
+{
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_free(ctx->salt);
+ OPENSSL_clear_free(ctx->pass, ctx->pass_len);
+ OPENSSL_cleanse(ctx, sizeof(*ctx));
+}
+
+static void kdf_pvk_free(void *vctx)
+{
+ KDF_PVK *ctx = (KDF_PVK *)vctx;
+
+ if (ctx != NULL) {
+ kdf_pvk_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void *kdf_pvk_dup(void *vctx)
+{
+ const KDF_PVK *src = (const KDF_PVK *)vctx;
+ KDF_PVK *dest;
+
+ dest = kdf_pvk_new(src->provctx);
+ if (dest != NULL)
+ if (!ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt, &dest->salt_len)
+ || !ossl_prov_memdup(src->pass, src->pass_len,
+ &dest->pass , &dest->pass_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ return dest;
+
+ err:
+ kdf_pvk_free(dest);
+ return NULL;
+}
+
+static void kdf_pvk_reset(void *vctx)
+{
+ KDF_PVK *ctx = (KDF_PVK *)vctx;
+ void *provctx = ctx->provctx;
+
+ kdf_pvk_cleanup(ctx);
+ ctx->provctx = provctx;
+ kdf_pvk_init(ctx);
+}
+
+static void kdf_pvk_init(KDF_PVK *ctx)
+{
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ SN_sha1, 0);
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ /* This is an error, but there is no way to indicate such directly */
+ ossl_prov_digest_reset(&ctx->digest);
+}
+
+static int pvk_set_membuf(unsigned char **buffer, size_t *buflen,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*buffer, *buflen);
+ *buffer = NULL;
+ *buflen = 0;
+
+ if (p->data_size == 0) {
+ if ((*buffer = OPENSSL_malloc(1)) == NULL)
+ return 0;
+ } else if (p->data != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
+ return 0;
+ }
+ return 1;
+}
+
+static int kdf_pvk_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_PVK *ctx = (KDF_PVK *)vctx;
+ const EVP_MD *md;
+ EVP_MD_CTX *mctx;
+ int res;
+
+ if (!ossl_prov_is_running() || !kdf_pvk_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->pass == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
+ return 0;
+ }
+
+ if (ctx->salt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
+ return 0;
+ }
+ res = EVP_MD_get_size(md);
+ if (res <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
+ return 0;
+ }
+ if ((size_t)res > keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE);
+ return 0;
+ }
+
+ mctx = EVP_MD_CTX_new();
+ res = mctx != NULL
+ && EVP_DigestInit_ex(mctx, md, NULL)
+ && EVP_DigestUpdate(mctx, ctx->salt, ctx->salt_len)
+ && EVP_DigestUpdate(mctx, ctx->pass, ctx->pass_len)
+ && EVP_DigestFinal_ex(mctx, key, NULL);
+ EVP_MD_CTX_free(mctx);
+ return res;
+}
+
+static int kdf_pvk_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_PVK *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!pvk_set_membuf(&ctx->pass, &ctx->pass_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL) {
+ if (!pvk_set_membuf(&ctx->salt, &ctx->salt_len, p))
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_pvk_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_pvk_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+ return -2;
+}
+
+static const OSSL_PARAM *kdf_pvk_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_pvk_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_pvk_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_pvk_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_pvk_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_pvk_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_pvk_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pvk_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_pvk_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_pvk_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_pvk_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/scrypt.c b/crypto/openssl/providers/implementations/kdfs/scrypt.c
new file mode 100644
index 000000000000..e27b09eb99a4
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/scrypt.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright 2017-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/err.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "crypto/evp.h"
+#include "internal/numbers.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/provider_util.h"
+
+#ifndef OPENSSL_NO_SCRYPT
+
+static OSSL_FUNC_kdf_newctx_fn kdf_scrypt_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_scrypt_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_scrypt_free;
+static OSSL_FUNC_kdf_reset_fn kdf_scrypt_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_scrypt_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_scrypt_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_scrypt_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_scrypt_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_scrypt_get_ctx_params;
+
+static int scrypt_alg(const char *pass, size_t passlen,
+ const unsigned char *salt, size_t saltlen,
+ uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem,
+ unsigned char *key, size_t keylen, EVP_MD *sha256,
+ OSSL_LIB_CTX *libctx, const char *propq);
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ unsigned char *pass;
+ size_t pass_len;
+ unsigned char *salt;
+ size_t salt_len;
+ uint64_t N;
+ uint64_t r, p;
+ uint64_t maxmem_bytes;
+ EVP_MD *sha256;
+} KDF_SCRYPT;
+
+static void kdf_scrypt_init(KDF_SCRYPT *ctx);
+
+static void *kdf_scrypt_new_inner(OSSL_LIB_CTX *libctx)
+{
+ KDF_SCRYPT *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+ ctx->libctx = libctx;
+ kdf_scrypt_init(ctx);
+ return ctx;
+}
+
+static void *kdf_scrypt_new(void *provctx)
+{
+ return kdf_scrypt_new_inner(PROV_LIBCTX_OF(provctx));
+}
+
+static void kdf_scrypt_free(void *vctx)
+{
+ KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx;
+
+ if (ctx != NULL) {
+ OPENSSL_free(ctx->propq);
+ EVP_MD_free(ctx->sha256);
+ kdf_scrypt_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_scrypt_reset(void *vctx)
+{
+ KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx;
+
+ OPENSSL_free(ctx->salt);
+ ctx->salt = NULL;
+ OPENSSL_clear_free(ctx->pass, ctx->pass_len);
+ ctx->pass = NULL;
+ kdf_scrypt_init(ctx);
+}
+
+static void *kdf_scrypt_dup(void *vctx)
+{
+ const KDF_SCRYPT *src = (const KDF_SCRYPT *)vctx;
+ KDF_SCRYPT *dest;
+
+ dest = kdf_scrypt_new_inner(src->libctx);
+ if (dest != NULL) {
+ if (src->sha256 != NULL && !EVP_MD_up_ref(src->sha256))
+ goto err;
+ if (src->propq != NULL) {
+ dest->propq = OPENSSL_strdup(src->propq);
+ if (dest->propq == NULL)
+ goto err;
+ }
+ if (!ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt, &dest->salt_len)
+ || !ossl_prov_memdup(src->pass, src->pass_len,
+ &dest->pass , &dest->pass_len))
+ goto err;
+ dest->N = src->N;
+ dest->r = src->r;
+ dest->p = src->p;
+ dest->maxmem_bytes = src->maxmem_bytes;
+ dest->sha256 = src->sha256;
+ }
+ return dest;
+
+ err:
+ kdf_scrypt_free(dest);
+ return NULL;
+}
+
+static void kdf_scrypt_init(KDF_SCRYPT *ctx)
+{
+ /* Default values are the most conservative recommendation given in the
+ * original paper of C. Percival. Derivation uses roughly 1 GiB of memory
+ * for this parameter choice (approx. 128 * r * N * p bytes).
+ */
+ ctx->N = 1 << 20;
+ ctx->r = 8;
+ ctx->p = 1;
+ ctx->maxmem_bytes = 1025 * 1024 * 1024;
+}
+
+static int scrypt_set_membuf(unsigned char **buffer, size_t *buflen,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*buffer, *buflen);
+ *buffer = NULL;
+ *buflen = 0;
+
+ if (p->data_size == 0) {
+ if ((*buffer = OPENSSL_malloc(1)) == NULL)
+ return 0;
+ } else if (p->data != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, (void **)buffer, 0, buflen))
+ return 0;
+ }
+ return 1;
+}
+
+static int set_digest(KDF_SCRYPT *ctx)
+{
+ EVP_MD_free(ctx->sha256);
+ ctx->sha256 = EVP_MD_fetch(ctx->libctx, "sha256", ctx->propq);
+ if (ctx->sha256 == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOAD_SHA256);
+ return 0;
+ }
+ return 1;
+}
+
+static int set_property_query(KDF_SCRYPT *ctx, const char *propq)
+{
+ OPENSSL_free(ctx->propq);
+ ctx->propq = NULL;
+ if (propq != NULL) {
+ ctx->propq = OPENSSL_strdup(propq);
+ if (ctx->propq == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static int kdf_scrypt_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_SCRYPT *ctx = (KDF_SCRYPT *)vctx;
+
+ if (!ossl_prov_is_running() || !kdf_scrypt_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->pass == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_PASS);
+ return 0;
+ }
+
+ if (ctx->salt == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SALT);
+ return 0;
+ }
+
+ if (ctx->sha256 == NULL && !set_digest(ctx))
+ return 0;
+
+ return scrypt_alg((char *)ctx->pass, ctx->pass_len, ctx->salt,
+ ctx->salt_len, ctx->N, ctx->r, ctx->p,
+ ctx->maxmem_bytes, key, keylen, ctx->sha256,
+ ctx->libctx, ctx->propq);
+}
+
+static int is_power_of_two(uint64_t value)
+{
+ return (value != 0) && ((value & (value - 1)) == 0);
+}
+
+static int kdf_scrypt_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_SCRYPT *ctx = vctx;
+ uint64_t u64_value;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PASSWORD)) != NULL)
+ if (!scrypt_set_membuf(&ctx->pass, &ctx->pass_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SALT)) != NULL)
+ if (!scrypt_set_membuf(&ctx->salt, &ctx->salt_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_N))
+ != NULL) {
+ if (!OSSL_PARAM_get_uint64(p, &u64_value)
+ || u64_value <= 1
+ || !is_power_of_two(u64_value))
+ return 0;
+ ctx->N = u64_value;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_R))
+ != NULL) {
+ if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1)
+ return 0;
+ ctx->r = u64_value;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_P))
+ != NULL) {
+ if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1)
+ return 0;
+ ctx->p = u64_value;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SCRYPT_MAXMEM))
+ != NULL) {
+ if (!OSSL_PARAM_get_uint64(p, &u64_value) || u64_value < 1)
+ return 0;
+ ctx->maxmem_bytes = u64_value;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || !set_property_query(ctx, p->data)
+ || !set_digest(ctx))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_scrypt_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_PASSWORD, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_uint64(OSSL_KDF_PARAM_SCRYPT_N, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_R, NULL),
+ OSSL_PARAM_uint32(OSSL_KDF_PARAM_SCRYPT_P, NULL),
+ OSSL_PARAM_uint64(OSSL_KDF_PARAM_SCRYPT_MAXMEM, NULL),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_scrypt_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, SIZE_MAX);
+ return -2;
+}
+
+static const OSSL_PARAM *kdf_scrypt_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_scrypt_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_scrypt_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_scrypt_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_scrypt_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_scrypt_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_scrypt_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_scrypt_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_scrypt_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_scrypt_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_scrypt_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b))))
+static void salsa208_word_specification(uint32_t inout[16])
+{
+ int i;
+ uint32_t x[16];
+
+ memcpy(x, inout, sizeof(x));
+ for (i = 8; i > 0; i -= 2) {
+ x[4] ^= R(x[0] + x[12], 7);
+ x[8] ^= R(x[4] + x[0], 9);
+ x[12] ^= R(x[8] + x[4], 13);
+ x[0] ^= R(x[12] + x[8], 18);
+ x[9] ^= R(x[5] + x[1], 7);
+ x[13] ^= R(x[9] + x[5], 9);
+ x[1] ^= R(x[13] + x[9], 13);
+ x[5] ^= R(x[1] + x[13], 18);
+ x[14] ^= R(x[10] + x[6], 7);
+ x[2] ^= R(x[14] + x[10], 9);
+ x[6] ^= R(x[2] + x[14], 13);
+ x[10] ^= R(x[6] + x[2], 18);
+ x[3] ^= R(x[15] + x[11], 7);
+ x[7] ^= R(x[3] + x[15], 9);
+ x[11] ^= R(x[7] + x[3], 13);
+ x[15] ^= R(x[11] + x[7], 18);
+ x[1] ^= R(x[0] + x[3], 7);
+ x[2] ^= R(x[1] + x[0], 9);
+ x[3] ^= R(x[2] + x[1], 13);
+ x[0] ^= R(x[3] + x[2], 18);
+ x[6] ^= R(x[5] + x[4], 7);
+ x[7] ^= R(x[6] + x[5], 9);
+ x[4] ^= R(x[7] + x[6], 13);
+ x[5] ^= R(x[4] + x[7], 18);
+ x[11] ^= R(x[10] + x[9], 7);
+ x[8] ^= R(x[11] + x[10], 9);
+ x[9] ^= R(x[8] + x[11], 13);
+ x[10] ^= R(x[9] + x[8], 18);
+ x[12] ^= R(x[15] + x[14], 7);
+ x[13] ^= R(x[12] + x[15], 9);
+ x[14] ^= R(x[13] + x[12], 13);
+ x[15] ^= R(x[14] + x[13], 18);
+ }
+ for (i = 0; i < 16; ++i)
+ inout[i] += x[i];
+ OPENSSL_cleanse(x, sizeof(x));
+}
+
+static void scryptBlockMix(uint32_t *B_, uint32_t *B, uint64_t r)
+{
+ uint64_t i, j;
+ uint32_t X[16], *pB;
+
+ memcpy(X, B + (r * 2 - 1) * 16, sizeof(X));
+ pB = B;
+ for (i = 0; i < r * 2; i++) {
+ for (j = 0; j < 16; j++)
+ X[j] ^= *pB++;
+ salsa208_word_specification(X);
+ memcpy(B_ + (i / 2 + (i & 1) * r) * 16, X, sizeof(X));
+ }
+ OPENSSL_cleanse(X, sizeof(X));
+}
+
+static void scryptROMix(unsigned char *B, uint64_t r, uint64_t N,
+ uint32_t *X, uint32_t *T, uint32_t *V)
+{
+ unsigned char *pB;
+ uint32_t *pV;
+ uint64_t i, k;
+
+ /* Convert from little endian input */
+ for (pV = V, i = 0, pB = B; i < 32 * r; i++, pV++) {
+ *pV = *pB++;
+ *pV |= *pB++ << 8;
+ *pV |= *pB++ << 16;
+ *pV |= (uint32_t)*pB++ << 24;
+ }
+
+ for (i = 1; i < N; i++, pV += 32 * r)
+ scryptBlockMix(pV, pV - 32 * r, r);
+
+ scryptBlockMix(X, V + (N - 1) * 32 * r, r);
+
+ for (i = 0; i < N; i++) {
+ uint32_t j;
+ j = X[16 * (2 * r - 1)] % N;
+ pV = V + 32 * r * j;
+ for (k = 0; k < 32 * r; k++)
+ T[k] = X[k] ^ *pV++;
+ scryptBlockMix(X, T, r);
+ }
+ /* Convert output to little endian */
+ for (i = 0, pB = B; i < 32 * r; i++) {
+ uint32_t xtmp = X[i];
+ *pB++ = xtmp & 0xff;
+ *pB++ = (xtmp >> 8) & 0xff;
+ *pB++ = (xtmp >> 16) & 0xff;
+ *pB++ = (xtmp >> 24) & 0xff;
+ }
+}
+
+#ifndef SIZE_MAX
+# define SIZE_MAX ((size_t)-1)
+#endif
+
+/*
+ * Maximum power of two that will fit in uint64_t: this should work on
+ * most (all?) platforms.
+ */
+
+#define LOG2_UINT64_MAX (sizeof(uint64_t) * 8 - 1)
+
+/*
+ * Maximum value of p * r:
+ * p <= ((2^32-1) * hLen) / MFLen =>
+ * p <= ((2^32-1) * 32) / (128 * r) =>
+ * p * r <= (2^30-1)
+ */
+
+#define SCRYPT_PR_MAX ((1 << 30) - 1)
+
+static int scrypt_alg(const char *pass, size_t passlen,
+ const unsigned char *salt, size_t saltlen,
+ uint64_t N, uint64_t r, uint64_t p, uint64_t maxmem,
+ unsigned char *key, size_t keylen, EVP_MD *sha256,
+ OSSL_LIB_CTX *libctx, const char *propq)
+{
+ int rv = 0;
+ unsigned char *B;
+ uint32_t *X, *V, *T;
+ uint64_t i, Blen, Vlen;
+
+ /* Sanity check parameters */
+ /* initial check, r,p must be non zero, N >= 2 and a power of 2 */
+ if (r == 0 || p == 0 || N < 2 || (N & (N - 1)))
+ return 0;
+ /* Check p * r < SCRYPT_PR_MAX avoiding overflow */
+ if (p > SCRYPT_PR_MAX / r) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+
+ /*
+ * Need to check N: if 2^(128 * r / 8) overflows limit this is
+ * automatically satisfied since N <= UINT64_MAX.
+ */
+
+ if (16 * r <= LOG2_UINT64_MAX) {
+ if (N >= (((uint64_t)1) << (16 * r))) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+ }
+
+ /* Memory checks: check total allocated buffer size fits in uint64_t */
+
+ /*
+ * B size in section 5 step 1.S
+ * Note: we know p * 128 * r < UINT64_MAX because we already checked
+ * p * r < SCRYPT_PR_MAX
+ */
+ Blen = p * 128 * r;
+ /*
+ * Yet we pass it as integer to PKCS5_PBKDF2_HMAC... [This would
+ * have to be revised when/if PKCS5_PBKDF2_HMAC accepts size_t.]
+ */
+ if (Blen > INT_MAX) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+
+ /*
+ * Check 32 * r * (N + 2) * sizeof(uint32_t) fits in uint64_t
+ * This is combined size V, X and T (section 4)
+ */
+ i = UINT64_MAX / (32 * sizeof(uint32_t));
+ if (N + 2 > i / r) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+ Vlen = 32 * r * (N + 2) * sizeof(uint32_t);
+
+ /* check total allocated size fits in uint64_t */
+ if (Blen > UINT64_MAX - Vlen) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+
+ /* Check that the maximum memory doesn't exceed a size_t limits */
+ if (maxmem > SIZE_MAX)
+ maxmem = SIZE_MAX;
+
+ if (Blen + Vlen > maxmem) {
+ ERR_raise(ERR_LIB_EVP, EVP_R_MEMORY_LIMIT_EXCEEDED);
+ return 0;
+ }
+
+ /* If no key return to indicate parameters are OK */
+ if (key == NULL)
+ return 1;
+
+ B = OPENSSL_malloc((size_t)(Blen + Vlen));
+ if (B == NULL)
+ return 0;
+ X = (uint32_t *)(B + Blen);
+ T = X + 32 * r;
+ V = T + 32 * r;
+ if (ossl_pkcs5_pbkdf2_hmac_ex(pass, passlen, salt, saltlen, 1, sha256,
+ (int)Blen, B, libctx, propq) == 0)
+ goto err;
+
+ for (i = 0; i < p; i++)
+ scryptROMix(B + 128 * r * i, r, N, X, T, V);
+
+ if (ossl_pkcs5_pbkdf2_hmac_ex(pass, passlen, B, (int)Blen, 1, sha256,
+ keylen, key, libctx, propq) == 0)
+ goto err;
+ rv = 1;
+ err:
+ if (rv == 0)
+ ERR_raise(ERR_LIB_EVP, EVP_R_PBKDF2_ERROR);
+
+ OPENSSL_clear_free(B, (size_t)(Blen + Vlen));
+ return rv;
+}
+
+#endif
diff --git a/crypto/openssl/providers/implementations/kdfs/sshkdf.c b/crypto/openssl/providers/implementations/kdfs/sshkdf.c
new file mode 100644
index 000000000000..4a9b141320bf
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/sshkdf.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright 2018-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+
+/* See RFC 4253, Section 7.2 */
+static OSSL_FUNC_kdf_newctx_fn kdf_sshkdf_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_sshkdf_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_sshkdf_free;
+static OSSL_FUNC_kdf_reset_fn kdf_sshkdf_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_sshkdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_sshkdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_sshkdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_sshkdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_sshkdf_get_ctx_params;
+
+static int SSHKDF(const EVP_MD *evp_md,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *xcghash, size_t xcghash_len,
+ const unsigned char *session_id, size_t session_id_len,
+ char type, unsigned char *okey, size_t okey_len);
+
+typedef struct {
+ void *provctx;
+ PROV_DIGEST digest;
+ unsigned char *key; /* K */
+ size_t key_len;
+ unsigned char *xcghash; /* H */
+ size_t xcghash_len;
+ char type; /* X */
+ unsigned char *session_id;
+ size_t session_id_len;
+ OSSL_FIPS_IND_DECLARE
+} KDF_SSHKDF;
+
+static void *kdf_sshkdf_new(void *provctx)
+{
+ KDF_SSHKDF *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) {
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ }
+ return ctx;
+}
+
+static void kdf_sshkdf_free(void *vctx)
+{
+ KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
+
+ if (ctx != NULL) {
+ kdf_sshkdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_sshkdf_reset(void *vctx)
+{
+ KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
+ void *provctx = ctx->provctx;
+
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_clear_free(ctx->key, ctx->key_len);
+ OPENSSL_clear_free(ctx->xcghash, ctx->xcghash_len);
+ OPENSSL_clear_free(ctx->session_id, ctx->session_id_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static void *kdf_sshkdf_dup(void *vctx)
+{
+ const KDF_SSHKDF *src = (const KDF_SSHKDF *)vctx;
+ KDF_SSHKDF *dest;
+
+ dest = kdf_sshkdf_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->key, src->key_len,
+ &dest->key, &dest->key_len)
+ || !ossl_prov_memdup(src->xcghash, src->xcghash_len,
+ &dest->xcghash , &dest->xcghash_len)
+ || !ossl_prov_memdup(src->session_id, src->session_id_len,
+ &dest->session_id , &dest->session_id_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->type = src->type;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ kdf_sshkdf_free(dest);
+ return NULL;
+}
+
+static int sshkdf_set_membuf(unsigned char **dst, size_t *dst_len,
+ const OSSL_PARAM *p)
+{
+ OPENSSL_clear_free(*dst, *dst_len);
+ *dst = NULL;
+ *dst_len = 0;
+ return OSSL_PARAM_get_octet_string(p, (void **)dst, 0, dst_len);
+}
+
+#ifdef FIPS_MODULE
+static int fips_digest_check_passed(KDF_SSHKDF *ctx, const EVP_MD *md)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ /*
+ * Perform digest check
+ *
+ * According to NIST SP 800-135r1 section 5.2, the valid hash functions are
+ * specified in FIPS 180-3. ACVP also only lists the same set of hash
+ * functions.
+ */
+ int digest_unapproved = !EVP_MD_is_a(md, SN_sha1)
+ && !EVP_MD_is_a(md, SN_sha224)
+ && !EVP_MD_is_a(md, SN_sha256)
+ && !EVP_MD_is_a(md, SN_sha384)
+ && !EVP_MD_is_a(md, SN_sha512);
+
+ if (digest_unapproved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "SSHKDF", "Digest",
+ ossl_fips_config_sshkdf_digest_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int fips_key_check_passed(KDF_SSHKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->key_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1,
+ libctx, "SSHKDF", "Key size",
+ ossl_fips_config_sshkdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int kdf_sshkdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_SSHKDF *ctx = (KDF_SSHKDF *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !kdf_sshkdf_set_ctx_params(ctx, params))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ if (ctx->key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ if (ctx->xcghash == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_XCGHASH);
+ return 0;
+ }
+ if (ctx->session_id == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SESSION_ID);
+ return 0;
+ }
+ if (ctx->type == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_TYPE);
+ return 0;
+ }
+
+ return SSHKDF(md, ctx->key, ctx->key_len,
+ ctx->xcghash, ctx->xcghash_len,
+ ctx->session_id, ctx->session_id_len,
+ ctx->type, key, keylen);
+}
+
+static int kdf_sshkdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ KDF_SSHKDF *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ const EVP_MD *md = NULL;
+
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+
+#ifdef FIPS_MODULE
+ if (!fips_digest_check_passed(ctx, md))
+ return 0;
+#endif
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY)) != NULL) {
+ if (!sshkdf_set_membuf(&ctx->key, &ctx->key_len, p))
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (!fips_key_check_passed(ctx))
+ return 0;
+#endif
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_XCGHASH))
+ != NULL)
+ if (!sshkdf_set_membuf(&ctx->xcghash, &ctx->xcghash_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_SESSION_ID))
+ != NULL)
+ if (!sshkdf_set_membuf(&ctx->session_id, &ctx->session_id_len, p))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SSHKDF_TYPE))
+ != NULL) {
+ const char *kdftype;
+
+ if (!OSSL_PARAM_get_utf8_string_ptr(p, &kdftype))
+ return 0;
+ /* Expect one character (byte in this case) */
+ if (kdftype == NULL || p->data_size != 1)
+ return 0;
+ if (kdftype[0] < 65 || kdftype[0] > 70) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_VALUE_ERROR);
+ return 0;
+ }
+ ctx->type = kdftype[0];
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_sshkdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_XCGHASH, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SSHKDF_SESSION_ID, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_SSHKDF_TYPE, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_sshkdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+ return 0;
+ }
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(((KDF_SSHKDF *)vctx), params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_sshkdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_sshkdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_sshkdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_sshkdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_sshkdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_sshkdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_sshkdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_sshkdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_sshkdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))kdf_sshkdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+static int SSHKDF(const EVP_MD *evp_md,
+ const unsigned char *key, size_t key_len,
+ const unsigned char *xcghash, size_t xcghash_len,
+ const unsigned char *session_id, size_t session_id_len,
+ char type, unsigned char *okey, size_t okey_len)
+{
+ EVP_MD_CTX *md = NULL;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dsize = 0;
+ size_t cursize = 0;
+ int ret = 0;
+
+ md = EVP_MD_CTX_new();
+ if (md == NULL)
+ return 0;
+
+ if (!EVP_DigestInit_ex(md, evp_md, NULL))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, key, key_len))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, &type, 1))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, session_id, session_id_len))
+ goto out;
+
+ if (!EVP_DigestFinal_ex(md, digest, &dsize))
+ goto out;
+
+ if (okey_len < dsize) {
+ memcpy(okey, digest, okey_len);
+ ret = 1;
+ goto out;
+ }
+
+ memcpy(okey, digest, dsize);
+
+ for (cursize = dsize; cursize < okey_len; cursize += dsize) {
+
+ if (!EVP_DigestInit_ex(md, evp_md, NULL))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, key, key_len))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, xcghash, xcghash_len))
+ goto out;
+
+ if (!EVP_DigestUpdate(md, okey, cursize))
+ goto out;
+
+ if (!EVP_DigestFinal_ex(md, digest, &dsize))
+ goto out;
+
+ if (okey_len < cursize + dsize) {
+ memcpy(okey + cursize, digest, okey_len - cursize);
+ ret = 1;
+ goto out;
+ }
+
+ memcpy(okey + cursize, digest, dsize);
+ }
+
+ ret = 1;
+
+out:
+ EVP_MD_CTX_free(md);
+ OPENSSL_cleanse(digest, EVP_MAX_MD_SIZE);
+ return ret;
+}
diff --git a/crypto/openssl/providers/implementations/kdfs/sskdf.c b/crypto/openssl/providers/implementations/kdfs/sskdf.c
new file mode 100644
index 000000000000..3c377db0058e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/sskdf.c
@@ -0,0 +1,786 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
+ * Section 4.1.
+ *
+ * The Single Step KDF algorithm is given by:
+ *
+ * Result(0) = empty bit string (i.e., the null string).
+ * For i = 1 to reps, do the following:
+ * Increment counter by 1.
+ * Result(i) = Result(i - 1) || H(counter || Z || FixedInfo).
+ * DKM = LeftmostBits(Result(reps), L))
+ *
+ * NOTES:
+ * Z is a shared secret required to produce the derived key material.
+ * counter is a 4 byte buffer.
+ * FixedInfo is a bit string containing context specific data.
+ * DKM is the output derived key material.
+ * L is the required size of the DKM.
+ * reps = [L / H_outputBits]
+ * H(x) is the auxiliary function that can be either a hash, HMAC or KMAC.
+ * H_outputBits is the length of the output of the auxiliary function H(x).
+ *
+ * Currently there is not a comprehensive list of test vectors for this
+ * algorithm, especially for H(x) = HMAC and H(x) = KMAC.
+ * Test vectors for H(x) = Hash are indirectly used by CAVS KAS tests.
+ */
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/hmac.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+#include "internal/params.h"
+
+typedef struct {
+ void *provctx;
+ EVP_MAC_CTX *macctx; /* H(x) = HMAC_hash OR H(x) = KMAC */
+ PROV_DIGEST digest; /* H(x) = hash(x) */
+ unsigned char *secret;
+ size_t secret_len;
+ unsigned char *info;
+ size_t info_len;
+ unsigned char *salt;
+ size_t salt_len;
+ size_t out_len; /* optional KMAC parameter */
+ int is_kmac;
+ OSSL_FIPS_IND_DECLARE
+} KDF_SSKDF;
+
+#define SSKDF_MAX_INLEN (1<<30)
+#define SSKDF_KMAC128_DEFAULT_SALT_SIZE (168 - 4)
+#define SSKDF_KMAC256_DEFAULT_SALT_SIZE (136 - 4)
+
+/* KMAC uses a Customisation string of 'KDF' */
+static const unsigned char kmac_custom_str[] = { 0x4B, 0x44, 0x46 };
+
+static OSSL_FUNC_kdf_newctx_fn sskdf_new;
+static OSSL_FUNC_kdf_dupctx_fn sskdf_dup;
+static OSSL_FUNC_kdf_freectx_fn sskdf_free;
+static OSSL_FUNC_kdf_reset_fn sskdf_reset;
+static OSSL_FUNC_kdf_derive_fn sskdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn sskdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn sskdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn sskdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn sskdf_get_ctx_params;
+static OSSL_FUNC_kdf_derive_fn x963kdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn x963kdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn x963kdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn x963kdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn x963kdf_get_ctx_params;
+
+/* Settable context parameters that are common across SSKDF and X963 KDF */
+#define SSKDF_COMMON_SETTABLES \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_INFO, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_MAC, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SALT, NULL, 0), \
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_MAC_SIZE, NULL)
+
+/* Gettable context parameters that are common across SSKDF and X963 KDF */
+#define SSKDF_COMMON_GETTABLES \
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL)
+
+/*
+ * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
+ * Section 4. One-Step Key Derivation using H(x) = hash(x)
+ * Note: X9.63 also uses this code with the only difference being that the
+ * counter is appended to the secret 'z'.
+ * i.e.
+ * result[i] = Hash(counter || z || info) for One Step OR
+ * result[i] = Hash(z || counter || info) for X9.63.
+ */
+static int SSKDF_hash_kdm(const EVP_MD *kdf_md,
+ const unsigned char *z, size_t z_len,
+ const unsigned char *info, size_t info_len,
+ unsigned int append_ctr,
+ unsigned char *derived_key, size_t derived_key_len)
+{
+ int ret = 0, hlen;
+ size_t counter, out_len, len = derived_key_len;
+ unsigned char c[4];
+ unsigned char mac[EVP_MAX_MD_SIZE];
+ unsigned char *out = derived_key;
+ EVP_MD_CTX *ctx = NULL, *ctx_init = NULL;
+
+ if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN
+ || derived_key_len > SSKDF_MAX_INLEN
+ || derived_key_len == 0)
+ return 0;
+
+ hlen = EVP_MD_get_size(kdf_md);
+ if (hlen <= 0)
+ return 0;
+ out_len = (size_t)hlen;
+
+ ctx = EVP_MD_CTX_create();
+ ctx_init = EVP_MD_CTX_create();
+ if (ctx == NULL || ctx_init == NULL)
+ goto end;
+
+ if (!EVP_DigestInit(ctx_init, kdf_md))
+ goto end;
+
+ for (counter = 1;; counter++) {
+ c[0] = (unsigned char)((counter >> 24) & 0xff);
+ c[1] = (unsigned char)((counter >> 16) & 0xff);
+ c[2] = (unsigned char)((counter >> 8) & 0xff);
+ c[3] = (unsigned char)(counter & 0xff);
+
+ if (!(EVP_MD_CTX_copy_ex(ctx, ctx_init)
+ && (append_ctr || EVP_DigestUpdate(ctx, c, sizeof(c)))
+ && EVP_DigestUpdate(ctx, z, z_len)
+ && (!append_ctr || EVP_DigestUpdate(ctx, c, sizeof(c)))
+ && EVP_DigestUpdate(ctx, info, info_len)))
+ goto end;
+ if (len >= out_len) {
+ if (!EVP_DigestFinal_ex(ctx, out, NULL))
+ goto end;
+ out += out_len;
+ len -= out_len;
+ if (len == 0)
+ break;
+ } else {
+ if (!EVP_DigestFinal_ex(ctx, mac, NULL))
+ goto end;
+ memcpy(out, mac, len);
+ break;
+ }
+ }
+ ret = 1;
+end:
+ EVP_MD_CTX_destroy(ctx);
+ EVP_MD_CTX_destroy(ctx_init);
+ OPENSSL_cleanse(mac, sizeof(mac));
+ return ret;
+}
+
+static int kmac_init(EVP_MAC_CTX *ctx, const unsigned char *custom,
+ size_t custom_len, size_t kmac_out_len,
+ size_t derived_key_len, unsigned char **out)
+{
+ OSSL_PARAM params[2];
+
+ /* Only KMAC has custom data - so return if not KMAC */
+ if (custom == NULL)
+ return 1;
+
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_MAC_PARAM_CUSTOM,
+ (void *)custom, custom_len);
+ params[1] = OSSL_PARAM_construct_end();
+
+ if (!EVP_MAC_CTX_set_params(ctx, params))
+ return 0;
+
+ /* By default only do one iteration if kmac_out_len is not specified */
+ if (kmac_out_len == 0)
+ kmac_out_len = derived_key_len;
+ /* otherwise check the size is valid */
+ else if (!(kmac_out_len == derived_key_len
+ || kmac_out_len == 20
+ || kmac_out_len == 28
+ || kmac_out_len == 32
+ || kmac_out_len == 48
+ || kmac_out_len == 64))
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_size_t(OSSL_MAC_PARAM_SIZE,
+ &kmac_out_len);
+
+ if (EVP_MAC_CTX_set_params(ctx, params) <= 0)
+ return 0;
+
+ /*
+ * For kmac the output buffer can be larger than EVP_MAX_MD_SIZE: so
+ * alloc a buffer for this case.
+ */
+ if (kmac_out_len > EVP_MAX_MD_SIZE) {
+ *out = OPENSSL_zalloc(kmac_out_len);
+ if (*out == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Refer to https://csrc.nist.gov/publications/detail/sp/800-56c/rev-1/final
+ * Section 4. One-Step Key Derivation using MAC: i.e either
+ * H(x) = HMAC-hash(salt, x) OR
+ * H(x) = KMAC#(salt, x, outbits, CustomString='KDF')
+ */
+static int SSKDF_mac_kdm(EVP_MAC_CTX *ctx_init,
+ const unsigned char *kmac_custom,
+ size_t kmac_custom_len, size_t kmac_out_len,
+ const unsigned char *salt, size_t salt_len,
+ const unsigned char *z, size_t z_len,
+ const unsigned char *info, size_t info_len,
+ unsigned char *derived_key, size_t derived_key_len)
+{
+ int ret = 0;
+ size_t counter, out_len, len;
+ unsigned char c[4];
+ unsigned char mac_buf[EVP_MAX_MD_SIZE];
+ unsigned char *out = derived_key;
+ EVP_MAC_CTX *ctx = NULL;
+ unsigned char *mac = mac_buf, *kmac_buffer = NULL;
+
+ if (z_len > SSKDF_MAX_INLEN || info_len > SSKDF_MAX_INLEN
+ || derived_key_len > SSKDF_MAX_INLEN
+ || derived_key_len == 0)
+ return 0;
+
+ if (!kmac_init(ctx_init, kmac_custom, kmac_custom_len, kmac_out_len,
+ derived_key_len, &kmac_buffer))
+ goto end;
+ if (kmac_buffer != NULL)
+ mac = kmac_buffer;
+
+ if (!EVP_MAC_init(ctx_init, salt, salt_len, NULL))
+ goto end;
+
+ out_len = EVP_MAC_CTX_get_mac_size(ctx_init); /* output size */
+ if (out_len <= 0 || (mac == mac_buf && out_len > sizeof(mac_buf)))
+ goto end;
+ len = derived_key_len;
+
+ for (counter = 1;; counter++) {
+ c[0] = (unsigned char)((counter >> 24) & 0xff);
+ c[1] = (unsigned char)((counter >> 16) & 0xff);
+ c[2] = (unsigned char)((counter >> 8) & 0xff);
+ c[3] = (unsigned char)(counter & 0xff);
+
+ ctx = EVP_MAC_CTX_dup(ctx_init);
+ if (!(ctx != NULL
+ && EVP_MAC_update(ctx, c, sizeof(c))
+ && EVP_MAC_update(ctx, z, z_len)
+ && EVP_MAC_update(ctx, info, info_len)))
+ goto end;
+ if (len >= out_len) {
+ if (!EVP_MAC_final(ctx, out, NULL, len))
+ goto end;
+ out += out_len;
+ len -= out_len;
+ if (len == 0)
+ break;
+ } else {
+ if (!EVP_MAC_final(ctx, mac, NULL, out_len))
+ goto end;
+ memcpy(out, mac, len);
+ break;
+ }
+ EVP_MAC_CTX_free(ctx);
+ ctx = NULL;
+ }
+ ret = 1;
+end:
+ if (kmac_buffer != NULL)
+ OPENSSL_clear_free(kmac_buffer, kmac_out_len);
+ else
+ OPENSSL_cleanse(mac_buf, sizeof(mac_buf));
+
+ EVP_MAC_CTX_free(ctx);
+ return ret;
+}
+
+static void *sskdf_new(void *provctx)
+{
+ KDF_SSKDF *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) {
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ }
+ return ctx;
+}
+
+static void sskdf_reset(void *vctx)
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+ void *provctx = ctx->provctx;
+
+ EVP_MAC_CTX_free(ctx->macctx);
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_clear_free(ctx->secret, ctx->secret_len);
+ OPENSSL_clear_free(ctx->info, ctx->info_len);
+ OPENSSL_clear_free(ctx->salt, ctx->salt_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static void sskdf_free(void *vctx)
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+
+ if (ctx != NULL) {
+ sskdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void *sskdf_dup(void *vctx)
+{
+ const KDF_SSKDF *src = (const KDF_SSKDF *)vctx;
+ KDF_SSKDF *dest;
+
+ dest = sskdf_new(src->provctx);
+ if (dest != NULL) {
+ if (src->macctx != NULL) {
+ dest->macctx = EVP_MAC_CTX_dup(src->macctx);
+ if (dest->macctx == NULL)
+ goto err;
+ }
+ if (!ossl_prov_memdup(src->info, src->info_len,
+ &dest->info, &dest->info_len)
+ || !ossl_prov_memdup(src->salt, src->salt_len,
+ &dest->salt , &dest->salt_len)
+ || !ossl_prov_memdup(src->secret, src->secret_len,
+ &dest->secret, &dest->secret_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->out_len = src->out_len;
+ dest->is_kmac = src->is_kmac;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ sskdf_free(dest);
+ return NULL;
+}
+
+static size_t sskdf_size(KDF_SSKDF *ctx)
+{
+ int len;
+ const EVP_MD *md = NULL;
+
+ if (ctx->is_kmac)
+ return SIZE_MAX;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ len = EVP_MD_get_size(md);
+ return (len <= 0) ? 0 : (size_t)len;
+}
+
+#ifdef FIPS_MODULE
+static int fips_sskdf_key_check_passed(KDF_SSKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->secret_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "SSKDF", "Key size",
+ ossl_fips_config_sskdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int sskdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !sskdf_set_ctx_params(ctx, params))
+ return 0;
+ if (ctx->secret == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET);
+ return 0;
+ }
+
+ md = ossl_prov_digest_md(&ctx->digest);
+
+ if (ctx->macctx != NULL) {
+ /* H(x) = KMAC or H(x) = HMAC */
+ int ret;
+ const unsigned char *custom = NULL;
+ size_t custom_len = 0;
+ int default_salt_len;
+ EVP_MAC *mac = EVP_MAC_CTX_get0_mac(ctx->macctx);
+
+ if (EVP_MAC_is_a(mac, OSSL_MAC_NAME_HMAC)) {
+ /* H(x) = HMAC(x, salt, hash) */
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ default_salt_len = EVP_MD_get_size(md);
+ if (default_salt_len <= 0)
+ return 0;
+ } else if (ctx->is_kmac) {
+ /* H(x) = KMACzzz(x, salt, custom) */
+ custom = kmac_custom_str;
+ custom_len = sizeof(kmac_custom_str);
+ if (EVP_MAC_is_a(mac, OSSL_MAC_NAME_KMAC128))
+ default_salt_len = SSKDF_KMAC128_DEFAULT_SALT_SIZE;
+ else
+ default_salt_len = SSKDF_KMAC256_DEFAULT_SALT_SIZE;
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_MAC_TYPE);
+ return 0;
+ }
+ /* If no salt is set then use a default_salt of zeros */
+ if (ctx->salt == NULL || ctx->salt_len <= 0) {
+ ctx->salt = OPENSSL_zalloc(default_salt_len);
+ if (ctx->salt == NULL)
+ return 0;
+ ctx->salt_len = default_salt_len;
+ }
+ ret = SSKDF_mac_kdm(ctx->macctx,
+ custom, custom_len, ctx->out_len,
+ ctx->salt, ctx->salt_len,
+ ctx->secret, ctx->secret_len,
+ ctx->info, ctx->info_len, key, keylen);
+ return ret;
+ } else {
+ /* H(x) = hash */
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len,
+ ctx->info, ctx->info_len, 0, key, keylen);
+ }
+}
+
+#ifdef FIPS_MODULE
+static int fips_x963kdf_digest_check_passed(KDF_SSKDF *ctx, const EVP_MD *md)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ /*
+ * Perform digest check
+ *
+ * X963KDF is a KDF defined in ANSI-X9.63. According to ACVP specification
+ * section 7.3.1, only SHA-2 and SHA-3 can be regarded as valid hash
+ * functions.
+ */
+ int digest_unapproved = (ctx->is_kmac != 1) && EVP_MD_is_a(md, SN_sha1);
+
+ if (digest_unapproved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "X963KDF", "Digest",
+ ossl_fips_config_x963kdf_digest_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int fips_x963kdf_key_check_passed(KDF_SSKDF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->secret_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1,
+ libctx, "X963KDF", "Key size",
+ ossl_fips_config_x963kdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int x963kdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+ const EVP_MD *md;
+
+ if (!ossl_prov_is_running() || !x963kdf_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->secret == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET);
+ return 0;
+ }
+
+ if (ctx->macctx != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED);
+ return 0;
+ }
+
+ /* H(x) = hash */
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+
+ return SSKDF_hash_kdm(md, ctx->secret, ctx->secret_len,
+ ctx->info, ctx->info_len, 1, key, keylen);
+}
+
+static int sskdf_common_set_ctx_params(KDF_SSKDF *ctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ const EVP_MD *md = NULL;
+ size_t sz;
+ int r;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_prov_macctx_load_from_params(&ctx->macctx, params,
+ NULL, NULL, NULL, libctx))
+ return 0;
+ if (ctx->macctx != NULL) {
+ if (EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->macctx),
+ OSSL_MAC_NAME_KMAC128)
+ || EVP_MAC_is_a(EVP_MAC_CTX_get0_mac(ctx->macctx),
+ OSSL_MAC_NAME_KMAC256)) {
+ ctx->is_kmac = 1;
+ }
+ }
+
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, libctx))
+ return 0;
+
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+ }
+
+ r = ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SECRET,
+ &ctx->secret, &ctx->secret_len);
+ if (r == -1)
+ r = ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_KEY,
+ &ctx->secret, &ctx->secret_len);
+ if (r == 0)
+ return 0;
+
+ if (ossl_param_get1_concat_octet_string(params, OSSL_KDF_PARAM_INFO,
+ &ctx->info, &ctx->info_len, 0) == 0)
+ return 0;
+
+ if (ossl_param_get1_octet_string(params, OSSL_KDF_PARAM_SALT,
+ &ctx->salt, &ctx->salt_len) == 0)
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_MAC_SIZE))
+ != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &sz) || sz == 0)
+ return 0;
+ ctx->out_len = sz;
+ }
+ return 1;
+}
+
+static int sskdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!sskdf_common_set_ctx_params(ctx, params))
+ return 0;
+
+#ifdef FIPS_MODULE
+ if ((OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) ||
+ (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET) != NULL))
+ if (!fips_sskdf_key_check_passed(ctx))
+ return 0;
+#endif
+
+ return 1;
+}
+
+static const OSSL_PARAM *sskdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ SSKDF_COMMON_SETTABLES,
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int sskdf_common_get_ctx_params(KDF_SSKDF *ctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, sskdf_size(ctx)))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int sskdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!sskdf_common_get_ctx_params(ctx, params))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *sskdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ SSKDF_COMMON_GETTABLES,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int x963kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!sskdf_common_set_ctx_params(ctx, params))
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+
+ if (!fips_x963kdf_digest_check_passed(ctx, md))
+ return 0;
+ }
+
+ if ((OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY) != NULL) ||
+ (OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET) != NULL))
+ if (!fips_x963kdf_key_check_passed(ctx))
+ return 0;
+#endif
+
+ return 1;
+}
+
+static const OSSL_PARAM *x963kdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ SSKDF_COMMON_SETTABLES,
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int x963kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_SSKDF *ctx = (KDF_SSKDF *)vctx;
+
+ if (!sskdf_common_get_ctx_params(ctx, params))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *x963kdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ SSKDF_COMMON_GETTABLES,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_sskdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))sskdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))sskdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))sskdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))sskdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))sskdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))sskdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))sskdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))sskdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))sskdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_kdf_x963_kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))sskdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))sskdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))sskdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))sskdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))x963kdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))x963kdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))x963kdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))x963kdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))x963kdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kdfs/tls1_prf.c b/crypto/openssl/providers/implementations/kdfs/tls1_prf.c
new file mode 100644
index 000000000000..4b6469c00d23
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/tls1_prf.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2016-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Refer to "The TLS Protocol Version 1.0" Section 5
+ * (https://tools.ietf.org/html/rfc2246#section-5) and
+ * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
+ * (https://tools.ietf.org/html/rfc5246#section-5).
+ *
+ * For TLS v1.0 and TLS v1.1 the TLS PRF algorithm is given by:
+ *
+ * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
+ * P_SHA-1(S2, label + seed)
+ *
+ * where P_MD5 and P_SHA-1 are defined by P_<hash>, below, and S1 and S2 are
+ * two halves of the secret (with the possibility of one shared byte, in the
+ * case where the length of the original secret is odd). S1 is taken from the
+ * first half of the secret, S2 from the second half.
+ *
+ * For TLS v1.2 the TLS PRF algorithm is given by:
+ *
+ * PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+ *
+ * where hash is SHA-256 for all cipher suites defined in RFC 5246 as well as
+ * those published prior to TLS v1.2 while the TLS v1.2 protocol is in effect,
+ * unless defined otherwise by the cipher suite.
+ *
+ * P_<hash> is an expansion function that uses a single hash function to expand
+ * a secret and seed into an arbitrary quantity of output:
+ *
+ * P_<hash>(secret, seed) = HMAC_<hash>(secret, A(1) + seed) +
+ * HMAC_<hash>(secret, A(2) + seed) +
+ * HMAC_<hash>(secret, A(3) + seed) + ...
+ *
+ * where + indicates concatenation. P_<hash> can be iterated as many times as
+ * is necessary to produce the required quantity of data.
+ *
+ * A(i) is defined as:
+ * A(0) = seed
+ * A(i) = HMAC_<hash>(secret, A(i-1))
+ */
+
+/*
+ * Low level APIs (such as DH) are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/numbers.h"
+#include "crypto/evp.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+#include "internal/e_os.h"
+#include "internal/safe_math.h"
+
+OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
+
+static OSSL_FUNC_kdf_newctx_fn kdf_tls1_prf_new;
+static OSSL_FUNC_kdf_dupctx_fn kdf_tls1_prf_dup;
+static OSSL_FUNC_kdf_freectx_fn kdf_tls1_prf_free;
+static OSSL_FUNC_kdf_reset_fn kdf_tls1_prf_reset;
+static OSSL_FUNC_kdf_derive_fn kdf_tls1_prf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn kdf_tls1_prf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn kdf_tls1_prf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn kdf_tls1_prf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn kdf_tls1_prf_get_ctx_params;
+
+static int tls1_prf_alg(EVP_MAC_CTX *mdctx, EVP_MAC_CTX *sha1ctx,
+ const unsigned char *sec, size_t slen,
+ const unsigned char *seed, size_t seed_len,
+ unsigned char *out, size_t olen);
+
+#define TLS_MD_MASTER_SECRET_CONST "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"
+#define TLS_MD_MASTER_SECRET_CONST_SIZE 13
+
+/* TLS KDF kdf context structure */
+typedef struct {
+ void *provctx;
+
+ /* MAC context for the main digest */
+ EVP_MAC_CTX *P_hash;
+ /* MAC context for SHA1 for the MD5/SHA-1 combined PRF */
+ EVP_MAC_CTX *P_sha1;
+
+ /* Secret value to use for PRF */
+ unsigned char *sec;
+ size_t seclen;
+ /* Concatenated seed data */
+ unsigned char *seed;
+ size_t seedlen;
+
+ OSSL_FIPS_IND_DECLARE
+} TLS1_PRF;
+
+static void *kdf_tls1_prf_new(void *provctx)
+{
+ TLS1_PRF *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL) {
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ }
+ return ctx;
+}
+
+static void kdf_tls1_prf_free(void *vctx)
+{
+ TLS1_PRF *ctx = (TLS1_PRF *)vctx;
+
+ if (ctx != NULL) {
+ kdf_tls1_prf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void kdf_tls1_prf_reset(void *vctx)
+{
+ TLS1_PRF *ctx = (TLS1_PRF *)vctx;
+ void *provctx = ctx->provctx;
+
+ EVP_MAC_CTX_free(ctx->P_hash);
+ EVP_MAC_CTX_free(ctx->P_sha1);
+ OPENSSL_clear_free(ctx->sec, ctx->seclen);
+ OPENSSL_clear_free(ctx->seed, ctx->seedlen);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+}
+
+static void *kdf_tls1_prf_dup(void *vctx)
+{
+ const TLS1_PRF *src = (const TLS1_PRF *)vctx;
+ TLS1_PRF *dest;
+
+ dest = kdf_tls1_prf_new(src->provctx);
+ if (dest != NULL) {
+ if (src->P_hash != NULL
+ && (dest->P_hash = EVP_MAC_CTX_dup(src->P_hash)) == NULL)
+ goto err;
+ if (src->P_sha1 != NULL
+ && (dest->P_sha1 = EVP_MAC_CTX_dup(src->P_sha1)) == NULL)
+ goto err;
+ if (!ossl_prov_memdup(src->sec, src->seclen, &dest->sec, &dest->seclen))
+ goto err;
+ if (!ossl_prov_memdup(src->seed, src->seedlen, &dest->seed,
+ &dest->seedlen))
+ goto err;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ kdf_tls1_prf_free(dest);
+ return NULL;
+}
+
+#ifdef FIPS_MODULE
+
+static int fips_ems_check_passed(TLS1_PRF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ /*
+ * Check that TLS is using EMS.
+ *
+ * The seed buffer is prepended with a label.
+ * If EMS mode is enforced then the label "master secret" is not allowed,
+ * We do the check this way since the PRF is used for other purposes, as well
+ * as "extended master secret".
+ */
+ int ems_approved = (ctx->seedlen < TLS_MD_MASTER_SECRET_CONST_SIZE
+ || memcmp(ctx->seed, TLS_MD_MASTER_SECRET_CONST,
+ TLS_MD_MASTER_SECRET_CONST_SIZE) != 0);
+
+ if (!ems_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "TLS_PRF", "EMS",
+ ossl_fips_config_tls1_prf_ems_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_EMS_NOT_ENABLED);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int fips_digest_check_passed(TLS1_PRF *ctx, const EVP_MD *md)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ /*
+ * Perform digest check
+ *
+ * According to NIST SP 800-135r1 section 5.2, the valid hash functions are
+ * specified in FIPS 180-3. ACVP also only lists the same set of hash
+ * functions.
+ */
+ int digest_unapproved = !EVP_MD_is_a(md, SN_sha256)
+ && !EVP_MD_is_a(md, SN_sha384)
+ && !EVP_MD_is_a(md, SN_sha512);
+
+ if (digest_unapproved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE1,
+ libctx, "TLS_PRF", "Digest",
+ ossl_fips_config_tls1_prf_digest_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int fips_key_check_passed(TLS1_PRF *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->seclen);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2,
+ libctx, "TLS_PRF", "Key size",
+ ossl_fips_config_tls1_prf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int kdf_tls1_prf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ TLS1_PRF *ctx = (TLS1_PRF *)vctx;
+
+ if (!ossl_prov_is_running() || !kdf_tls1_prf_set_ctx_params(ctx, params))
+ return 0;
+
+ if (ctx->P_hash == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ if (ctx->sec == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET);
+ return 0;
+ }
+ if (ctx->seedlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SEED);
+ return 0;
+ }
+ if (keylen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+#ifdef FIPS_MODULE
+ if (!fips_ems_check_passed(ctx))
+ return 0;
+#endif
+
+ return tls1_prf_alg(ctx->P_hash, ctx->P_sha1,
+ ctx->sec, ctx->seclen,
+ ctx->seed, ctx->seedlen,
+ key, keylen);
+}
+
+static int kdf_tls1_prf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+ TLS1_PRF *ctx = vctx;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_EMS_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_KDF_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE2, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_DIGEST)) != NULL) {
+ PROV_DIGEST digest;
+ const EVP_MD *md = NULL;
+
+ if (OPENSSL_strcasecmp(p->data, SN_md5_sha1) == 0) {
+ if (!ossl_prov_macctx_load_from_params(&ctx->P_hash, params,
+ OSSL_MAC_NAME_HMAC,
+ NULL, SN_md5, libctx)
+ || !ossl_prov_macctx_load_from_params(&ctx->P_sha1, params,
+ OSSL_MAC_NAME_HMAC,
+ NULL, SN_sha1, libctx))
+ return 0;
+ } else {
+ EVP_MAC_CTX_free(ctx->P_sha1);
+ if (!ossl_prov_macctx_load_from_params(&ctx->P_hash, params,
+ OSSL_MAC_NAME_HMAC,
+ NULL, NULL, libctx))
+ return 0;
+ }
+
+ memset(&digest, 0, sizeof(digest));
+ if (!ossl_prov_digest_load_from_params(&digest, params, libctx))
+ return 0;
+
+ md = ossl_prov_digest_md(&digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ ossl_prov_digest_reset(&digest);
+ return 0;
+ }
+
+#ifdef FIPS_MODULE
+ if (!fips_digest_check_passed(ctx, md)) {
+ ossl_prov_digest_reset(&digest);
+ return 0;
+ }
+#endif
+
+ ossl_prov_digest_reset(&digest);
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET)) != NULL) {
+ OPENSSL_clear_free(ctx->sec, ctx->seclen);
+ ctx->sec = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->sec, 0, &ctx->seclen))
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (!fips_key_check_passed(ctx))
+ return 0;
+#endif
+ }
+ /* The seed fields concatenate, so process them all */
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SEED)) != NULL) {
+ for (; p != NULL; p = OSSL_PARAM_locate_const(p + 1,
+ OSSL_KDF_PARAM_SEED)) {
+ if (p->data_size != 0 && p->data != NULL) {
+ const void *val = NULL;
+ size_t sz = 0;
+ unsigned char *seed;
+ size_t seedlen;
+ int err = 0;
+
+ if (!OSSL_PARAM_get_octet_string_ptr(p, &val, &sz))
+ return 0;
+
+ seedlen = safe_add_size_t(ctx->seedlen, sz, &err);
+ if (err)
+ return 0;
+
+ seed = OPENSSL_clear_realloc(ctx->seed, ctx->seedlen, seedlen);
+ if (!seed)
+ return 0;
+
+ ctx->seed = seed;
+ if (ossl_assert(sz != 0))
+ memcpy(ctx->seed + ctx->seedlen, val, sz);
+ ctx->seedlen = seedlen;
+ }
+ }
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_prf_settable_ctx_params(
+ ossl_unused void *ctx, ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SEED, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_EMS_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int kdf_tls1_prf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE)) != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, SIZE_MAX))
+ return 0;
+ }
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(((TLS1_PRF *)vctx), params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *kdf_tls1_prf_gettable_ctx_params(
+ ossl_unused void *ctx, ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_tls1_prf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))kdf_tls1_prf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))kdf_tls1_prf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))kdf_tls1_prf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))kdf_tls1_prf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))kdf_tls1_prf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_prf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_prf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_prf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS,
+ (void(*)(void))kdf_tls1_prf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+/*
+ * Refer to "The TLS Protocol Version 1.0" Section 5
+ * (https://tools.ietf.org/html/rfc2246#section-5) and
+ * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
+ * (https://tools.ietf.org/html/rfc5246#section-5).
+ *
+ * P_<hash> is an expansion function that uses a single hash function to expand
+ * a secret and seed into an arbitrary quantity of output:
+ *
+ * P_<hash>(secret, seed) = HMAC_<hash>(secret, A(1) + seed) +
+ * HMAC_<hash>(secret, A(2) + seed) +
+ * HMAC_<hash>(secret, A(3) + seed) + ...
+ *
+ * where + indicates concatenation. P_<hash> can be iterated as many times as
+ * is necessary to produce the required quantity of data.
+ *
+ * A(i) is defined as:
+ * A(0) = seed
+ * A(i) = HMAC_<hash>(secret, A(i-1))
+ */
+static int tls1_prf_P_hash(EVP_MAC_CTX *ctx_init,
+ const unsigned char *sec, size_t sec_len,
+ const unsigned char *seed, size_t seed_len,
+ unsigned char *out, size_t olen)
+{
+ size_t chunk;
+ EVP_MAC_CTX *ctx = NULL, *ctx_Ai = NULL;
+ unsigned char Ai[EVP_MAX_MD_SIZE];
+ size_t Ai_len;
+ int ret = 0;
+
+ if (!EVP_MAC_init(ctx_init, sec, sec_len, NULL))
+ goto err;
+ chunk = EVP_MAC_CTX_get_mac_size(ctx_init);
+ if (chunk == 0)
+ goto err;
+ /* A(0) = seed */
+ ctx_Ai = EVP_MAC_CTX_dup(ctx_init);
+ if (ctx_Ai == NULL)
+ goto err;
+ if (seed != NULL && !EVP_MAC_update(ctx_Ai, seed, seed_len))
+ goto err;
+
+ for (;;) {
+ /* calc: A(i) = HMAC_<hash>(secret, A(i-1)) */
+ if (!EVP_MAC_final(ctx_Ai, Ai, &Ai_len, sizeof(Ai)))
+ goto err;
+ EVP_MAC_CTX_free(ctx_Ai);
+ ctx_Ai = NULL;
+
+ /* calc next chunk: HMAC_<hash>(secret, A(i) + seed) */
+ ctx = EVP_MAC_CTX_dup(ctx_init);
+ if (ctx == NULL)
+ goto err;
+ if (!EVP_MAC_update(ctx, Ai, Ai_len))
+ goto err;
+ /* save state for calculating next A(i) value */
+ if (olen > chunk) {
+ ctx_Ai = EVP_MAC_CTX_dup(ctx);
+ if (ctx_Ai == NULL)
+ goto err;
+ }
+ if (seed != NULL && !EVP_MAC_update(ctx, seed, seed_len))
+ goto err;
+ if (olen <= chunk) {
+ /* last chunk - use Ai as temp bounce buffer */
+ if (!EVP_MAC_final(ctx, Ai, &Ai_len, sizeof(Ai)))
+ goto err;
+ memcpy(out, Ai, olen);
+ break;
+ }
+ if (!EVP_MAC_final(ctx, out, NULL, olen))
+ goto err;
+ EVP_MAC_CTX_free(ctx);
+ ctx = NULL;
+ out += chunk;
+ olen -= chunk;
+ }
+ ret = 1;
+ err:
+ EVP_MAC_CTX_free(ctx);
+ EVP_MAC_CTX_free(ctx_Ai);
+ OPENSSL_cleanse(Ai, sizeof(Ai));
+ return ret;
+}
+
+/*
+ * Refer to "The TLS Protocol Version 1.0" Section 5
+ * (https://tools.ietf.org/html/rfc2246#section-5) and
+ * "The Transport Layer Security (TLS) Protocol Version 1.2" Section 5
+ * (https://tools.ietf.org/html/rfc5246#section-5).
+ *
+ * For TLS v1.0 and TLS v1.1:
+ *
+ * PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR
+ * P_SHA-1(S2, label + seed)
+ *
+ * S1 is taken from the first half of the secret, S2 from the second half.
+ *
+ * L_S = length in bytes of secret;
+ * L_S1 = L_S2 = ceil(L_S / 2);
+ *
+ * For TLS v1.2:
+ *
+ * PRF(secret, label, seed) = P_<hash>(secret, label + seed)
+ */
+static int tls1_prf_alg(EVP_MAC_CTX *mdctx, EVP_MAC_CTX *sha1ctx,
+ const unsigned char *sec, size_t slen,
+ const unsigned char *seed, size_t seed_len,
+ unsigned char *out, size_t olen)
+{
+ if (sha1ctx != NULL) {
+ /* TLS v1.0 and TLS v1.1 */
+ size_t i;
+ unsigned char *tmp;
+ /* calc: L_S1 = L_S2 = ceil(L_S / 2) */
+ size_t L_S1 = (slen + 1) / 2;
+ size_t L_S2 = L_S1;
+
+ if (!tls1_prf_P_hash(mdctx, sec, L_S1,
+ seed, seed_len, out, olen))
+ return 0;
+
+ if ((tmp = OPENSSL_malloc(olen)) == NULL)
+ return 0;
+
+ if (!tls1_prf_P_hash(sha1ctx, sec + slen - L_S2, L_S2,
+ seed, seed_len, tmp, olen)) {
+ OPENSSL_clear_free(tmp, olen);
+ return 0;
+ }
+ for (i = 0; i < olen; i++)
+ out[i] ^= tmp[i];
+ OPENSSL_clear_free(tmp, olen);
+ return 1;
+ }
+
+ /* TLS v1.2 */
+ if (!tls1_prf_P_hash(mdctx, sec, slen, seed, seed_len, out, olen))
+ return 0;
+
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/kdfs/x942kdf.c b/crypto/openssl/providers/implementations/kdfs/x942kdf.c
new file mode 100644
index 000000000000..63164d8b8fbe
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kdfs/x942kdf.c
@@ -0,0 +1,679 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/e_os.h"
+#include <openssl/core_names.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+#include "internal/packet.h"
+#include "internal/der.h"
+#include "internal/nelem.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_util.h"
+#include "prov/securitycheck.h"
+#include "prov/der_wrap.h"
+
+#define X942KDF_MAX_INLEN (1 << 30)
+
+static OSSL_FUNC_kdf_newctx_fn x942kdf_new;
+static OSSL_FUNC_kdf_dupctx_fn x942kdf_dup;
+static OSSL_FUNC_kdf_freectx_fn x942kdf_free;
+static OSSL_FUNC_kdf_reset_fn x942kdf_reset;
+static OSSL_FUNC_kdf_derive_fn x942kdf_derive;
+static OSSL_FUNC_kdf_settable_ctx_params_fn x942kdf_settable_ctx_params;
+static OSSL_FUNC_kdf_set_ctx_params_fn x942kdf_set_ctx_params;
+static OSSL_FUNC_kdf_gettable_ctx_params_fn x942kdf_gettable_ctx_params;
+static OSSL_FUNC_kdf_get_ctx_params_fn x942kdf_get_ctx_params;
+
+typedef struct {
+ void *provctx;
+ PROV_DIGEST digest;
+ unsigned char *secret;
+ size_t secret_len;
+ unsigned char *acvpinfo;
+ size_t acvpinfo_len;
+ unsigned char *partyuinfo, *partyvinfo, *supp_pubinfo, *supp_privinfo;
+ size_t partyuinfo_len, partyvinfo_len, supp_pubinfo_len, supp_privinfo_len;
+ size_t dkm_len;
+ const unsigned char *cek_oid;
+ size_t cek_oid_len;
+ int use_keybits;
+ OSSL_FIPS_IND_DECLARE
+} KDF_X942;
+
+/*
+ * A table of allowed wrapping algorithms, oids and the associated output
+ * lengths.
+ * NOTE: RC2wrap and camellia128_wrap have been removed as there are no
+ * corresponding ciphers for these operations.
+ */
+static const struct {
+ const char *name;
+ const unsigned char *oid;
+ size_t oid_len;
+ size_t keklen; /* size in bytes */
+} kek_algs[] = {
+ { "AES-128-WRAP", ossl_der_oid_id_aes128_wrap, DER_OID_SZ_id_aes128_wrap,
+ 16 },
+ { "AES-192-WRAP", ossl_der_oid_id_aes192_wrap, DER_OID_SZ_id_aes192_wrap,
+ 24 },
+ { "AES-256-WRAP", ossl_der_oid_id_aes256_wrap, DER_OID_SZ_id_aes256_wrap,
+ 32 },
+#ifndef FIPS_MODULE
+ { "DES3-WRAP", ossl_der_oid_id_alg_CMS3DESwrap,
+ DER_OID_SZ_id_alg_CMS3DESwrap, 24 },
+#endif
+};
+
+static int find_alg_id(OSSL_LIB_CTX *libctx, const char *algname,
+ const char *propq, size_t *id)
+{
+ int ret = 1;
+ size_t i;
+ EVP_CIPHER *cipher;
+
+ cipher = EVP_CIPHER_fetch(libctx, algname, propq);
+ if (cipher != NULL) {
+ for (i = 0; i < OSSL_NELEM(kek_algs); i++) {
+ if (EVP_CIPHER_is_a(cipher, kek_algs[i].name)) {
+ *id = i;
+ goto end;
+ }
+ }
+ }
+ ret = 0;
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNSUPPORTED_CEK_ALG);
+end:
+ EVP_CIPHER_free(cipher);
+ return ret;
+}
+
+static int DER_w_keyinfo(WPACKET *pkt,
+ const unsigned char *der_oid, size_t der_oidlen,
+ unsigned char **pcounter)
+{
+ return ossl_DER_w_begin_sequence(pkt, -1)
+ /* Store the initial value of 1 into the counter */
+ && ossl_DER_w_octet_string_uint32(pkt, -1, 1)
+ /* Remember where we stored the counter in the buffer */
+ && (pcounter == NULL
+ || (*pcounter = WPACKET_get_curr(pkt)) != NULL)
+ && ossl_DER_w_precompiled(pkt, -1, der_oid, der_oidlen)
+ && ossl_DER_w_end_sequence(pkt, -1);
+}
+
+static int der_encode_sharedinfo(WPACKET *pkt, unsigned char *buf, size_t buflen,
+ const unsigned char *der_oid, size_t der_oidlen,
+ const unsigned char *acvp, size_t acvplen,
+ const unsigned char *partyu, size_t partyulen,
+ const unsigned char *partyv, size_t partyvlen,
+ const unsigned char *supp_pub, size_t supp_publen,
+ const unsigned char *supp_priv, size_t supp_privlen,
+ uint32_t keylen_bits, unsigned char **pcounter)
+{
+ return (buf != NULL ? WPACKET_init_der(pkt, buf, buflen) :
+ WPACKET_init_null_der(pkt))
+ && ossl_DER_w_begin_sequence(pkt, -1)
+ && (supp_priv == NULL
+ || ossl_DER_w_octet_string(pkt, 3, supp_priv, supp_privlen))
+ && (supp_pub == NULL
+ || ossl_DER_w_octet_string(pkt, 2, supp_pub, supp_publen))
+ && (keylen_bits == 0
+ || ossl_DER_w_octet_string_uint32(pkt, 2, keylen_bits))
+ && (partyv == NULL || ossl_DER_w_octet_string(pkt, 1, partyv, partyvlen))
+ && (partyu == NULL || ossl_DER_w_octet_string(pkt, 0, partyu, partyulen))
+ && (acvp == NULL || ossl_DER_w_precompiled(pkt, -1, acvp, acvplen))
+ && DER_w_keyinfo(pkt, der_oid, der_oidlen, pcounter)
+ && ossl_DER_w_end_sequence(pkt, -1)
+ && WPACKET_finish(pkt);
+}
+
+/*
+ * Encode the other info structure.
+ *
+ * The ANS X9.42-2003 standard uses OtherInfo:
+ *
+ * OtherInfo ::= SEQUENCE {
+ * keyInfo KeySpecificInfo,
+ * partyUInfo [0] OCTET STRING OPTIONAL,
+ * partyVInfo [1] OCTET STRING OPTIONAL,
+ * suppPubInfo [2] OCTET STRING OPTIONAL,
+ * suppPrivInfo [3] OCTET STRING OPTIONAL
+ * }
+ *
+ * KeySpecificInfo ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * counter OCTET STRING SIZE (4..4)
+ * }
+ *
+ * RFC2631 Section 2.1.2 Contains the following definition for OtherInfo
+ *
+ * OtherInfo ::= SEQUENCE {
+ * keyInfo KeySpecificInfo,
+ * partyAInfo [0] OCTET STRING OPTIONAL,
+ * suppPubInfo [2] OCTET STRING
+ * }
+ * Where suppPubInfo is the key length (in bits) (stored into 4 bytes)
+ *
+ * |keylen| is the length (in bytes) of the generated KEK. It is stored into
+ * suppPubInfo (in bits). It is ignored if the value is 0.
+ * |cek_oid| The oid of the key wrapping algorithm.
+ * |cek_oidlen| The length (in bytes) of the key wrapping algorithm oid,
+ * |acvp| is the optional blob of DER data representing one or more of the
+ * OtherInfo fields related to |partyu|, |partyv|, |supp_pub| and |supp_priv|.
+ * This field should normally be NULL. If |acvp| is non NULL then |partyu|,
+ * |partyv|, |supp_pub| and |supp_priv| should all be NULL.
+ * |acvp_len| is the |acvp| length (in bytes).
+ * |partyu| is the optional public info contributed by the initiator.
+ * It can be NULL. (It is also used as the ukm by CMS).
+ * |partyu_len| is the |partyu| length (in bytes).
+ * |partyv| is the optional public info contributed by the responder.
+ * It can be NULL.
+ * |partyv_len| is the |partyv| length (in bytes).
+ * |supp_pub| is the optional additional, mutually-known public information.
+ * It can be NULL. |keylen| should be 0 if this is not NULL.
+ * |supp_pub_len| is the |supp_pub| length (in bytes).
+ * |supp_priv| is the optional additional, mutually-known private information.
+ * It can be NULL.
+ * |supp_priv_len| is the |supp_priv| length (in bytes).
+ * |der| is the returned encoded data. It must be freed by the caller.
+ * |der_len| is the returned size of the encoded data.
+ * |out_ctr| returns a pointer to the counter data which is embedded inside the
+ * encoded data. This allows the counter bytes to be updated without
+ * re-encoding.
+ *
+ * Returns: 1 if successfully encoded, or 0 otherwise.
+ * Assumptions: |der|, |der_len| & |out_ctr| are not NULL.
+ */
+static int
+x942_encode_otherinfo(size_t keylen,
+ const unsigned char *cek_oid, size_t cek_oid_len,
+ const unsigned char *acvp, size_t acvp_len,
+ const unsigned char *partyu, size_t partyu_len,
+ const unsigned char *partyv, size_t partyv_len,
+ const unsigned char *supp_pub, size_t supp_pub_len,
+ const unsigned char *supp_priv, size_t supp_priv_len,
+ unsigned char **der, size_t *der_len,
+ unsigned char **out_ctr)
+{
+ int ret = 0;
+ unsigned char *pcounter = NULL, *der_buf = NULL;
+ size_t der_buflen = 0;
+ WPACKET pkt;
+ uint32_t keylen_bits;
+
+ /* keylenbits must fit into 4 bytes */
+ if (keylen > 0xFFFFFF)
+ return 0;
+ keylen_bits = 8 * keylen;
+
+ /* Calculate the size of the buffer */
+ if (!der_encode_sharedinfo(&pkt, NULL, 0, cek_oid, cek_oid_len,
+ acvp, acvp_len,
+ partyu, partyu_len, partyv, partyv_len,
+ supp_pub, supp_pub_len, supp_priv, supp_priv_len,
+ keylen_bits, NULL)
+ || !WPACKET_get_total_written(&pkt, &der_buflen))
+ goto err;
+ WPACKET_cleanup(&pkt);
+ /* Alloc the buffer */
+ der_buf = OPENSSL_zalloc(der_buflen);
+ if (der_buf == NULL)
+ goto err;
+ /* Encode into the buffer */
+ if (!der_encode_sharedinfo(&pkt, der_buf, der_buflen, cek_oid, cek_oid_len,
+ acvp, acvp_len,
+ partyu, partyu_len, partyv, partyv_len,
+ supp_pub, supp_pub_len, supp_priv, supp_priv_len,
+ keylen_bits, &pcounter))
+ goto err;
+ /*
+ * Since we allocated the exact size required, the buffer should point to the
+ * start of the allocated buffer at this point.
+ */
+ if (WPACKET_get_curr(&pkt) != der_buf)
+ goto err;
+
+ /*
+ * The data for the DER encoded octet string of a 32 bit counter = 1
+ * should be 04 04 00 00 00 01
+ * So just check the header is correct and skip over it.
+ * This counter will be incremented in the kdf update loop.
+ */
+ if (pcounter == NULL
+ || pcounter[0] != 0x04
+ || pcounter[1] != 0x04)
+ goto err;
+ *out_ctr = (pcounter + 2);
+ *der = der_buf;
+ *der_len = der_buflen;
+ ret = 1;
+err:
+ WPACKET_cleanup(&pkt);
+ return ret;
+}
+
+static int x942kdf_hash_kdm(const EVP_MD *kdf_md,
+ const unsigned char *z, size_t z_len,
+ const unsigned char *other, size_t other_len,
+ unsigned char *ctr,
+ unsigned char *derived_key, size_t derived_key_len)
+{
+ int ret = 0, hlen;
+ size_t counter, out_len, len = derived_key_len;
+ unsigned char mac[EVP_MAX_MD_SIZE];
+ unsigned char *out = derived_key;
+ EVP_MD_CTX *ctx = NULL, *ctx_init = NULL;
+
+ if (z_len > X942KDF_MAX_INLEN
+ || other_len > X942KDF_MAX_INLEN
+ || derived_key_len > X942KDF_MAX_INLEN
+ || derived_key_len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
+ return 0;
+ }
+
+ hlen = EVP_MD_get_size(kdf_md);
+ if (hlen <= 0)
+ return 0;
+ out_len = (size_t)hlen;
+
+ ctx = EVP_MD_CTX_create();
+ ctx_init = EVP_MD_CTX_create();
+ if (ctx == NULL || ctx_init == NULL)
+ goto end;
+
+ if (!EVP_DigestInit(ctx_init, kdf_md))
+ goto end;
+
+ for (counter = 1;; counter++) {
+ /* updating the ctr modifies 4 bytes in the 'other' buffer */
+ ctr[0] = (unsigned char)((counter >> 24) & 0xff);
+ ctr[1] = (unsigned char)((counter >> 16) & 0xff);
+ ctr[2] = (unsigned char)((counter >> 8) & 0xff);
+ ctr[3] = (unsigned char)(counter & 0xff);
+
+ if (!EVP_MD_CTX_copy_ex(ctx, ctx_init)
+ || !EVP_DigestUpdate(ctx, z, z_len)
+ || !EVP_DigestUpdate(ctx, other, other_len))
+ goto end;
+ if (len >= out_len) {
+ if (!EVP_DigestFinal_ex(ctx, out, NULL))
+ goto end;
+ out += out_len;
+ len -= out_len;
+ if (len == 0)
+ break;
+ } else {
+ if (!EVP_DigestFinal_ex(ctx, mac, NULL))
+ goto end;
+ memcpy(out, mac, len);
+ break;
+ }
+ }
+ ret = 1;
+end:
+ EVP_MD_CTX_free(ctx);
+ EVP_MD_CTX_free(ctx_init);
+ OPENSSL_cleanse(mac, sizeof(mac));
+ return ret;
+}
+
+static void *x942kdf_new(void *provctx)
+{
+ KDF_X942 *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(ctx)
+ ctx->use_keybits = 1;
+ return ctx;
+}
+
+static void x942kdf_reset(void *vctx)
+{
+ KDF_X942 *ctx = (KDF_X942 *)vctx;
+ void *provctx = ctx->provctx;
+
+ ossl_prov_digest_reset(&ctx->digest);
+ OPENSSL_clear_free(ctx->secret, ctx->secret_len);
+ OPENSSL_clear_free(ctx->acvpinfo, ctx->acvpinfo_len);
+ OPENSSL_clear_free(ctx->partyuinfo, ctx->partyuinfo_len);
+ OPENSSL_clear_free(ctx->partyvinfo, ctx->partyvinfo_len);
+ OPENSSL_clear_free(ctx->supp_pubinfo, ctx->supp_pubinfo_len);
+ OPENSSL_clear_free(ctx->supp_privinfo, ctx->supp_privinfo_len);
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->provctx = provctx;
+ ctx->use_keybits = 1;
+}
+
+static void x942kdf_free(void *vctx)
+{
+ KDF_X942 *ctx = (KDF_X942 *)vctx;
+
+ if (ctx != NULL) {
+ x942kdf_reset(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+
+static void *x942kdf_dup(void *vctx)
+{
+ const KDF_X942 *src = (const KDF_X942 *)vctx;
+ KDF_X942 *dest;
+
+ dest = x942kdf_new(src->provctx);
+ if (dest != NULL) {
+ if (!ossl_prov_memdup(src->secret, src->secret_len,
+ &dest->secret , &dest->secret_len)
+ || !ossl_prov_memdup(src->acvpinfo, src->acvpinfo_len,
+ &dest->acvpinfo , &dest->acvpinfo_len)
+ || !ossl_prov_memdup(src->partyuinfo, src->partyuinfo_len,
+ &dest->partyuinfo , &dest->partyuinfo_len)
+ || !ossl_prov_memdup(src->partyvinfo, src->partyvinfo_len,
+ &dest->partyvinfo , &dest->partyvinfo_len)
+ || !ossl_prov_memdup(src->supp_pubinfo, src->supp_pubinfo_len,
+ &dest->supp_pubinfo,
+ &dest->supp_pubinfo_len)
+ || !ossl_prov_memdup(src->supp_privinfo, src->supp_privinfo_len,
+ &dest->supp_privinfo,
+ &dest->supp_privinfo_len)
+ || !ossl_prov_digest_copy(&dest->digest, &src->digest))
+ goto err;
+ dest->cek_oid = src->cek_oid;
+ dest->cek_oid_len = src->cek_oid_len;
+ dest->dkm_len = src->dkm_len;
+ dest->use_keybits = src->use_keybits;
+ OSSL_FIPS_IND_COPY(dest, src)
+ }
+ return dest;
+
+ err:
+ x942kdf_free(dest);
+ return NULL;
+}
+
+static int x942kdf_set_buffer(unsigned char **out, size_t *out_len,
+ const OSSL_PARAM *p)
+{
+ if (p->data_size == 0 || p->data == NULL)
+ return 1;
+
+ OPENSSL_free(*out);
+ *out = NULL;
+ return OSSL_PARAM_get_octet_string(p, (void **)out, 0, out_len);
+}
+
+static size_t x942kdf_size(KDF_X942 *ctx)
+{
+ int len;
+ const EVP_MD *md = ossl_prov_digest_md(&ctx->digest);
+
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ len = EVP_MD_get_size(md);
+ return (len <= 0) ? 0 : (size_t)len;
+}
+
+#ifdef FIPS_MODULE
+static int fips_x942kdf_key_check_passed(KDF_X942 *ctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ int key_approved = ossl_kdf_check_key_size(ctx->secret_len);
+
+ if (!key_approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "X942KDF", "Key size",
+ ossl_fips_config_x942kdf_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int x942kdf_derive(void *vctx, unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ KDF_X942 *ctx = (KDF_X942 *)vctx;
+ const EVP_MD *md;
+ int ret = 0;
+ unsigned char *ctr;
+ unsigned char *der = NULL;
+ size_t der_len = 0;
+
+ if (!ossl_prov_is_running() || !x942kdf_set_ctx_params(ctx, params))
+ return 0;
+
+ /*
+ * These 2 options encode to the same field so only one of them should be
+ * active at once.
+ */
+ if (ctx->use_keybits && ctx->supp_pubinfo != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PUBINFO);
+ return 0;
+ }
+ /*
+ * If the blob of acvp data is used then the individual info fields that it
+ * replaces should not also be defined.
+ */
+ if (ctx->acvpinfo != NULL
+ && (ctx->partyuinfo != NULL
+ || ctx->partyvinfo != NULL
+ || ctx->supp_pubinfo != NULL
+ || ctx->supp_privinfo != NULL)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DATA);
+ return 0;
+ }
+ if (ctx->secret == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_SECRET);
+ return 0;
+ }
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (md == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MESSAGE_DIGEST);
+ return 0;
+ }
+ if (ctx->cek_oid == NULL || ctx->cek_oid_len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CEK_ALG);
+ return 0;
+ }
+ if (ctx->partyuinfo != NULL && ctx->partyuinfo_len >= X942KDF_MAX_INLEN) {
+ /*
+ * Note the ukm length MUST be 512 bits if it is used.
+ * For backwards compatibility the old check is being done.
+ */
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_UKM_LENGTH);
+ return 0;
+ }
+ /* generate the otherinfo der */
+ if (!x942_encode_otherinfo(ctx->use_keybits ? ctx->dkm_len : 0,
+ ctx->cek_oid, ctx->cek_oid_len,
+ ctx->acvpinfo, ctx->acvpinfo_len,
+ ctx->partyuinfo, ctx->partyuinfo_len,
+ ctx->partyvinfo, ctx->partyvinfo_len,
+ ctx->supp_pubinfo, ctx->supp_pubinfo_len,
+ ctx->supp_privinfo, ctx->supp_privinfo_len,
+ &der, &der_len, &ctr)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_ENCODING);
+ return 0;
+ }
+ ret = x942kdf_hash_kdm(md, ctx->secret, ctx->secret_len,
+ der, der_len, ctr, key, keylen);
+ OPENSSL_free(der);
+ return ret;
+}
+
+static int x942kdf_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p, *pq;
+ KDF_X942 *ctx = vctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(ctx->provctx);
+ const char *propq = NULL;
+ const EVP_MD *md;
+ size_t id;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KDF_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST) != NULL) {
+ if (!ossl_prov_digest_load_from_params(&ctx->digest, params, provctx))
+ return 0;
+ md = ossl_prov_digest_md(&ctx->digest);
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_SECRET);
+ if (p == NULL)
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_KEY);
+ if (p != NULL) {
+ if (!x942kdf_set_buffer(&ctx->secret, &ctx->secret_len, p))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!fips_x942kdf_key_check_passed(ctx))
+ return 0;
+#endif
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_ACVPINFO);
+ if (p != NULL
+ && !x942kdf_set_buffer(&ctx->acvpinfo, &ctx->acvpinfo_len, p))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_PARTYUINFO);
+ if (p == NULL)
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_UKM);
+ if (p != NULL
+ && !x942kdf_set_buffer(&ctx->partyuinfo, &ctx->partyuinfo_len, p))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_PARTYVINFO);
+ if (p != NULL
+ && !x942kdf_set_buffer(&ctx->partyvinfo, &ctx->partyvinfo_len, p))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_USE_KEYBITS);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->use_keybits))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_SUPP_PUBINFO);
+ if (p != NULL) {
+ if (!x942kdf_set_buffer(&ctx->supp_pubinfo, &ctx->supp_pubinfo_len, p))
+ return 0;
+ ctx->use_keybits = 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_X942_SUPP_PRIVINFO);
+ if (p != NULL
+ && !x942kdf_set_buffer(&ctx->supp_privinfo, &ctx->supp_privinfo_len, p))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_CEK_ALG);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ pq = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_PROPERTIES);
+ /*
+ * We already grab the properties during ossl_prov_digest_load_from_params()
+ * so there is no need to check the validity again..
+ */
+ if (pq != NULL)
+ propq = p->data;
+ if (find_alg_id(provctx, p->data, propq, &id) == 0)
+ return 0;
+ ctx->cek_oid = kek_algs[id].oid;
+ ctx->cek_oid_len = kek_algs[id].oid_len;
+ ctx->dkm_len = kek_algs[id].keklen;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *x942kdf_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_SECRET, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_UKM, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_ACVPINFO, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_PARTYUINFO, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_PARTYVINFO, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_SUPP_PUBINFO, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KDF_PARAM_X942_SUPP_PRIVINFO, NULL, 0),
+ OSSL_PARAM_int(OSSL_KDF_PARAM_X942_USE_KEYBITS, NULL),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_CEK_ALG, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KDF_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int x942kdf_get_ctx_params(void *vctx, OSSL_PARAM params[])
+{
+ KDF_X942 *ctx = (KDF_X942 *)vctx;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_KDF_PARAM_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, x942kdf_size(ctx)))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *x942kdf_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_KDF_PARAM_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_kdf_x942_kdf_functions[] = {
+ { OSSL_FUNC_KDF_NEWCTX, (void(*)(void))x942kdf_new },
+ { OSSL_FUNC_KDF_DUPCTX, (void(*)(void))x942kdf_dup },
+ { OSSL_FUNC_KDF_FREECTX, (void(*)(void))x942kdf_free },
+ { OSSL_FUNC_KDF_RESET, (void(*)(void))x942kdf_reset },
+ { OSSL_FUNC_KDF_DERIVE, (void(*)(void))x942kdf_derive },
+ { OSSL_FUNC_KDF_SETTABLE_CTX_PARAMS,
+ (void(*)(void))x942kdf_settable_ctx_params },
+ { OSSL_FUNC_KDF_SET_CTX_PARAMS, (void(*)(void))x942kdf_set_ctx_params },
+ { OSSL_FUNC_KDF_GETTABLE_CTX_PARAMS,
+ (void(*)(void))x942kdf_gettable_ctx_params },
+ { OSSL_FUNC_KDF_GET_CTX_PARAMS, (void(*)(void))x942kdf_get_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/build.info b/crypto/openssl/providers/implementations/kem/build.info
new file mode 100644
index 000000000000..05b8b185127d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/build.info
@@ -0,0 +1,26 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$RSA_KEM_GOAL=../../libdefault.a ../../libfips.a
+$EC_KEM_GOAL=../../libdefault.a
+$TEMPLATE_KEM_GOAL=../../libtemplate.a
+$ML_KEM_GOAL=../../libdefault.a ../../libfips.a
+$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a
+
+SOURCE[$RSA_KEM_GOAL]=rsa_kem.c
+
+IF[{- !$disabled{ec} -}]
+ SOURCE[$EC_KEM_GOAL]=kem_util.c ec_kem.c
+ IF[{- !$disabled{ecx} -}]
+ SOURCE[$EC_KEM_GOAL]=ecx_kem.c
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{'ml-kem'} -}]
+ IF[{- !$disabled{ec} -}]
+ SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kem.c
+ ENDIF
+ SOURCE[$ML_KEM_GOAL] = ml_kem_kem.c
+ENDIF
+
+SOURCE[$TEMPLATE_KEM_GOAL]=template_kem.c
diff --git a/crypto/openssl/providers/implementations/kem/ec_kem.c b/crypto/openssl/providers/implementations/kem/ec_kem.c
new file mode 100644
index 000000000000..975f41144ad0
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/ec_kem.c
@@ -0,0 +1,815 @@
+/*
+ * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * The following implementation is part of RFC 9180 related to DHKEM using
+ * EC keys (i.e. P-256, P-384 and P-521)
+ * References to Sections in the comments below refer to RFC 9180.
+ */
+
+#include "internal/deprecated.h"
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/ec.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/kdf.h>
+#include <openssl/rand.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+
+#include <openssl/hpke.h>
+#include "internal/hpke_util.h"
+#include "crypto/ec.h"
+#include "prov/ecx.h"
+#include "eckem.h"
+
+typedef struct {
+ EC_KEY *recipient_key;
+ EC_KEY *sender_authkey;
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ unsigned int mode;
+ unsigned int op;
+ unsigned char *ikm;
+ size_t ikmlen;
+ const char *kdfname;
+ const OSSL_HPKE_KEM_INFO *info;
+} PROV_EC_CTX;
+
+static OSSL_FUNC_kem_newctx_fn eckem_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn eckem_encapsulate_init;
+static OSSL_FUNC_kem_auth_encapsulate_init_fn eckem_auth_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn eckem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn eckem_decapsulate_init;
+static OSSL_FUNC_kem_auth_decapsulate_init_fn eckem_auth_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn eckem_decapsulate;
+static OSSL_FUNC_kem_freectx_fn eckem_freectx;
+static OSSL_FUNC_kem_set_ctx_params_fn eckem_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn eckem_settable_ctx_params;
+
+/* ASCII: "KEM", in hex for EBCDIC compatibility */
+static const char LABEL_KEM[] = "\x4b\x45\x4d";
+
+static int eckey_check(const EC_KEY *ec, int requires_privatekey)
+{
+ int rv = 0;
+ BN_CTX *bnctx = NULL;
+ BIGNUM *rem = NULL;
+ const BIGNUM *priv = EC_KEY_get0_private_key(ec);
+ const EC_POINT *pub = EC_KEY_get0_public_key(ec);
+
+ /* Keys always require a public component */
+ if (pub == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ if (priv == NULL) {
+ return (requires_privatekey == 0);
+ } else {
+ /* If there is a private key, check that is non zero (mod order) */
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+ const BIGNUM *order = EC_GROUP_get0_order(group);
+
+ bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
+ rem = BN_new();
+
+ if (order != NULL && rem != NULL && bnctx != NULL) {
+ rv = BN_mod(rem, priv, order, bnctx)
+ && !BN_is_zero(rem);
+ }
+ }
+ BN_free(rem);
+ BN_CTX_free(bnctx);
+ return rv;
+}
+
+/* Returns NULL if the curve is not supported */
+static const char *ec_curvename_get0(const EC_KEY *ec)
+{
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+
+ return EC_curve_nid2nist(EC_GROUP_get_curve_name(group));
+}
+
+/*
+ * Set the recipient key, and free any existing key.
+ * ec can be NULL.
+ * The ec key may have only a private or public component
+ * (but it must have a group).
+ */
+static int recipient_key_set(PROV_EC_CTX *ctx, EC_KEY *ec)
+{
+ EC_KEY_free(ctx->recipient_key);
+ ctx->recipient_key = NULL;
+
+ if (ec != NULL) {
+ const char *curve = ec_curvename_get0(ec);
+
+ if (curve == NULL)
+ return -2;
+ ctx->info = ossl_HPKE_KEM_INFO_find_curve(curve);
+ if (ctx->info == NULL)
+ return -2;
+ if (!EC_KEY_up_ref(ec))
+ return 0;
+ ctx->recipient_key = ec;
+ ctx->kdfname = "HKDF";
+ }
+ return 1;
+}
+
+/*
+ * Set the senders auth key, and free any existing auth key.
+ * ec can be NULL.
+ */
+static int sender_authkey_set(PROV_EC_CTX *ctx, EC_KEY *ec)
+{
+ EC_KEY_free(ctx->sender_authkey);
+ ctx->sender_authkey = NULL;
+
+ if (ec != NULL) {
+ if (!EC_KEY_up_ref(ec))
+ return 0;
+ ctx->sender_authkey = ec;
+ }
+ return 1;
+}
+
+/*
+ * Serializes a encoded public key buffer into a EC public key.
+ * Params:
+ * in Contains the group.
+ * pubbuf The encoded public key buffer
+ * Returns: The created public EC key, or NULL if there is an error.
+ */
+static EC_KEY *eckey_frompub(EC_KEY *in,
+ const unsigned char *pubbuf, size_t pubbuflen)
+{
+ EC_KEY *key;
+
+ key = EC_KEY_new_ex(ossl_ec_key_get_libctx(in), ossl_ec_key_get0_propq(in));
+ if (key == NULL)
+ goto err;
+ if (!EC_KEY_set_group(key, EC_KEY_get0_group(in)))
+ goto err;
+ if (!EC_KEY_oct2key(key, pubbuf, pubbuflen, NULL))
+ goto err;
+ return key;
+err:
+ EC_KEY_free(key);
+ return NULL;
+}
+
+/*
+ * Deserialises a EC public key into a encoded byte array.
+ * Returns: 1 if successful or 0 otherwise.
+ */
+static int ecpubkey_todata(const EC_KEY *ec, unsigned char *out, size_t *outlen,
+ size_t maxoutlen)
+{
+ const EC_POINT *pub;
+ const EC_GROUP *group;
+
+ group = EC_KEY_get0_group(ec);
+ pub = EC_KEY_get0_public_key(ec);
+ *outlen = EC_POINT_point2oct(group, pub, POINT_CONVERSION_UNCOMPRESSED,
+ out, maxoutlen, NULL);
+ return *outlen != 0;
+}
+
+static void *eckem_newctx(void *provctx)
+{
+ PROV_EC_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_EC_CTX));
+
+ if (ctx == NULL)
+ return NULL;
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->mode = KEM_MODE_DHKEM;
+
+ return ctx;
+}
+
+static void eckem_freectx(void *vectx)
+{
+ PROV_EC_CTX *ctx = (PROV_EC_CTX *)vectx;
+
+ OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+ recipient_key_set(ctx, NULL);
+ sender_authkey_set(ctx, NULL);
+ OPENSSL_free(ctx);
+}
+
+static int ossl_ec_match_params(const EC_KEY *key1, const EC_KEY *key2)
+{
+ int ret;
+ BN_CTX *ctx = NULL;
+ const EC_GROUP *group1 = EC_KEY_get0_group(key1);
+ const EC_GROUP *group2 = EC_KEY_get0_group(key2);
+
+ ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key1));
+ if (ctx == NULL)
+ return 0;
+
+ ret = group1 != NULL
+ && group2 != NULL
+ && EC_GROUP_cmp(group1, group2, ctx) == 0;
+ if (!ret)
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISMATCHING_DOMAIN_PARAMETERS);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+static int eckem_init(void *vctx, int operation, void *vec, void *vauth,
+ const OSSL_PARAM params[])
+{
+ int rv;
+ PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+ EC_KEY *ec = vec;
+ EC_KEY *auth = vauth;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!eckey_check(ec, operation == EVP_PKEY_OP_DECAPSULATE))
+ return 0;
+ rv = recipient_key_set(ctx, ec);
+ if (rv <= 0)
+ return rv;
+
+ if (auth != NULL) {
+ if (!ossl_ec_match_params(ec, auth)
+ || !eckey_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
+ || !sender_authkey_set(ctx, auth))
+ return 0;
+ }
+
+ ctx->op = operation;
+ return eckem_set_ctx_params(vctx, params);
+}
+
+static int eckem_encapsulate_init(void *vctx, void *vec,
+ const OSSL_PARAM params[])
+{
+ return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vec, NULL, params);
+}
+
+static int eckem_decapsulate_init(void *vctx, void *vec,
+ const OSSL_PARAM params[])
+{
+ return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vec, NULL, params);
+}
+
+static int eckem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
+ const OSSL_PARAM params[])
+{
+ return eckem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
+}
+
+static int eckem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
+ const OSSL_PARAM params[])
+{
+ return eckem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
+}
+
+static int eckem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+ const OSSL_PARAM *p;
+ int mode;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
+ if (p != NULL) {
+ void *tmp = NULL;
+ size_t tmplen = 0;
+
+ if (p->data != NULL && p->data_size != 0) {
+ if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
+ return 0;
+ }
+ OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+ /* Set the ephemeral seed */
+ ctx->ikm = tmp;
+ ctx->ikmlen = tmplen;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ mode = ossl_eckem_modename2id(p->data);
+ if (mode == KEM_MODE_UNDEFINED)
+ return 0;
+ ctx->mode = mode;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_eckem_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *eckem_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_eckem_ctx_params;
+}
+
+/*
+ * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
+ */
+static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
+ unsigned char *okm, size_t okmlen,
+ uint16_t kemid,
+ const unsigned char *dhkm, size_t dhkmlen,
+ const unsigned char *kemctx,
+ size_t kemctxlen)
+{
+ uint8_t suiteid[2];
+ uint8_t prk[EVP_MAX_MD_SIZE];
+ size_t prklen = okmlen;
+ int ret;
+
+ if (prklen > sizeof(prk))
+ return 0;
+
+ suiteid[0] = (kemid >> 8) & 0xff;
+ suiteid[1] = kemid & 0xff;
+
+ ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
+ && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
+ LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_SHARED_SECRET,
+ kemctx, kemctxlen);
+ OPENSSL_cleanse(prk, prklen);
+ return ret;
+}
+
+/*
+ * See Section 7.1.3 DeriveKeyPair.
+ *
+ * This function is used by ec keygen.
+ * (For this reason it does not use any of the state stored in PROV_EC_CTX).
+ *
+ * Params:
+ * ec An initialized ec key.
+ * priv The buffer to store the generated private key into (it is assumed
+ * this is of length alg->encodedprivlen).
+ * ikm buffer containing the input key material (seed). This must be set.
+ * ikmlen size of the ikm buffer in bytes
+ * Returns:
+ * 1 if successful or 0 otherwise.
+ */
+int ossl_ec_dhkem_derive_private(EC_KEY *ec, BIGNUM *priv,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ret = 0;
+ EVP_KDF_CTX *kdfctx = NULL;
+ uint8_t suiteid[2];
+ unsigned char prk[OSSL_HPKE_MAX_SECRET];
+ unsigned char privbuf[OSSL_HPKE_MAX_PRIVATE];
+ const BIGNUM *order;
+ unsigned char counter = 0;
+ const char *curve = ec_curvename_get0(ec);
+ const OSSL_HPKE_KEM_INFO *info;
+
+ if (curve == NULL)
+ return -2;
+
+ info = ossl_HPKE_KEM_INFO_find_curve(curve);
+ if (info == NULL)
+ return -2;
+
+ kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname,
+ ossl_ec_key_get_libctx(ec),
+ ossl_ec_key_get0_propq(ec));
+ if (kdfctx == NULL)
+ return 0;
+
+ /* ikmlen should have a length of at least Nsk */
+ if (ikmlen < info->Nsk) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
+ "ikm length is :%zu, should be at least %zu",
+ ikmlen, info->Nsk);
+ goto err;
+ }
+
+ suiteid[0] = info->kem_id / 256;
+ suiteid[1] = info->kem_id % 256;
+
+ if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
+ goto err;
+
+ order = EC_GROUP_get0_order(EC_KEY_get0_group(ec));
+ do {
+ if (!ossl_hpke_labeled_expand(kdfctx, privbuf, info->Nsk,
+ prk, info->Nsecret,
+ LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_CANDIDATE,
+ &counter, 1))
+ goto err;
+ privbuf[0] &= info->bitmask;
+ if (BN_bin2bn(privbuf, info->Nsk, priv) == NULL)
+ goto err;
+ if (counter == 0xFF) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ goto err;
+ }
+ counter++;
+ } while (BN_is_zero(priv) || BN_cmp(priv, order) >= 0);
+ ret = 1;
+err:
+ OPENSSL_cleanse(prk, sizeof(prk));
+ OPENSSL_cleanse(privbuf, sizeof(privbuf));
+ EVP_KDF_CTX_free(kdfctx);
+ return ret;
+}
+
+/*
+ * Do a keygen operation without having to use EVP_PKEY.
+ * Params:
+ * ctx Context object
+ * ikm The seed material - if this is NULL, then a random seed is used.
+ * Returns:
+ * The generated EC key, or NULL on failure.
+ */
+static EC_KEY *derivekey(PROV_EC_CTX *ctx,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ret = 0;
+ EC_KEY *key;
+ unsigned char *seed = (unsigned char *)ikm;
+ size_t seedlen = ikmlen;
+ unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
+
+ key = EC_KEY_new_ex(ctx->libctx, ctx->propq);
+ if (key == NULL)
+ goto err;
+ if (!EC_KEY_set_group(key, EC_KEY_get0_group(ctx->recipient_key)))
+ goto err;
+
+ /* Generate a random seed if there is no input ikm */
+ if (seed == NULL || seedlen == 0) {
+ seedlen = ctx->info->Nsk;
+ if (seedlen > sizeof(tmpbuf))
+ goto err;
+ if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, seedlen, 0) <= 0)
+ goto err;
+ seed = tmpbuf;
+ }
+ ret = ossl_ec_generate_key_dhkem(key, seed, seedlen);
+err:
+ if (seed != ikm)
+ OPENSSL_cleanse(seed, seedlen);
+ if (ret <= 0) {
+ EC_KEY_free(key);
+ key = NULL;
+ }
+ return key;
+}
+
+/*
+ * Before doing a key exchange the public key of the peer needs to be checked
+ * Note that the group check is not done here as we have already checked
+ * that it only uses one of the approved curve names when the key was set.
+ *
+ * Returns 1 if the public key is valid, or 0 if it fails.
+ */
+static int check_publickey(const EC_KEY *pub)
+{
+ int ret = 0;
+ BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(pub));
+
+ if (bnctx == NULL)
+ return 0;
+ ret = ossl_ec_key_public_check(pub, bnctx);
+ BN_CTX_free(bnctx);
+
+ return ret;
+}
+
+/*
+ * Do an ecdh key exchange.
+ * dhkm = DH(sender, peer)
+ *
+ * NOTE: Instead of using EVP_PKEY_derive() API's, we use EC_KEY operations
+ * to avoid messy conversions back to EVP_PKEY.
+ *
+ * Returns the size of the secret if successful, or 0 otherwise,
+ */
+static int generate_ecdhkm(const EC_KEY *sender, const EC_KEY *peer,
+ unsigned char *out, size_t maxout,
+ unsigned int secretsz)
+{
+ const EC_GROUP *group = EC_KEY_get0_group(sender);
+ size_t secretlen = (EC_GROUP_get_degree(group) + 7) / 8;
+
+ if (secretlen != secretsz || secretlen > maxout) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "secretsz invalid");
+ return 0;
+ }
+
+ if (!check_publickey(peer))
+ return 0;
+ return ECDH_compute_key(out, secretlen, EC_KEY_get0_public_key(peer),
+ sender, NULL) > 0;
+}
+
+/*
+ * Derive a secret using ECDH (code is shared by the encap and decap)
+ *
+ * dhkm = Concat(ecdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
+ * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
+ * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
+ *
+ * Params:
+ * ctx Object that contains algorithm state and constants.
+ * secret The returned secret (with a length ctx->alg->secretlen bytes).
+ * privkey1 A private key used for ECDH key derivation.
+ * peerkey1 A public key used for ECDH key derivation with privkey1
+ * privkey2 A optional private key used for a second ECDH key derivation.
+ * It can be NULL.
+ * peerkey2 A optional public key used for a second ECDH key derivation
+ * with privkey2,. It can be NULL.
+ * sender_pub The senders public key in encoded form.
+ * recipient_pub The recipients public key in encoded form.
+ * Notes:
+ * The second ecdh() is only used for the HPKE auth modes when both privkey2
+ * and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
+ */
+static int derive_secret(PROV_EC_CTX *ctx, unsigned char *secret,
+ const EC_KEY *privkey1, const EC_KEY *peerkey1,
+ const EC_KEY *privkey2, const EC_KEY *peerkey2,
+ const unsigned char *sender_pub,
+ const unsigned char *recipient_pub)
+{
+ int ret = 0;
+ EVP_KDF_CTX *kdfctx = NULL;
+ unsigned char sender_authpub[OSSL_HPKE_MAX_PUBLIC];
+ unsigned char dhkm[OSSL_HPKE_MAX_PRIVATE * 2];
+ unsigned char kemctx[OSSL_HPKE_MAX_PUBLIC * 3];
+ size_t sender_authpublen;
+ size_t kemctxlen = 0, dhkmlen = 0;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+ size_t encodedpublen = info->Npk;
+ size_t encodedprivlen = info->Nsk;
+ int auth = ctx->sender_authkey != NULL;
+
+ if (!generate_ecdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedprivlen))
+ goto err;
+ dhkmlen = encodedprivlen;
+ kemctxlen = 2 * encodedpublen;
+
+ /* Concat the optional second ECDH (used for Auth) */
+ if (auth) {
+ /* Get the public key of the auth sender in encoded form */
+ if (!ecpubkey_todata(ctx->sender_authkey, sender_authpub,
+ &sender_authpublen, sizeof(sender_authpub)))
+ goto err;
+ if (sender_authpublen != encodedpublen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "Invalid sender auth public key");
+ goto err;
+ }
+ if (!generate_ecdhkm(privkey2, peerkey2,
+ dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
+ encodedprivlen))
+ goto err;
+ dhkmlen += encodedprivlen;
+ kemctxlen += encodedpublen;
+ }
+ if (kemctxlen > sizeof(kemctx))
+ goto err;
+
+ /* kemctx is the concat of both sides encoded public key */
+ memcpy(kemctx, sender_pub, info->Npk);
+ memcpy(kemctx + info->Npk, recipient_pub, info->Npk);
+ if (auth)
+ memcpy(kemctx + 2 * encodedpublen, sender_authpub, encodedpublen);
+ kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
+ ctx->libctx, ctx->propq);
+ if (kdfctx == NULL)
+ goto err;
+ if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
+ info->kem_id, dhkm, dhkmlen,
+ kemctx, kemctxlen))
+ goto err;
+ ret = 1;
+err:
+ OPENSSL_cleanse(dhkm, dhkmlen);
+ EVP_KDF_CTX_free(kdfctx);
+ return ret;
+}
+
+/*
+ * Do a DHKEM encapsulate operation.
+ *
+ * See Section 4.1 Encap() and AuthEncap()
+ *
+ * Params:
+ * ctx A context object holding the recipients public key and the
+ * optional senders auth private key.
+ * enc A buffer to return the senders ephemeral public key.
+ * Setting this to NULL allows the enclen and secretlen to return
+ * values, without calculating the secret.
+ * enclen Passes in the max size of the enc buffer and returns the
+ * encoded public key length.
+ * secret A buffer to return the calculated shared secret.
+ * secretlen Passes in the max size of the secret buffer and returns the
+ * secret length.
+ * Returns: 1 on success or 0 otherwise.
+ */
+static int dhkem_encap(PROV_EC_CTX *ctx,
+ unsigned char *enc, size_t *enclen,
+ unsigned char *secret, size_t *secretlen)
+{
+ int ret = 0;
+ EC_KEY *sender_ephemkey = NULL;
+ unsigned char sender_pub[OSSL_HPKE_MAX_PUBLIC];
+ unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
+ size_t sender_publen, recipient_publen;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+
+ if (enc == NULL) {
+ if (enclen == NULL && secretlen == NULL)
+ return 0;
+ if (enclen != NULL)
+ *enclen = info->Nenc;
+ if (secretlen != NULL)
+ *secretlen = info->Nsecret;
+ return 1;
+ }
+
+ if (*secretlen < info->Nsecret) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+ return 0;
+ }
+ if (*enclen < info->Nenc) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
+ return 0;
+ }
+
+ /* Create an ephemeral key */
+ sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
+ if (sender_ephemkey == NULL)
+ goto err;
+ if (!ecpubkey_todata(sender_ephemkey, sender_pub, &sender_publen,
+ sizeof(sender_pub))
+ || !ecpubkey_todata(ctx->recipient_key, recipient_pub,
+ &recipient_publen, sizeof(recipient_pub)))
+ goto err;
+
+ if (sender_publen != info->Npk
+ || recipient_publen != sender_publen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid public key");
+ goto err;
+ }
+
+ if (!derive_secret(ctx, secret,
+ sender_ephemkey, ctx->recipient_key,
+ ctx->sender_authkey, ctx->recipient_key,
+ sender_pub, recipient_pub))
+ goto err;
+
+ /* Return the senders ephemeral public key in encoded form */
+ memcpy(enc, sender_pub, sender_publen);
+ *enclen = sender_publen;
+ *secretlen = info->Nsecret;
+ ret = 1;
+err:
+ EC_KEY_free(sender_ephemkey);
+ return ret;
+}
+
+/*
+ * Do a DHKEM decapsulate operation.
+ * See Section 4.1 Decap() and Auth Decap()
+ *
+ * Params:
+ * ctx A context object holding the recipients private key and the
+ * optional senders auth public key.
+ * secret A buffer to return the calculated shared secret. Setting this to
+ * NULL can be used to return the secretlen.
+ * secretlen Passes in the max size of the secret buffer and returns the
+ * secret length.
+ * enc A buffer containing the senders ephemeral public key that was returned
+ * from dhkem_encap().
+ * enclen The length in bytes of enc.
+ * Returns: 1 If the shared secret is returned or 0 on error.
+ */
+static int dhkem_decap(PROV_EC_CTX *ctx,
+ unsigned char *secret, size_t *secretlen,
+ const unsigned char *enc, size_t enclen)
+{
+ int ret = 0;
+ EC_KEY *sender_ephempubkey = NULL;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+ unsigned char recipient_pub[OSSL_HPKE_MAX_PUBLIC];
+ size_t recipient_publen;
+ size_t encodedpublen = info->Npk;
+
+ if (secret == NULL) {
+ *secretlen = info->Nsecret;
+ return 1;
+ }
+
+ if (*secretlen < info->Nsecret) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+ return 0;
+ }
+ if (enclen != encodedpublen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
+ return 0;
+ }
+
+ sender_ephempubkey = eckey_frompub(ctx->recipient_key, enc, enclen);
+ if (sender_ephempubkey == NULL)
+ goto err;
+ if (!ecpubkey_todata(ctx->recipient_key, recipient_pub, &recipient_publen,
+ sizeof(recipient_pub)))
+ goto err;
+ if (recipient_publen != encodedpublen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid recipient public key");
+ goto err;
+ }
+
+ if (!derive_secret(ctx, secret,
+ ctx->recipient_key, sender_ephempubkey,
+ ctx->recipient_key, ctx->sender_authkey,
+ enc, recipient_pub))
+ goto err;
+ *secretlen = info->Nsecret;
+ ret = 1;
+err:
+ EC_KEY_free(sender_ephempubkey);
+ return ret;
+}
+
+static int eckem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ unsigned char *secret, size_t *secretlen)
+{
+ PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+
+ switch (ctx->mode) {
+ case KEM_MODE_DHKEM:
+ return dhkem_encap(ctx, out, outlen, secret, secretlen);
+ default:
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return -2;
+ }
+}
+
+static int eckem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ PROV_EC_CTX *ctx = (PROV_EC_CTX *)vctx;
+
+ switch (ctx->mode) {
+ case KEM_MODE_DHKEM:
+ return dhkem_decap(ctx, out, outlen, in, inlen);
+ default:
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return -2;
+ }
+}
+
+const OSSL_DISPATCH ossl_ec_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))eckem_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+ (void (*)(void))eckem_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))eckem_encapsulate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+ (void (*)(void))eckem_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))eckem_decapsulate },
+ { OSSL_FUNC_KEM_FREECTX, (void (*)(void))eckem_freectx },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+ (void (*)(void))eckem_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+ (void (*)(void))eckem_settable_ctx_params },
+ { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
+ (void (*)(void))eckem_auth_encapsulate_init },
+ { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
+ (void (*)(void))eckem_auth_decapsulate_init },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/eckem.h b/crypto/openssl/providers/implementations/kem/eckem.h
new file mode 100644
index 000000000000..2e46a0f2ffab
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/eckem.h
@@ -0,0 +1,13 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#define KEM_MODE_UNDEFINED 0
+#define KEM_MODE_DHKEM 1
+
+int ossl_eckem_modename2id(const char *name);
diff --git a/crypto/openssl/providers/implementations/kem/ecx_kem.c b/crypto/openssl/providers/implementations/kem/ecx_kem.c
new file mode 100644
index 000000000000..823662dd37c3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/ecx_kem.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * The following implementation is part of RFC 9180 related to DHKEM using
+ * ECX keys (i.e. X25519 and X448)
+ * References to Sections in the comments below refer to RFC 9180.
+ */
+
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/kdf.h>
+#include <openssl/err.h>
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+#include "prov/ecx.h"
+#include "crypto/ecx.h"
+#include <openssl/hpke.h>
+#include "internal/hpke_util.h"
+#include "eckem.h"
+
+#define MAX_ECX_KEYLEN X448_KEYLEN
+
+/* KEM identifiers from Section 7.1 "Table 2 KEM IDs" */
+#define KEMID_X25519_HKDF_SHA256 0x20
+#define KEMID_X448_HKDF_SHA512 0x21
+
+/* ASCII: "KEM", in hex for EBCDIC compatibility */
+static const char LABEL_KEM[] = "\x4b\x45\x4d";
+
+typedef struct {
+ ECX_KEY *recipient_key;
+ ECX_KEY *sender_authkey;
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ unsigned int mode;
+ unsigned int op;
+ unsigned char *ikm;
+ size_t ikmlen;
+ const char *kdfname;
+ const OSSL_HPKE_KEM_INFO *info;
+} PROV_ECX_CTX;
+
+static OSSL_FUNC_kem_newctx_fn ecxkem_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn ecxkem_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn ecxkem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn ecxkem_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn ecxkem_decapsulate;
+static OSSL_FUNC_kem_freectx_fn ecxkem_freectx;
+static OSSL_FUNC_kem_set_ctx_params_fn ecxkem_set_ctx_params;
+static OSSL_FUNC_kem_auth_encapsulate_init_fn ecxkem_auth_encapsulate_init;
+static OSSL_FUNC_kem_auth_decapsulate_init_fn ecxkem_auth_decapsulate_init;
+
+/*
+ * Set KEM values as specified in Section 7.1 "Table 2 KEM IDs"
+ * There is only one set of values for X25519 and X448.
+ * Additional values could be set via set_params if required.
+ */
+static const OSSL_HPKE_KEM_INFO *get_kem_info(ECX_KEY *ecx)
+{
+ const char *name = NULL;
+
+ if (ecx->type == ECX_KEY_TYPE_X25519)
+ name = SN_X25519;
+ else
+ name = SN_X448;
+ return ossl_HPKE_KEM_INFO_find_curve(name);
+}
+
+/*
+ * Set the recipient key, and free any existing key.
+ * ecx can be NULL. The ecx key may have only a private or public component.
+ */
+static int recipient_key_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
+{
+ ossl_ecx_key_free(ctx->recipient_key);
+ ctx->recipient_key = NULL;
+ if (ecx != NULL) {
+ ctx->info = get_kem_info(ecx);
+ if (ctx->info == NULL)
+ return -2;
+ ctx->kdfname = "HKDF";
+ if (!ossl_ecx_key_up_ref(ecx))
+ return 0;
+ ctx->recipient_key = ecx;
+ }
+ return 1;
+}
+
+/*
+ * Set the senders auth key, and free any existing auth key.
+ * ecx can be NULL.
+ */
+static int sender_authkey_set(PROV_ECX_CTX *ctx, ECX_KEY *ecx)
+{
+ ossl_ecx_key_free(ctx->sender_authkey);
+ ctx->sender_authkey = NULL;
+
+ if (ecx != NULL) {
+ if (!ossl_ecx_key_up_ref(ecx))
+ return 0;
+ ctx->sender_authkey = ecx;
+ }
+ return 1;
+}
+
+/*
+ * Serialize a public key from byte array's for the encoded public keys.
+ * ctx is used to access the key type.
+ * Returns: The created ECX_KEY or NULL on error.
+ */
+static ECX_KEY *ecxkey_pubfromdata(PROV_ECX_CTX *ctx,
+ const unsigned char *pubbuf, size_t pubbuflen)
+{
+ ECX_KEY *ecx = NULL;
+ OSSL_PARAM params[2], *p = params;
+
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+ (char *)pubbuf, pubbuflen);
+ *p = OSSL_PARAM_construct_end();
+
+ ecx = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 1, ctx->propq);
+ if (ecx == NULL)
+ return NULL;
+ if (ossl_ecx_key_fromdata(ecx, params, 0) <= 0) {
+ ossl_ecx_key_free(ecx);
+ ecx = NULL;
+ }
+ return ecx;
+}
+
+static unsigned char *ecx_pubkey(ECX_KEY *ecx)
+{
+ if (ecx == NULL || !ecx->haspubkey) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ return 0;
+ }
+ return ecx->pubkey;
+}
+
+static void *ecxkem_newctx(void *provctx)
+{
+ PROV_ECX_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_ECX_CTX));
+
+ if (ctx == NULL)
+ return NULL;
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->mode = KEM_MODE_DHKEM;
+
+ return ctx;
+}
+
+static void ecxkem_freectx(void *vectx)
+{
+ PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vectx;
+
+ OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+ recipient_key_set(ctx, NULL);
+ sender_authkey_set(ctx, NULL);
+ OPENSSL_free(ctx);
+}
+
+static int ecx_match_params(const ECX_KEY *key1, const ECX_KEY *key2)
+{
+ return (key1->type == key2->type && key1->keylen == key2->keylen);
+}
+
+static int ecx_key_check(const ECX_KEY *ecx, int requires_privatekey)
+{
+ if (ecx->privkey == NULL)
+ return (requires_privatekey == 0);
+ return 1;
+}
+
+static int ecxkem_init(void *vecxctx, int operation, void *vecx, void *vauth,
+ ossl_unused const OSSL_PARAM params[])
+{
+ int rv;
+ PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vecxctx;
+ ECX_KEY *ecx = vecx;
+ ECX_KEY *auth = vauth;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ecx_key_check(ecx, operation == EVP_PKEY_OP_DECAPSULATE))
+ return 0;
+ rv = recipient_key_set(ctx, ecx);
+ if (rv <= 0)
+ return rv;
+
+ if (auth != NULL) {
+ if (!ecx_match_params(auth, ctx->recipient_key)
+ || !ecx_key_check(auth, operation == EVP_PKEY_OP_ENCAPSULATE)
+ || !sender_authkey_set(ctx, auth))
+ return 0;
+ }
+
+ ctx->op = operation;
+ return ecxkem_set_ctx_params(vecxctx, params);
+}
+
+static int ecxkem_encapsulate_init(void *vecxctx, void *vecx,
+ const OSSL_PARAM params[])
+{
+ return ecxkem_init(vecxctx, EVP_PKEY_OP_ENCAPSULATE, vecx, NULL, params);
+}
+
+static int ecxkem_decapsulate_init(void *vecxctx, void *vecx,
+ const OSSL_PARAM params[])
+{
+ return ecxkem_init(vecxctx, EVP_PKEY_OP_DECAPSULATE, vecx, NULL, params);
+}
+
+static int ecxkem_auth_encapsulate_init(void *vctx, void *vecx, void *vauthpriv,
+ const OSSL_PARAM params[])
+{
+ return ecxkem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vecx, vauthpriv, params);
+}
+
+static int ecxkem_auth_decapsulate_init(void *vctx, void *vecx, void *vauthpub,
+ const OSSL_PARAM params[])
+{
+ return ecxkem_init(vctx, EVP_PKEY_OP_DECAPSULATE, vecx, vauthpub, params);
+}
+
+static int ecxkem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+ const OSSL_PARAM *p;
+ int mode;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME);
+ if (p != NULL) {
+ void *tmp = NULL;
+ size_t tmplen = 0;
+
+ if (p->data != NULL && p->data_size != 0) {
+ if (!OSSL_PARAM_get_octet_string(p, &tmp, 0, &tmplen))
+ return 0;
+ }
+ OPENSSL_clear_free(ctx->ikm, ctx->ikmlen);
+ ctx->ikm = tmp;
+ ctx->ikmlen = tmplen;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ mode = ossl_eckem_modename2id(p->data);
+ if (mode == KEM_MODE_UNDEFINED)
+ return 0;
+ ctx->mode = mode;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ecxkem_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ecxkem_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ecxkem_ctx_params;
+}
+
+/*
+ * See Section 4.1 DH-Based KEM (DHKEM) ExtractAndExpand
+ */
+static int dhkem_extract_and_expand(EVP_KDF_CTX *kctx,
+ unsigned char *okm, size_t okmlen,
+ uint16_t kemid,
+ const unsigned char *dhkm, size_t dhkmlen,
+ const unsigned char *kemctx,
+ size_t kemctxlen)
+{
+ uint8_t suiteid[2];
+ uint8_t prk[EVP_MAX_MD_SIZE];
+ size_t prklen = okmlen; /* Nh */
+ int ret;
+
+ if (prklen > sizeof(prk))
+ return 0;
+
+ suiteid[0] = (kemid >> 8) &0xff;
+ suiteid[1] = kemid & 0xff;
+
+ ret = ossl_hpke_labeled_extract(kctx, prk, prklen,
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_EAE_PRK, dhkm, dhkmlen)
+ && ossl_hpke_labeled_expand(kctx, okm, okmlen, prk, prklen,
+ LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_SHARED_SECRET,
+ kemctx, kemctxlen);
+ OPENSSL_cleanse(prk, prklen);
+ return ret;
+}
+
+/*
+ * See Section 7.1.3 DeriveKeyPair.
+ *
+ * This function is used by ecx keygen.
+ * (For this reason it does not use any of the state stored in PROV_ECX_CTX).
+ *
+ * Params:
+ * ecx An initialized ecx key.
+ * privout The buffer to store the generated private key into (it is assumed
+ * this is of length ecx->keylen).
+ * ikm buffer containing the input key material (seed). This must be non NULL.
+ * ikmlen size of the ikm buffer in bytes
+ * Returns:
+ * 1 if successful or 0 otherwise.
+ */
+int ossl_ecx_dhkem_derive_private(ECX_KEY *ecx, unsigned char *privout,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ret = 0;
+ EVP_KDF_CTX *kdfctx = NULL;
+ unsigned char prk[EVP_MAX_MD_SIZE];
+ uint8_t suiteid[2];
+ const OSSL_HPKE_KEM_INFO *info = get_kem_info(ecx);
+
+ /* ikmlen should have a length of at least Nsk */
+ if (ikmlen < info->Nsk) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_INPUT_LENGTH,
+ "ikm length is :%zu, should be at least %zu",
+ ikmlen, info->Nsk);
+ goto err;
+ }
+
+ kdfctx = ossl_kdf_ctx_create("HKDF", info->mdname, ecx->libctx, ecx->propq);
+ if (kdfctx == NULL)
+ return 0;
+
+ suiteid[0] = info->kem_id / 256;
+ suiteid[1] = info->kem_id % 256;
+
+ if (!ossl_hpke_labeled_extract(kdfctx, prk, info->Nsecret,
+ NULL, 0, LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_DKP_PRK, ikm, ikmlen))
+ goto err;
+
+ if (!ossl_hpke_labeled_expand(kdfctx, privout, info->Nsk, prk, info->Nsecret,
+ LABEL_KEM, suiteid, sizeof(suiteid),
+ OSSL_DHKEM_LABEL_SK, NULL, 0))
+ goto err;
+ ret = 1;
+err:
+ OPENSSL_cleanse(prk, sizeof(prk));
+ EVP_KDF_CTX_free(kdfctx);
+ return ret;
+}
+
+/*
+ * Do a keygen operation without having to use EVP_PKEY.
+ * Params:
+ * ctx Context object
+ * ikm The seed material - if this is NULL, then a random seed is used.
+ * Returns:
+ * The generated ECX key, or NULL on failure.
+ */
+static ECX_KEY *derivekey(PROV_ECX_CTX *ctx,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ok = 0;
+ ECX_KEY *key;
+ unsigned char *privkey;
+ unsigned char *seed = (unsigned char *)ikm;
+ size_t seedlen = ikmlen;
+ unsigned char tmpbuf[OSSL_HPKE_MAX_PRIVATE];
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+
+ key = ossl_ecx_key_new(ctx->libctx, ctx->recipient_key->type, 0, ctx->propq);
+ if (key == NULL)
+ return NULL;
+ privkey = ossl_ecx_key_allocate_privkey(key);
+ if (privkey == NULL)
+ goto err;
+
+ /* Generate a random seed if there is no input ikm */
+ if (seed == NULL || seedlen == 0) {
+ if (info->Nsk > sizeof(tmpbuf))
+ goto err;
+ if (RAND_priv_bytes_ex(ctx->libctx, tmpbuf, info->Nsk, 0) <= 0)
+ goto err;
+ seed = tmpbuf;
+ seedlen = info->Nsk;
+ }
+ if (!ossl_ecx_dhkem_derive_private(key, privkey, seed, seedlen))
+ goto err;
+ if (!ossl_ecx_public_from_private(key))
+ goto err;
+ key->haspubkey = 1;
+ ok = 1;
+err:
+ if (!ok) {
+ ossl_ecx_key_free(key);
+ key = NULL;
+ }
+ if (seed != ikm)
+ OPENSSL_cleanse(seed, seedlen);
+ return key;
+}
+
+/*
+ * Do an ecxdh key exchange.
+ * dhkm = DH(sender, peer)
+ *
+ * NOTE: Instead of using EVP_PKEY_derive() API's, we use ECX_KEY operations
+ * to avoid messy conversions back to EVP_PKEY.
+ *
+ * Returns the size of the secret if successful, or 0 otherwise,
+ */
+static int generate_ecxdhkm(const ECX_KEY *sender, const ECX_KEY *peer,
+ unsigned char *out, size_t maxout,
+ unsigned int secretsz)
+{
+ size_t len = 0;
+
+ /* NOTE: ossl_ecx_compute_key checks for shared secret being all zeros */
+ return ossl_ecx_compute_key((ECX_KEY *)peer, (ECX_KEY *)sender,
+ sender->keylen, out, &len, maxout);
+}
+
+/*
+ * Derive a secret using ECXDH (code is shared by the encap and decap)
+ *
+ * dhkm = Concat(ecxdh(privkey1, peerkey1), ecdh(privkey2, peerkey2)
+ * kemctx = Concat(sender_pub, recipient_pub, ctx->sender_authkey)
+ * secret = dhkem_extract_and_expand(kemid, dhkm, kemctx);
+ *
+ * Params:
+ * ctx Object that contains algorithm state and constants.
+ * secret The returned secret (with a length ctx->alg->secretlen bytes).
+ * privkey1 A private key used for ECXDH key derivation.
+ * peerkey1 A public key used for ECXDH key derivation with privkey1
+ * privkey2 A optional private key used for a second ECXDH key derivation.
+ * It can be NULL.
+ * peerkey2 A optional public key used for a second ECXDH key derivation
+ * with privkey2,. It can be NULL.
+ * sender_pub The senders public key in encoded form.
+ * recipient_pub The recipients public key in encoded form.
+ * Notes:
+ * The second ecdh() is only used for the HPKE auth modes when both privkey2
+ * and peerkey2 are non NULL (i.e. ctx->sender_authkey is not NULL).
+ */
+static int derive_secret(PROV_ECX_CTX *ctx, unsigned char *secret,
+ const ECX_KEY *privkey1, const ECX_KEY *peerkey1,
+ const ECX_KEY *privkey2, const ECX_KEY *peerkey2,
+ const unsigned char *sender_pub,
+ const unsigned char *recipient_pub)
+{
+ int ret = 0;
+ EVP_KDF_CTX *kdfctx = NULL;
+ unsigned char *sender_authpub = NULL;
+ unsigned char dhkm[MAX_ECX_KEYLEN * 2];
+ unsigned char kemctx[MAX_ECX_KEYLEN * 3];
+ size_t kemctxlen = 0, dhkmlen = 0;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+ int auth = ctx->sender_authkey != NULL;
+ size_t encodedkeylen = info->Npk;
+
+ if (!generate_ecxdhkm(privkey1, peerkey1, dhkm, sizeof(dhkm), encodedkeylen))
+ goto err;
+ dhkmlen = encodedkeylen;
+
+ /* Concat the optional second ECXDH (used for Auth) */
+ if (auth) {
+ if (!generate_ecxdhkm(privkey2, peerkey2,
+ dhkm + dhkmlen, sizeof(dhkm) - dhkmlen,
+ encodedkeylen))
+ goto err;
+ /* Get the public key of the auth sender in encoded form */
+ sender_authpub = ecx_pubkey(ctx->sender_authkey);
+ if (sender_authpub == NULL)
+ goto err;
+ dhkmlen += encodedkeylen;
+ }
+ kemctxlen = encodedkeylen + dhkmlen;
+ if (kemctxlen > sizeof(kemctx))
+ goto err;
+
+ /* kemctx is the concat of both sides encoded public key */
+ memcpy(kemctx, sender_pub, encodedkeylen);
+ memcpy(kemctx + encodedkeylen, recipient_pub, encodedkeylen);
+ if (auth)
+ memcpy(kemctx + 2 * encodedkeylen, sender_authpub, encodedkeylen);
+ kdfctx = ossl_kdf_ctx_create(ctx->kdfname, info->mdname,
+ ctx->libctx, ctx->propq);
+ if (kdfctx == NULL)
+ goto err;
+ if (!dhkem_extract_and_expand(kdfctx, secret, info->Nsecret,
+ info->kem_id, dhkm, dhkmlen,
+ kemctx, kemctxlen))
+ goto err;
+ ret = 1;
+err:
+ OPENSSL_cleanse(dhkm, dhkmlen);
+ EVP_KDF_CTX_free(kdfctx);
+ return ret;
+}
+
+/*
+ * Do a DHKEM encapsulate operation.
+ *
+ * See Section 4.1 Encap() and AuthEncap()
+ *
+ * Params:
+ * ctx A context object holding the recipients public key and the
+ * optional senders auth private key.
+ * enc A buffer to return the senders ephemeral public key.
+ * Setting this to NULL allows the enclen and secretlen to return
+ * values, without calculating the secret.
+ * enclen Passes in the max size of the enc buffer and returns the
+ * encoded public key length.
+ * secret A buffer to return the calculated shared secret.
+ * secretlen Passes in the max size of the secret buffer and returns the
+ * secret length.
+ * Returns: 1 on success or 0 otherwise.
+ */
+static int dhkem_encap(PROV_ECX_CTX *ctx,
+ unsigned char *enc, size_t *enclen,
+ unsigned char *secret, size_t *secretlen)
+{
+ int ret = 0;
+ ECX_KEY *sender_ephemkey = NULL;
+ unsigned char *sender_ephempub, *recipient_pub;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+
+ if (enc == NULL) {
+ if (enclen == NULL && secretlen == NULL)
+ return 0;
+ if (enclen != NULL)
+ *enclen = info->Nenc;
+ if (secretlen != NULL)
+ *secretlen = info->Nsecret;
+ return 1;
+ }
+
+ if (*secretlen < info->Nsecret) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+ return 0;
+ }
+ if (*enclen < info->Nenc) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*enclen too small");
+ return 0;
+ }
+
+ /* Create an ephemeral key */
+ sender_ephemkey = derivekey(ctx, ctx->ikm, ctx->ikmlen);
+
+ sender_ephempub = ecx_pubkey(sender_ephemkey);
+ recipient_pub = ecx_pubkey(ctx->recipient_key);
+ if (sender_ephempub == NULL || recipient_pub == NULL)
+ goto err;
+
+ if (!derive_secret(ctx, secret,
+ sender_ephemkey, ctx->recipient_key,
+ ctx->sender_authkey, ctx->recipient_key,
+ sender_ephempub, recipient_pub))
+ goto err;
+
+ /* Return the public part of the ephemeral key */
+ memcpy(enc, sender_ephempub, info->Nenc);
+ *enclen = info->Nenc;
+ *secretlen = info->Nsecret;
+ ret = 1;
+err:
+ ossl_ecx_key_free(sender_ephemkey);
+ return ret;
+}
+
+/*
+ * Do a DHKEM decapsulate operation.
+ * See Section 4.1 Decap() and Auth Decap()
+ *
+ * Params:
+ * ctx A context object holding the recipients private key and the
+ * optional senders auth public key.
+ * secret A buffer to return the calculated shared secret. Setting this to
+ * NULL can be used to return the secretlen.
+ * secretlen Passes in the max size of the secret buffer and returns the
+ * secret length.
+ * enc A buffer containing the senders ephemeral public key that was returned
+ * from dhkem_encap().
+ * enclen The length in bytes of enc.
+ * Returns: 1 If the shared secret is returned or 0 on error.
+ */
+static int dhkem_decap(PROV_ECX_CTX *ctx,
+ unsigned char *secret, size_t *secretlen,
+ const unsigned char *enc, size_t enclen)
+{
+ int ret = 0;
+ ECX_KEY *recipient_privkey = ctx->recipient_key;
+ ECX_KEY *sender_ephempubkey = NULL;
+ const OSSL_HPKE_KEM_INFO *info = ctx->info;
+ unsigned char *recipient_pub;
+
+ if (secret == NULL) {
+ *secretlen = info->Nsecret;
+ return 1;
+ }
+ if (*secretlen < info->Nsecret) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_BAD_LENGTH, "*secretlen too small");
+ return 0;
+ }
+ if (enclen != info->Nenc) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, "Invalid enc public key");
+ return 0;
+ }
+
+ /* Get the public part of the ephemeral key created by encap */
+ sender_ephempubkey = ecxkey_pubfromdata(ctx, enc, enclen);
+ if (sender_ephempubkey == NULL)
+ goto err;
+
+ recipient_pub = ecx_pubkey(recipient_privkey);
+ if (recipient_pub == NULL)
+ goto err;
+
+ if (!derive_secret(ctx, secret,
+ ctx->recipient_key, sender_ephempubkey,
+ ctx->recipient_key, ctx->sender_authkey,
+ enc, recipient_pub))
+ goto err;
+
+ *secretlen = info->Nsecret;
+ ret = 1;
+err:
+ ossl_ecx_key_free(sender_ephempubkey);
+ return ret;
+}
+
+static int ecxkem_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ unsigned char *secret, size_t *secretlen)
+{
+ PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+
+ switch (ctx->mode) {
+ case KEM_MODE_DHKEM:
+ return dhkem_encap(ctx, out, outlen, secret, secretlen);
+ default:
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return -2;
+ }
+}
+
+static int ecxkem_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ PROV_ECX_CTX *ctx = (PROV_ECX_CTX *)vctx;
+
+ switch (ctx->mode) {
+ case KEM_MODE_DHKEM:
+ return dhkem_decap(vctx, out, outlen, in, inlen);
+ default:
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return -2;
+ }
+}
+
+const OSSL_DISPATCH ossl_ecx_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))ecxkem_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+ (void (*)(void))ecxkem_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))ecxkem_encapsulate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+ (void (*)(void))ecxkem_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))ecxkem_decapsulate },
+ { OSSL_FUNC_KEM_FREECTX, (void (*)(void))ecxkem_freectx },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+ (void (*)(void))ecxkem_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+ (void (*)(void))ecxkem_settable_ctx_params },
+ { OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT,
+ (void (*)(void))ecxkem_auth_encapsulate_init },
+ { OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT,
+ (void (*)(void))ecxkem_auth_decapsulate_init },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/kem_util.c b/crypto/openssl/providers/implementations/kem/kem_util.c
new file mode 100644
index 000000000000..1fd52e1c2d52
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/kem_util.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h> /* for memcpy() */
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include "eckem.h"
+
+typedef struct {
+ unsigned int id;
+ const char *mode;
+} KEM_MODE;
+
+static const KEM_MODE eckem_modename_id_map[] = {
+ { KEM_MODE_DHKEM, OSSL_KEM_PARAM_OPERATION_DHKEM },
+ { 0, NULL }
+};
+
+int ossl_eckem_modename2id(const char *name)
+{
+ size_t i;
+
+ if (name == NULL)
+ return KEM_MODE_UNDEFINED;
+
+ for (i = 0; eckem_modename_id_map[i].mode != NULL; ++i) {
+ if (OPENSSL_strcasecmp(name, eckem_modename_id_map[i].mode) == 0)
+ return eckem_modename_id_map[i].id;
+ }
+ return KEM_MODE_UNDEFINED;
+}
diff --git a/crypto/openssl/providers/implementations/kem/ml_kem_kem.c b/crypto/openssl/providers/implementations/kem/ml_kem_kem.c
new file mode 100644
index 000000000000..27aa3b819836
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/ml_kem_kem.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "crypto/ml_kem.h"
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_kem_newctx_fn ml_kem_newctx;
+static OSSL_FUNC_kem_freectx_fn ml_kem_freectx;
+static OSSL_FUNC_kem_encapsulate_init_fn ml_kem_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn ml_kem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn ml_kem_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn ml_kem_decapsulate;
+static OSSL_FUNC_kem_set_ctx_params_fn ml_kem_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn ml_kem_settable_ctx_params;
+
+typedef struct {
+ ML_KEM_KEY *key;
+ uint8_t entropy_buf[ML_KEM_RANDOM_BYTES];
+ uint8_t *entropy;
+ int op;
+} PROV_ML_KEM_CTX;
+
+static void *ml_kem_newctx(void *provctx)
+{
+ PROV_ML_KEM_CTX *ctx;
+
+ if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
+ return NULL;
+
+ ctx->key = NULL;
+ ctx->entropy = NULL;
+ ctx->op = 0;
+ return ctx;
+}
+
+static void ml_kem_freectx(void *vctx)
+{
+ PROV_ML_KEM_CTX *ctx = vctx;
+
+ if (ctx->entropy != NULL)
+ OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
+ OPENSSL_free(ctx);
+}
+
+static int ml_kem_init(void *vctx, int op, void *key,
+ const OSSL_PARAM params[])
+{
+ PROV_ML_KEM_CTX *ctx = vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ ctx->key = key;
+ ctx->op = op;
+ return ml_kem_set_ctx_params(vctx, params);
+}
+
+static int ml_kem_encapsulate_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[])
+{
+ ML_KEM_KEY *key = vkey;
+
+ if (!ossl_ml_kem_have_pubkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ return ml_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
+}
+
+static int ml_kem_decapsulate_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[])
+{
+ ML_KEM_KEY *key = vkey;
+
+ if (!ossl_ml_kem_have_prvkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ return ml_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
+}
+
+static int ml_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ML_KEM_CTX *ctx = vctx;
+ const OSSL_PARAM *p;
+
+ if (ctx == NULL)
+ return 0;
+
+ if (ctx->op == EVP_PKEY_OP_DECAPSULATE && ctx->entropy != NULL) {
+ /* Decapsulation is deterministic */
+ OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
+ ctx->entropy = NULL;
+ }
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ /* Encapsulation ephemeral input key material "ikmE" */
+ if (ctx->op == EVP_PKEY_OP_ENCAPSULATE
+ && (p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_IKME)) != NULL) {
+ size_t len = ML_KEM_RANDOM_BYTES;
+
+ ctx->entropy = ctx->entropy_buf;
+ if (OSSL_PARAM_get_octet_string(p, (void **)&ctx->entropy,
+ len, &len)
+ && len == ML_KEM_RANDOM_BYTES)
+ return 1;
+
+ /* Possibly, but much less likely wrong type */
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ ctx->entropy = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *ml_kem_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM params[] = {
+ OSSL_PARAM_octet_string(OSSL_KEM_PARAM_IKME, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return params;
+}
+
+static int ml_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
+ unsigned char *shsec, size_t *slen)
+{
+ PROV_ML_KEM_CTX *ctx = vctx;
+ ML_KEM_KEY *key = ctx->key;
+ const ML_KEM_VINFO *v;
+ size_t encap_clen;
+ size_t encap_slen;
+ int ret = 0;
+
+ if (!ossl_ml_kem_have_pubkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ goto end;
+ }
+ v = ossl_ml_kem_key_vinfo(key);
+ encap_clen = v->ctext_bytes;
+ encap_slen = ML_KEM_SHARED_SECRET_BYTES;
+
+ if (ctext == NULL) {
+ if (clen == NULL && slen == NULL)
+ return 0;
+ if (clen != NULL)
+ *clen = encap_clen;
+ if (slen != NULL)
+ *slen = encap_slen;
+ return 1;
+ }
+ if (shsec == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
+ "NULL shared-secret buffer");
+ goto end;
+ }
+
+ if (clen == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
+ "null ciphertext input/output length pointer");
+ goto end;
+ } else if (*clen < encap_clen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "ciphertext buffer too small");
+ goto end;
+ } else {
+ *clen = encap_clen;
+ }
+
+ if (slen == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
+ "null shared secret input/output length pointer");
+ goto end;
+ } else if (*slen < encap_slen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "shared-secret buffer too small");
+ goto end;
+ } else {
+ *slen = encap_slen;
+ }
+
+ if (ctx->entropy != NULL)
+ ret = ossl_ml_kem_encap_seed(ctext, encap_clen, shsec, encap_slen,
+ ctx->entropy, ML_KEM_RANDOM_BYTES, key);
+ else
+ ret = ossl_ml_kem_encap_rand(ctext, encap_clen, shsec, encap_slen, key);
+
+ end:
+ /*
+ * One shot entropy, each encapsulate call must either provide a new
+ * "ikmE", or else will use a random value. If a caller sets an explicit
+ * ikmE once for testing, and later performs multiple encapsulations
+ * without again calling encapsulate_init(), these should not share the
+ * original entropy.
+ */
+ if (ctx->entropy != NULL) {
+ OPENSSL_cleanse(ctx->entropy, ML_KEM_RANDOM_BYTES);
+ ctx->entropy = NULL;
+ }
+ return ret;
+}
+
+static int ml_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
+ const uint8_t *ctext, size_t clen)
+{
+ PROV_ML_KEM_CTX *ctx = vctx;
+ ML_KEM_KEY *key = ctx->key;
+ size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES;
+
+ if (!ossl_ml_kem_have_prvkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ if (shsec == NULL) {
+ if (slen == NULL)
+ return 0;
+ *slen = ML_KEM_SHARED_SECRET_BYTES;
+ return 1;
+ }
+
+ /* For now tolerate newly-deprecated NULL length pointers. */
+ if (slen == NULL) {
+ slen = &decap_slen;
+ } else if (*slen < decap_slen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "shared-secret buffer too small");
+ return 0;
+ } else {
+ *slen = decap_slen;
+ }
+
+ /* ML-KEM decap handles incorrect ciphertext lengths internally */
+ return ossl_ml_kem_decap(shsec, decap_slen, ctext, clen, key);
+}
+
+const OSSL_DISPATCH ossl_ml_kem_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) ml_kem_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) ml_kem_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) ml_kem_encapsulate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) ml_kem_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) ml_kem_decapsulate },
+ { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) ml_kem_freectx },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) ml_kem_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) ml_kem_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/mlx_kem.c b/crypto/openssl/providers/implementations/kem/mlx_kem.c
new file mode 100644
index 000000000000..197c345d85cb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/mlx_kem.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+#include <openssl/rand.h>
+#include "prov/implementations.h"
+#include "prov/mlx_kem.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_kem_newctx_fn mlx_kem_newctx;
+static OSSL_FUNC_kem_freectx_fn mlx_kem_freectx;
+static OSSL_FUNC_kem_encapsulate_init_fn mlx_kem_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn mlx_kem_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn mlx_kem_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn mlx_kem_decapsulate;
+static OSSL_FUNC_kem_set_ctx_params_fn mlx_kem_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn mlx_kem_settable_ctx_params;
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ MLX_KEY *key;
+ int op;
+} PROV_MLX_KEM_CTX;
+
+static void *mlx_kem_newctx(void *provctx)
+{
+ PROV_MLX_KEM_CTX *ctx;
+
+ if ((ctx = OPENSSL_malloc(sizeof(*ctx))) == NULL)
+ return NULL;
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->key = NULL;
+ ctx->op = 0;
+ return ctx;
+}
+
+static void mlx_kem_freectx(void *vctx)
+{
+ OPENSSL_free(vctx);
+}
+
+static int mlx_kem_init(void *vctx, int op, void *key,
+ ossl_unused const OSSL_PARAM params[])
+{
+ PROV_MLX_KEM_CTX *ctx = vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ ctx->key = key;
+ ctx->op = op;
+ return 1;
+}
+
+static int
+mlx_kem_encapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ MLX_KEY *key = vkey;
+
+ if (!mlx_kem_have_pubkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ return mlx_kem_init(vctx, EVP_PKEY_OP_ENCAPSULATE, key, params);
+}
+
+static int
+mlx_kem_decapsulate_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ MLX_KEY *key = vkey;
+
+ if (!mlx_kem_have_prvkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ return mlx_kem_init(vctx, EVP_PKEY_OP_DECAPSULATE, key, params);
+}
+
+static const OSSL_PARAM *mlx_kem_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM params[] = { OSSL_PARAM_END };
+
+ return params;
+}
+
+static int
+mlx_kem_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ return 1;
+}
+
+static int mlx_kem_encapsulate(void *vctx, unsigned char *ctext, size_t *clen,
+ unsigned char *shsec, size_t *slen)
+{
+ MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key;
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *xkey = NULL;
+ size_t encap_clen;
+ size_t encap_slen;
+ uint8_t *cbuf;
+ uint8_t *sbuf;
+ int ml_kem_slot = key->xinfo->ml_kem_slot;
+ int ret = 0;
+
+ if (!mlx_kem_have_pubkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ goto end;
+ }
+ encap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
+ encap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
+
+ if (ctext == NULL) {
+ if (clen == NULL && slen == NULL)
+ return 0;
+ if (clen != NULL)
+ *clen = encap_clen;
+ if (slen != NULL)
+ *slen = encap_slen;
+ return 1;
+ }
+ if (shsec == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_OUTPUT_BUFFER,
+ "null shared-secret output buffer");
+ return 0;
+ }
+
+ if (clen == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
+ "null ciphertext input/output length pointer");
+ return 0;
+ } else if (*clen < encap_clen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "ciphertext buffer too small");
+ return 0;
+ } else {
+ *clen = encap_clen;
+ }
+
+ if (slen == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NULL_LENGTH_POINTER,
+ "null shared secret input/output length pointer");
+ return 0;
+ } else if (*slen < encap_slen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "shared-secret buffer too small");
+ return 0;
+ } else {
+ *slen = encap_slen;
+ }
+
+ /* ML-KEM encapsulation */
+ encap_clen = key->minfo->ctext_bytes;
+ encap_slen = ML_KEM_SHARED_SECRET_BYTES;
+ cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
+ sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
+ ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
+ if (ctx == NULL
+ || EVP_PKEY_encapsulate_init(ctx, NULL) <= 0
+ || EVP_PKEY_encapsulate(ctx, cbuf, &encap_clen, sbuf, &encap_slen) <= 0)
+ goto end;
+ if (encap_clen != key->minfo->ctext_bytes) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s ciphertext output size: %lu",
+ key->minfo->algorithm_name, (unsigned long) encap_clen);
+ goto end;
+ }
+ if (encap_slen != ML_KEM_SHARED_SECRET_BYTES) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s shared secret output size: %lu",
+ key->minfo->algorithm_name, (unsigned long) encap_slen);
+ goto end;
+ }
+ EVP_PKEY_CTX_free(ctx);
+
+ /*-
+ * ECDHE encapsulation
+ *
+ * Generate own ephemeral private key and add its public key to ctext.
+ *
+ * Note, we could support a settable parameter that sets an extant ECDH
+ * keypair as the keys to use in encap, making it possible to reuse the
+ * same (TLS client) ECDHE keypair for both the classical EC keyshare and a
+ * corresponding ECDHE + ML-KEM keypair. But the TLS layer would then need
+ * know that this is a hybrid, and that it can partly reuse the same keys
+ * as another group for which a keyshare will be sent. Deferred until we
+ * support generating multiple keyshares, there's a workable keyshare
+ * prediction specification, and the optimisation is justified.
+ */
+ cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
+ encap_clen = key->xinfo->pubkey_bytes;
+ ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
+ if (ctx == NULL
+ || EVP_PKEY_keygen_init(ctx) <= 0
+ || EVP_PKEY_keygen(ctx, &xkey) <= 0
+ || EVP_PKEY_get_octet_string_param(xkey, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+ cbuf, encap_clen, &encap_clen) <= 0)
+ goto end;
+ if (encap_clen != key->xinfo->pubkey_bytes) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s public key output size: %lu",
+ key->xinfo->algorithm_name, (unsigned long) encap_clen);
+ goto end;
+ }
+ EVP_PKEY_CTX_free(ctx);
+
+ /* Derive the ECDH shared secret */
+ encap_slen = key->xinfo->shsec_bytes;
+ sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
+ ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, xkey, key->propq);
+ if (ctx == NULL
+ || EVP_PKEY_derive_init(ctx) <= 0
+ || EVP_PKEY_derive_set_peer(ctx, key->xkey) <= 0
+ || EVP_PKEY_derive(ctx, sbuf, &encap_slen) <= 0)
+ goto end;
+ if (encap_slen != key->xinfo->shsec_bytes) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s shared secret output size: %lu",
+ key->xinfo->algorithm_name, (unsigned long) encap_slen);
+ goto end;
+ }
+
+ ret = 1;
+ end:
+ EVP_PKEY_free(xkey);
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+static int mlx_kem_decapsulate(void *vctx, uint8_t *shsec, size_t *slen,
+ const uint8_t *ctext, size_t clen)
+{
+ MLX_KEY *key = ((PROV_MLX_KEM_CTX *) vctx)->key;
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *xkey = NULL;
+ const uint8_t *cbuf;
+ uint8_t *sbuf;
+ size_t decap_slen = ML_KEM_SHARED_SECRET_BYTES + key->xinfo->shsec_bytes;
+ size_t decap_clen = key->minfo->ctext_bytes + key->xinfo->pubkey_bytes;
+ int ml_kem_slot = key->xinfo->ml_kem_slot;
+ int ret = 0;
+
+ if (!mlx_kem_have_prvkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ if (shsec == NULL) {
+ if (slen == NULL)
+ return 0;
+ *slen = decap_slen;
+ return 1;
+ }
+
+ /* For now tolerate newly-deprecated NULL length pointers. */
+ if (slen == NULL) {
+ slen = &decap_slen;
+ } else if (*slen < decap_slen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "shared-secret buffer too small");
+ return 0;
+ } else {
+ *slen = decap_slen;
+ }
+ if (clen != decap_clen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_WRONG_CIPHERTEXT_SIZE,
+ "wrong decapsulation input ciphertext size: %lu",
+ (unsigned long) clen);
+ return 0;
+ }
+
+ /* ML-KEM decapsulation */
+ decap_clen = key->minfo->ctext_bytes;
+ decap_slen = ML_KEM_SHARED_SECRET_BYTES;
+ cbuf = ctext + ml_kem_slot * key->xinfo->pubkey_bytes;
+ sbuf = shsec + ml_kem_slot * key->xinfo->shsec_bytes;
+ ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->mkey, key->propq);
+ if (ctx == NULL
+ || EVP_PKEY_decapsulate_init(ctx, NULL) <= 0
+ || EVP_PKEY_decapsulate(ctx, sbuf, &decap_slen, cbuf, decap_clen) <= 0)
+ goto end;
+ if (decap_slen != ML_KEM_SHARED_SECRET_BYTES) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s shared secret output size: %lu",
+ key->minfo->algorithm_name, (unsigned long) decap_slen);
+ goto end;
+ }
+ EVP_PKEY_CTX_free(ctx);
+
+ /* ECDH decapsulation */
+ decap_clen = key->xinfo->pubkey_bytes;
+ decap_slen = key->xinfo->shsec_bytes;
+ cbuf = ctext + (1 - ml_kem_slot) * key->minfo->ctext_bytes;
+ sbuf = shsec + (1 - ml_kem_slot) * ML_KEM_SHARED_SECRET_BYTES;
+ ctx = EVP_PKEY_CTX_new_from_pkey(key->libctx, key->xkey, key->propq);
+ if (ctx == NULL
+ || (xkey = EVP_PKEY_new()) == NULL
+ || EVP_PKEY_copy_parameters(xkey, key->xkey) <= 0
+ || EVP_PKEY_set1_encoded_public_key(xkey, cbuf, decap_clen) <= 0
+ || EVP_PKEY_derive_init(ctx) <= 0
+ || EVP_PKEY_derive_set_peer(ctx, xkey) <= 0
+ || EVP_PKEY_derive(ctx, sbuf, &decap_slen) <= 0)
+ goto end;
+ if (decap_slen != key->xinfo->shsec_bytes) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "unexpected %s shared secret output size: %lu",
+ key->xinfo->algorithm_name, (unsigned long) decap_slen);
+ goto end;
+ }
+
+ ret = 1;
+ end:
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(xkey);
+ return ret;
+}
+
+const OSSL_DISPATCH ossl_mlx_kem_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (OSSL_FUNC) mlx_kem_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT, (OSSL_FUNC) mlx_kem_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (OSSL_FUNC) mlx_kem_encapsulate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT, (OSSL_FUNC) mlx_kem_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (OSSL_FUNC) mlx_kem_decapsulate },
+ { OSSL_FUNC_KEM_FREECTX, (OSSL_FUNC) mlx_kem_freectx },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS, (OSSL_FUNC) mlx_kem_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, (OSSL_FUNC) mlx_kem_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/rsa_kem.c b/crypto/openssl/providers/implementations/kem/rsa_kem.c
new file mode 100644
index 000000000000..7494dcc0107f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/rsa_kem.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+#include "internal/nelem.h"
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/rsa.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "crypto/rsa.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+
+static OSSL_FUNC_kem_newctx_fn rsakem_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn rsakem_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn rsakem_generate;
+static OSSL_FUNC_kem_decapsulate_init_fn rsakem_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn rsakem_recover;
+static OSSL_FUNC_kem_freectx_fn rsakem_freectx;
+static OSSL_FUNC_kem_dupctx_fn rsakem_dupctx;
+static OSSL_FUNC_kem_get_ctx_params_fn rsakem_get_ctx_params;
+static OSSL_FUNC_kem_gettable_ctx_params_fn rsakem_gettable_ctx_params;
+static OSSL_FUNC_kem_set_ctx_params_fn rsakem_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn rsakem_settable_ctx_params;
+
+/*
+ * Only the KEM for RSASVE as defined in SP800-56b r2 is implemented
+ * currently.
+ */
+#define KEM_OP_UNDEFINED -1
+#define KEM_OP_RSASVE 0
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes RSA structures, so
+ * we use that here too.
+ */
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ RSA *rsa;
+ int op;
+ OSSL_FIPS_IND_DECLARE
+} PROV_RSA_CTX;
+
+static const OSSL_ITEM rsakem_opname_id_map[] = {
+ { KEM_OP_RSASVE, OSSL_KEM_PARAM_OPERATION_RSASVE },
+};
+
+static int name2id(const char *name, const OSSL_ITEM *map, size_t sz)
+{
+ size_t i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < sz; ++i) {
+ if (OPENSSL_strcasecmp(map[i].ptr, name) == 0)
+ return map[i].id;
+ }
+ return -1;
+}
+
+static int rsakem_opname2id(const char *name)
+{
+ return name2id(name, rsakem_opname_id_map, OSSL_NELEM(rsakem_opname_id_map));
+}
+
+static void *rsakem_newctx(void *provctx)
+{
+ PROV_RSA_CTX *prsactx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX));
+ if (prsactx == NULL)
+ return NULL;
+ prsactx->libctx = PROV_LIBCTX_OF(provctx);
+ prsactx->op = KEM_OP_RSASVE;
+ OSSL_FIPS_IND_INIT(prsactx)
+
+ return prsactx;
+}
+
+static void rsakem_freectx(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ RSA_free(prsactx->rsa);
+ OPENSSL_free(prsactx);
+}
+
+static void *rsakem_dupctx(void *vprsactx)
+{
+ PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx;
+ PROV_RSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) {
+ OPENSSL_free(dstctx);
+ return NULL;
+ }
+ return dstctx;
+}
+
+static int rsakem_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int protect = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (prsactx == NULL || vrsa == NULL)
+ return 0;
+
+ if (!ossl_rsa_key_op_get_protect(vrsa, operation, &protect))
+ return 0;
+ if (!RSA_up_ref(vrsa))
+ return 0;
+ RSA_free(prsactx->rsa);
+ prsactx->rsa = vrsa;
+
+ OSSL_FIPS_IND_SET_APPROVED(prsactx)
+ if (!rsakem_set_ctx_params(prsactx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx),
+ OSSL_FIPS_IND_SETTABLE0, prsactx->libctx,
+ prsactx->rsa, desc, protect))
+ return 0;
+#endif
+ return 1;
+}
+
+static int rsakem_encapsulate_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_ENCAPSULATE,
+ "RSA Encapsulate Init");
+}
+
+static int rsakem_decapsulate_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ return rsakem_init(vprsactx, vrsa, params, EVP_PKEY_OP_DECAPSULATE,
+ "RSA Decapsulate Init");
+}
+
+static int rsakem_get_ctx_params(void *vprsactx, OSSL_PARAM *params)
+{
+ PROV_RSA_CTX *ctx = (PROV_RSA_CTX *)vprsactx;
+
+ if (ctx == NULL)
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_rsakem_ctx_params[] = {
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsakem_gettable_ctx_params(ossl_unused void *vprsactx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_rsakem_ctx_params;
+}
+
+static int rsakem_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ const OSSL_PARAM *p;
+ int op;
+
+ if (prsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_KEM_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ op = rsakem_opname2id(p->data);
+ if (op < 0)
+ return 0;
+ prsactx->op = op;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_rsakem_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_KEM_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsakem_settable_ctx_params(ossl_unused void *vprsactx,
+ ossl_unused void *provctx)
+{
+ return known_settable_rsakem_ctx_params;
+}
+
+/*
+ * NIST.SP.800-56Br2
+ * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE).
+ *
+ * Generate a random in the range 1 < z < (n – 1)
+ */
+static int rsasve_gen_rand_bytes(RSA *rsa_pub,
+ unsigned char *out, int outlen)
+{
+ int ret = 0;
+ BN_CTX *bnctx;
+ BIGNUM *z, *nminus3;
+
+ bnctx = BN_CTX_secure_new_ex(ossl_rsa_get0_libctx(rsa_pub));
+ if (bnctx == NULL)
+ return 0;
+
+ /*
+ * Generate a random in the range 1 < z < (n – 1).
+ * Since BN_priv_rand_range_ex() returns a value in range 0 <= r < max
+ * We can achieve this by adding 2.. but then we need to subtract 3 from
+ * the upper bound i.e: 2 + (0 <= r < (n - 3))
+ */
+ BN_CTX_start(bnctx);
+ nminus3 = BN_CTX_get(bnctx);
+ z = BN_CTX_get(bnctx);
+ ret = (z != NULL
+ && (BN_copy(nminus3, RSA_get0_n(rsa_pub)) != NULL)
+ && BN_sub_word(nminus3, 3)
+ && BN_priv_rand_range_ex(z, nminus3, 0, bnctx)
+ && BN_add_word(z, 2)
+ && (BN_bn2binpad(z, out, outlen) == outlen));
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+/*
+ * NIST.SP.800-56Br2
+ * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE).
+ */
+static int rsasve_generate(PROV_RSA_CTX *prsactx,
+ unsigned char *out, size_t *outlen,
+ unsigned char *secret, size_t *secretlen)
+{
+ int ret;
+ size_t nlen;
+
+ /* Step (1): nlen = Ceil(len(n)/8) */
+ nlen = RSA_size(prsactx->rsa);
+
+ if (out == NULL) {
+ if (nlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+ if (outlen == NULL && secretlen == NULL)
+ return 0;
+ if (outlen != NULL)
+ *outlen = nlen;
+ if (secretlen != NULL)
+ *secretlen = nlen;
+ return 1;
+ }
+
+ /*
+ * If outlen is specified, then it must report the length
+ * of the out buffer on input so that we can confirm
+ * its size is sufficent for encapsulation
+ */
+ if (outlen != NULL && *outlen < nlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
+ return 0;
+ }
+
+ /*
+ * Step (2): Generate a random byte string z of nlen bytes where
+ * 1 < z < n - 1
+ */
+ if (!rsasve_gen_rand_bytes(prsactx->rsa, secret, nlen))
+ return 0;
+
+ /* Step(3): out = RSAEP((n,e), z) */
+ ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING);
+ if (ret) {
+ ret = 1;
+ if (outlen != NULL)
+ *outlen = nlen;
+ if (secretlen != NULL)
+ *secretlen = nlen;
+ } else {
+ OPENSSL_cleanse(secret, nlen);
+ }
+ return ret;
+}
+
+/**
+ * rsasve_recover - Recovers a secret value from ciphertext using an RSA
+ * private key. Once, recovered, the secret value is considered to be a
+ * shared secret. Algorithm is preformed as per
+ * NIST SP 800-56B Rev 2
+ * 7.2.1.3 RSASVE Recovery Operation (RSASVE.RECOVER).
+ *
+ * This function performs RSA decryption using the private key from the
+ * provided RSA context (`prsactx`). It takes the input ciphertext, decrypts
+ * it, and writes the decrypted message to the output buffer.
+ *
+ * @prsactx: The RSA context containing the private key.
+ * @out: The output buffer to store the decrypted message.
+ * @outlen: On input, the size of the output buffer. On successful
+ * completion, the actual length of the decrypted message.
+ * @in: The input buffer containing the ciphertext to be decrypted.
+ * @inlen: The length of the input ciphertext in bytes.
+ *
+ * Returns 1 on success, or 0 on error. In case of error, appropriate
+ * error messages are raised using the ERR_raise function.
+ */
+static int rsasve_recover(PROV_RSA_CTX *prsactx,
+ unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ size_t nlen;
+ int ret;
+
+ /* Step (1): get the byte length of n */
+ nlen = RSA_size(prsactx->rsa);
+
+ if (out == NULL) {
+ if (nlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+ *outlen = nlen;
+ return 1;
+ }
+
+ /*
+ * Step (2): check the input ciphertext 'inlen' matches the nlen
+ * and that outlen is at least nlen bytes
+ */
+ if (inlen != nlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH);
+ return 0;
+ }
+
+ /*
+ * If outlen is specified, then it must report the length
+ * of the out buffer, so that we can confirm that it is of
+ * sufficient size to hold the output of decapsulation
+ */
+ if (outlen != NULL && *outlen < nlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
+ return 0;
+ }
+
+ /* Step (3): out = RSADP((n,d), in) */
+ ret = RSA_private_decrypt(inlen, in, out, prsactx->rsa, RSA_NO_PADDING);
+ if (ret > 0 && outlen != NULL)
+ *outlen = ret;
+ return ret > 0;
+}
+
+static int rsakem_generate(void *vprsactx, unsigned char *out, size_t *outlen,
+ unsigned char *secret, size_t *secretlen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ switch (prsactx->op) {
+ case KEM_OP_RSASVE:
+ return rsasve_generate(prsactx, out, outlen, secret, secretlen);
+ default:
+ return -2;
+ }
+}
+
+static int rsakem_recover(void *vprsactx, unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ switch (prsactx->op) {
+ case KEM_OP_RSASVE:
+ return rsasve_recover(prsactx, out, outlen, in, inlen);
+ default:
+ return -2;
+ }
+}
+
+const OSSL_DISPATCH ossl_rsa_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))rsakem_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+ (void (*)(void))rsakem_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))rsakem_generate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+ (void (*)(void))rsakem_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))rsakem_recover },
+ { OSSL_FUNC_KEM_FREECTX, (void (*)(void))rsakem_freectx },
+ { OSSL_FUNC_KEM_DUPCTX, (void (*)(void))rsakem_dupctx },
+ { OSSL_FUNC_KEM_GET_CTX_PARAMS,
+ (void (*)(void))rsakem_get_ctx_params },
+ { OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS,
+ (void (*)(void))rsakem_gettable_ctx_params },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+ (void (*)(void))rsakem_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+ (void (*)(void))rsakem_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/kem/template_kem.c b/crypto/openssl/providers/implementations/kem/template_kem.c
new file mode 100644
index 000000000000..c621a31a9c35
--- /dev/null
+++ b/crypto/openssl/providers/implementations/kem/template_kem.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+
+extern const OSSL_DISPATCH ossl_template_asym_kem_functions[];
+
+#define BUFSIZE 1000
+#if defined(NDEBUG) || defined(OPENSSL_NO_STDIO)
+static void debug_print(char *fmt, ...)
+{
+}
+
+#else
+static void debug_print(char *fmt, ...)
+{
+ char out[BUFSIZE];
+ va_list argptr;
+
+ va_start(argptr, fmt);
+ vsnprintf(out, BUFSIZE, fmt, argptr);
+ va_end(argptr);
+ if (getenv("TEMPLATEKEM"))
+ fprintf(stderr, "TEMPLATE_KEM: %s", out);
+}
+#endif
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ /* some algorithm-specific key struct */
+ int op;
+} PROV_TEMPLATE_CTX;
+
+static OSSL_FUNC_kem_newctx_fn template_newctx;
+static OSSL_FUNC_kem_encapsulate_init_fn template_encapsulate_init;
+static OSSL_FUNC_kem_encapsulate_fn template_encapsulate;
+static OSSL_FUNC_kem_decapsulate_init_fn template_decapsulate_init;
+static OSSL_FUNC_kem_decapsulate_fn template_decapsulate;
+static OSSL_FUNC_kem_freectx_fn template_freectx;
+static OSSL_FUNC_kem_set_ctx_params_fn template_set_ctx_params;
+static OSSL_FUNC_kem_settable_ctx_params_fn template_settable_ctx_params;
+
+static void *template_newctx(void *provctx)
+{
+ PROV_TEMPLATE_CTX *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ debug_print("newctx called\n");
+ if (ctx == NULL)
+ return NULL;
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+
+ debug_print("newctx returns %p\n", ctx);
+ return ctx;
+}
+
+static void template_freectx(void *vctx)
+{
+ PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx;
+
+ debug_print("freectx %p\n", ctx);
+ OPENSSL_free(ctx);
+}
+
+static int template_init(void *vctx, int operation, void *vkey, void *vauth,
+ ossl_unused const OSSL_PARAM params[])
+{
+ PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx;
+
+ debug_print("init %p / %p\n", ctx, vkey);
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* check and fill in reference to key */
+ ctx->op = operation;
+ debug_print("init OK\n");
+ return 1;
+}
+
+static int template_encapsulate_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[])
+{
+ return template_init(vctx, EVP_PKEY_OP_ENCAPSULATE, vkey, NULL, params);
+}
+
+static int template_decapsulate_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[])
+{
+ return template_init(vctx, EVP_PKEY_OP_DECAPSULATE, vkey, NULL, params);
+}
+
+static int template_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_TEMPLATE_CTX *ctx = (PROV_TEMPLATE_CTX *)vctx;
+
+ debug_print("set ctx params %p\n", ctx);
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ debug_print("set ctx params OK\n");
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_template_ctx_params[] = {
+ /* possibly more params */
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *template_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_template_ctx_params;
+}
+
+static int template_encapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ unsigned char *secret, size_t *secretlen)
+{
+ debug_print("encaps %p to %p\n", vctx, out);
+
+ /* add algorithm-specific length checks */
+
+ if (outlen != NULL)
+ *outlen = 0; /* replace with real encapsulated data length */
+ if (secretlen != NULL)
+ *secretlen = 0; /* replace with real shared secret length */
+
+ if (out == NULL) {
+ if (outlen != NULL && secretlen != NULL)
+ debug_print("encaps outlens set to %zu and %zu\n", *outlen, *secretlen);
+ return 1;
+ }
+
+ /* check key and perform real KEM operation */
+
+ debug_print("encaps OK\n");
+ return 1;
+}
+
+static int template_decapsulate(void *vctx, unsigned char *out, size_t *outlen,
+ const unsigned char *in, size_t inlen)
+{
+ debug_print("decaps %p to %p inlen at %zu\n", vctx, out, inlen);
+
+ /* add algorithm-specific length checks */
+
+ if (outlen != NULL)
+ *outlen = 0; /* replace with shared secret length */
+
+ if (out == NULL) {
+ if (outlen != NULL)
+ debug_print("decaps outlen set to %zu \n", *outlen);
+ return 1;
+ }
+
+ /* check key and perform real decaps operation */
+
+ debug_print("decaps OK\n");
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_template_asym_kem_functions[] = {
+ { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))template_newctx },
+ { OSSL_FUNC_KEM_ENCAPSULATE_INIT,
+ (void (*)(void))template_encapsulate_init },
+ { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))template_encapsulate },
+ { OSSL_FUNC_KEM_DECAPSULATE_INIT,
+ (void (*)(void))template_decapsulate_init },
+ { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))template_decapsulate },
+ { OSSL_FUNC_KEM_FREECTX, (void (*)(void))template_freectx },
+ { OSSL_FUNC_KEM_SET_CTX_PARAMS,
+ (void (*)(void))template_set_ctx_params },
+ { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS,
+ (void (*)(void))template_settable_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/build.info b/crypto/openssl/providers/implementations/keymgmt/build.info
new file mode 100644
index 000000000000..908a32f3555e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/build.info
@@ -0,0 +1,65 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$DH_GOAL=../../libdefault.a ../../libfips.a
+$DSA_GOAL=../../libdefault.a ../../libfips.a
+$EC_GOAL=../../libdefault.a ../../libfips.a
+$ECX_GOAL=../../libdefault.a ../../libfips.a
+$KDF_GOAL=../../libdefault.a ../../libfips.a
+$MAC_GOAL=../../libdefault.a ../../libfips.a
+$RSA_GOAL=../../libdefault.a ../../libfips.a
+$TEMPLATE_GOAL=../../libtemplate.a
+$ML_DSA_GOAL=../../libdefault.a ../../libfips.a
+$ML_KEM_GOAL=../../libdefault.a ../../libfips.a
+$TLS_ML_KEM_HYBRID_GOAL=../../libdefault.a ../../libfips.a
+$SLH_DSA_GOAL=../../libdefault.a ../../libfips.a
+
+IF[{- !$disabled{dh} -}]
+ SOURCE[$DH_GOAL]=dh_kmgmt.c
+ENDIF
+IF[{- !$disabled{dsa} -}]
+ SOURCE[$DSA_GOAL]=dsa_kmgmt.c
+ENDIF
+IF[{- !$disabled{ec} -}]
+ SOURCE[$EC_GOAL]=ec_kmgmt.c
+ENDIF
+
+IF[{- !$disabled{asm} -}]
+ $ECDEF_s390x=S390X_EC_ASM
+
+ # Now that we have defined all the arch specific variables, use the
+ # appropriate one, and define the appropriate macros
+ IF[$ECASM_{- $target{asm_arch} -}]
+ $ECDEF=$ECDEF_{- $target{asm_arch} -}
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{ec} -}]
+ IF[{- !$disabled{ecx} -}]
+ SOURCE[$ECX_GOAL]=ecx_kmgmt.c
+ DEFINE[$ECX_GOAL]=$ECDEF
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{'ml-kem'} -}]
+ IF[{- !$disabled{ec} -}]
+ SOURCE[$TLS_ML_KEM_HYBRID_GOAL]=mlx_kmgmt.c
+ ENDIF
+ SOURCE[$ML_KEM_GOAL]=ml_kem_kmgmt.c
+ENDIF
+
+SOURCE[$RSA_GOAL]=rsa_kmgmt.c
+
+SOURCE[$KDF_GOAL]=kdf_legacy_kmgmt.c
+
+SOURCE[$MAC_GOAL]=mac_legacy_kmgmt.c
+
+SOURCE[$TEMPLATE_GOAL]=template_kmgmt.c
+
+IF[{- !$disabled{'ml-dsa'} -}]
+ SOURCE[$ML_DSA_GOAL]=ml_dsa_kmgmt.c
+ENDIF
+
+IF[{- !$disabled{'slh-dsa'} -}]
+ SOURCE[$SLH_DSA_GOAL]=slh_dsa_kmgmt.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c
new file mode 100644
index 000000000000..0e9e837383f2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/dh_kmgmt.c
@@ -0,0 +1,912 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DH low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+#include "internal/common.h"
+
+#include <string.h> /* strcmp */
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/self_test.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "crypto/dh.h"
+#include "internal/fips.h"
+#include "internal/sizes.h"
+
+static OSSL_FUNC_keymgmt_new_fn dh_newdata;
+static OSSL_FUNC_keymgmt_free_fn dh_freedata;
+static OSSL_FUNC_keymgmt_gen_init_fn dh_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn dhx_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn dh_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn dh_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn dh_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn dh_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn dh_gen_cleanup;
+static OSSL_FUNC_keymgmt_load_fn dh_load;
+static OSSL_FUNC_keymgmt_get_params_fn dh_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn dh_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn dh_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn dh_settable_params;
+static OSSL_FUNC_keymgmt_has_fn dh_has;
+static OSSL_FUNC_keymgmt_match_fn dh_match;
+static OSSL_FUNC_keymgmt_validate_fn dh_validate;
+static OSSL_FUNC_keymgmt_import_fn dh_import;
+static OSSL_FUNC_keymgmt_import_types_fn dh_import_types;
+static OSSL_FUNC_keymgmt_export_fn dh_export;
+static OSSL_FUNC_keymgmt_export_types_fn dh_export_types;
+static OSSL_FUNC_keymgmt_dup_fn dh_dup;
+
+#define DH_POSSIBLE_SELECTIONS \
+ (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)
+
+struct dh_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+
+ FFC_PARAMS *ffc_params;
+ int selection;
+ /* All these parameters are used for parameter generation only */
+ /* If there is a group name then the remaining parameters are not needed */
+ int group_nid;
+ size_t pbits;
+ size_t qbits;
+ unsigned char *seed; /* optional FIPS186-4 param for testing */
+ size_t seedlen;
+ int gindex; /* optional FIPS186-4 generator index (ignored if -1) */
+ int gen_type; /* see dhtype2id */
+ int generator; /* Used by DH_PARAMGEN_TYPE_GENERATOR in non fips mode only */
+ int pcounter;
+ int hindex;
+ int priv_len;
+
+ char *mdname;
+ char *mdprops;
+ OSSL_CALLBACK *cb;
+ void *cbarg;
+ int dh_type;
+};
+
+static int dh_gen_type_name2id_w_default(const char *name, int type)
+{
+ if (strcmp(name, "default") == 0) {
+#ifdef FIPS_MODULE
+ if (type == DH_FLAG_TYPE_DHX)
+ return DH_PARAMGEN_TYPE_FIPS_186_4;
+
+ return DH_PARAMGEN_TYPE_GROUP;
+#else
+ if (type == DH_FLAG_TYPE_DHX)
+ return DH_PARAMGEN_TYPE_FIPS_186_2;
+
+ return DH_PARAMGEN_TYPE_GENERATOR;
+#endif
+ }
+
+ return ossl_dh_gen_type_name2id(name, type);
+}
+
+static void *dh_newdata(void *provctx)
+{
+ DH *dh = NULL;
+
+ if (ossl_prov_is_running()) {
+ dh = ossl_dh_new_ex(PROV_LIBCTX_OF(provctx));
+ if (dh != NULL) {
+ DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+ DH_set_flags(dh, DH_FLAG_TYPE_DH);
+ }
+ }
+ return dh;
+}
+
+static void *dhx_newdata(void *provctx)
+{
+ DH *dh = NULL;
+
+ dh = ossl_dh_new_ex(PROV_LIBCTX_OF(provctx));
+ if (dh != NULL) {
+ DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+ DH_set_flags(dh, DH_FLAG_TYPE_DHX);
+ }
+ return dh;
+}
+
+static void dh_freedata(void *keydata)
+{
+ DH_free(keydata);
+}
+
+static int dh_has(const void *keydata, int selection)
+{
+ const DH *dh = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dh == NULL)
+ return 0;
+ if ((selection & DH_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* the selection is not missing */
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && (DH_get0_pub_key(dh) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && (DH_get0_priv_key(dh) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && (DH_get0_p(dh) != NULL && DH_get0_g(dh) != NULL);
+ return ok;
+}
+
+static int dh_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const DH *dh1 = keydata1;
+ const DH *dh2 = keydata2;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ const BIGNUM *pa = DH_get0_pub_key(dh1);
+ const BIGNUM *pb = DH_get0_pub_key(dh2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const BIGNUM *pa = DH_get0_priv_key(dh1);
+ const BIGNUM *pb = DH_get0_priv_key(dh2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ ok = ok && key_checked;
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ FFC_PARAMS *dhparams1 = ossl_dh_get0_params((DH *)dh1);
+ FFC_PARAMS *dhparams2 = ossl_dh_get0_params((DH *)dh2);
+
+ ok = ok && ossl_ffc_params_cmp(dhparams1, dhparams2, 1);
+ }
+ return ok;
+}
+
+static int dh_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ DH *dh = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dh == NULL)
+ return 0;
+
+ if ((selection & DH_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ /* a key without parameters is meaningless */
+ ok = ok && ossl_dh_params_fromdata(dh, params);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_dh_key_fromdata(dh, params, include_private);
+ }
+
+ return ok;
+}
+
+static int dh_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ DH *dh = keydata;
+ OSSL_PARAM_BLD *tmpl = NULL;
+ OSSL_PARAM *params = NULL;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dh == NULL)
+ return 0;
+
+ if ((selection & DH_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
+ ok = ok && ossl_dh_params_todata(dh, tmpl, NULL);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_dh_key_todata(dh, tmpl, NULL, include_private);
+ }
+
+ if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) {
+ ok = 0;
+ goto err;
+ }
+
+ ok = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ok;
+}
+
+/* IMEXPORT = IMPORT + EXPORT */
+
+# define DH_IMEXPORTABLE_PARAMETERS \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0)
+# define DH_IMEXPORTABLE_PUBLIC_KEY \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
+# define DH_IMEXPORTABLE_PRIVATE_KEY \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+static const OSSL_PARAM dh_all_types[] = {
+ DH_IMEXPORTABLE_PARAMETERS,
+ DH_IMEXPORTABLE_PUBLIC_KEY,
+ DH_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM dh_parameter_types[] = {
+ DH_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM dh_key_types[] = {
+ DH_IMEXPORTABLE_PUBLIC_KEY,
+ DH_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *dh_types[] = {
+ NULL, /* Index 0 = none of them */
+ dh_parameter_types, /* Index 1 = parameter types */
+ dh_key_types, /* Index 2 = key types */
+ dh_all_types /* Index 3 = 1 + 2 */
+};
+
+static const OSSL_PARAM *dh_imexport_types(int selection)
+{
+ int type_select = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
+ type_select += 1;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ type_select += 2;
+ return dh_types[type_select];
+}
+
+static const OSSL_PARAM *dh_import_types(int selection)
+{
+ return dh_imexport_types(selection);
+}
+
+static const OSSL_PARAM *dh_export_types(int selection)
+{
+ return dh_imexport_types(selection);
+}
+
+static ossl_inline int dh_get_params(void *key, OSSL_PARAM params[])
+{
+ DH *dh = key;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, DH_bits(dh)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, DH_security_bits(dh)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, DH_size(dh)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ p->return_size = ossl_dh_key2buf(dh, (unsigned char **)&p->data,
+ p->data_size, 0);
+ if (p->return_size == 0)
+ return 0;
+ }
+
+ return ossl_dh_params_todata(dh, NULL, params)
+ && ossl_dh_key_todata(dh, NULL, params, 1);
+}
+
+static const OSSL_PARAM dh_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ DH_IMEXPORTABLE_PARAMETERS,
+ DH_IMEXPORTABLE_PUBLIC_KEY,
+ DH_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dh_gettable_params(void *provctx)
+{
+ return dh_params;
+}
+
+static const OSSL_PARAM dh_known_settable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dh_settable_params(void *provctx)
+{
+ return dh_known_settable_params;
+}
+
+static int dh_set_params(void *key, const OSSL_PARAM params[])
+{
+ DH *dh = key;
+ const OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p != NULL
+ && (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !ossl_dh_buf2key(dh, p->data, p->data_size)))
+ return 0;
+
+ return 1;
+}
+
+static int dh_validate_public(const DH *dh, int checktype)
+{
+ const BIGNUM *pub_key = NULL;
+ int res = 0;
+
+ DH_get0_key(dh, &pub_key, NULL);
+ if (pub_key == NULL)
+ return 0;
+
+ /*
+ * The partial test is only valid for named group's with q = (p - 1) / 2
+ * but for that case it is also fully sufficient to check the key validity.
+ */
+ if (ossl_dh_is_named_safe_prime_group(dh))
+ return ossl_dh_check_pub_key_partial(dh, pub_key, &res);
+
+ return DH_check_pub_key_ex(dh, pub_key);
+}
+
+static int dh_validate_private(const DH *dh)
+{
+ int status = 0;
+ const BIGNUM *priv_key = NULL;
+
+ DH_get0_key(dh, NULL, &priv_key);
+ if (priv_key == NULL)
+ return 0;
+ return ossl_dh_check_priv_key(dh, priv_key, &status);
+}
+
+static int dh_validate(const void *keydata, int selection, int checktype)
+{
+ const DH *dh = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & DH_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ /*
+ * Both of these functions check parameters. DH_check_params_ex()
+ * performs a lightweight check (e.g. it does not check that p is a
+ * safe prime)
+ */
+ if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK)
+ ok = ok && DH_check_params_ex(dh);
+ else
+ ok = ok && DH_check_ex(dh);
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && dh_validate_public(dh, checktype);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && dh_validate_private(dh);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+ == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ ok = ok && ossl_dh_check_pairwise(dh, 0);
+ return ok;
+}
+
+static void *dh_gen_init_base(void *provctx, int selection,
+ const OSSL_PARAM params[], int type)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct dh_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((selection & (OSSL_KEYMGMT_SELECT_KEYPAIR
+ | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)) == 0)
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->selection = selection;
+ gctx->libctx = libctx;
+ gctx->pbits = 2048;
+ gctx->qbits = 224;
+ gctx->mdname = NULL;
+#ifdef FIPS_MODULE
+ gctx->gen_type = (type == DH_FLAG_TYPE_DHX)
+ ? DH_PARAMGEN_TYPE_FIPS_186_4
+ : DH_PARAMGEN_TYPE_GROUP;
+#else
+ gctx->gen_type = (type == DH_FLAG_TYPE_DHX)
+ ? DH_PARAMGEN_TYPE_FIPS_186_2
+ : DH_PARAMGEN_TYPE_GENERATOR;
+#endif
+ gctx->gindex = -1;
+ gctx->hindex = 0;
+ gctx->pcounter = -1;
+ gctx->generator = DH_GENERATOR_2;
+ gctx->dh_type = type;
+ }
+ if (!dh_gen_set_params(gctx, params)) {
+ OPENSSL_free(gctx);
+ gctx = NULL;
+ }
+ return gctx;
+}
+
+static void *dh_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return dh_gen_init_base(provctx, selection, params, DH_FLAG_TYPE_DH);
+}
+
+static void *dhx_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return dh_gen_init_base(provctx, selection, params, DH_FLAG_TYPE_DHX);
+}
+
+static int dh_gen_set_template(void *genctx, void *templ)
+{
+ struct dh_gen_ctx *gctx = genctx;
+ DH *dh = templ;
+
+ if (!ossl_prov_is_running() || gctx == NULL || dh == NULL)
+ return 0;
+ gctx->ffc_params = ossl_dh_get0_params(dh);
+ return 1;
+}
+
+static int dh_set_gen_seed(struct dh_gen_ctx *gctx, unsigned char *seed,
+ size_t seedlen)
+{
+ OPENSSL_clear_free(gctx->seed, gctx->seedlen);
+ gctx->seed = NULL;
+ gctx->seedlen = 0;
+ if (seed != NULL && seedlen > 0) {
+ gctx->seed = OPENSSL_memdup(seed, seedlen);
+ if (gctx->seed == NULL)
+ return 0;
+ gctx->seedlen = seedlen;
+ }
+ return 1;
+}
+
+static int dh_gen_common_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct dh_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+ int gen_type = -1;
+
+ if (gctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_TYPE);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || ((gen_type =
+ dh_gen_type_name2id_w_default(p->data, gctx->dh_type)) == -1)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ if (gen_type != -1)
+ gctx->gen_type = gen_type;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+ if (p != NULL) {
+ const DH_NAMED_GROUP *group = NULL;
+
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || p->data == NULL
+ || (group = ossl_ffc_name_to_dh_named_group(p->data)) == NULL
+ || ((gctx->group_nid =
+ ossl_ffc_named_group_get_uid(group)) == NID_undef)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ }
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL
+ && !OSSL_PARAM_get_size_t(p, &gctx->pbits))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_PRIV_LEN);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->priv_len))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *dh_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM dh_gen_settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_GENERATOR, NULL),
+ OSSL_PARAM_END
+ };
+ return dh_gen_settable;
+}
+
+static const OSSL_PARAM *dhx_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM dhx_gen_settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_DH_PRIV_LEN, NULL),
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+ OSSL_PARAM_END
+ };
+ return dhx_gen_settable;
+}
+
+static int dhx_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct dh_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (!dh_gen_common_set_params(genctx, params))
+ return 0;
+
+ /* Parameters related to fips186-4 and fips186-2 */
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->gindex))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->pcounter))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->hindex))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED);
+ if (p != NULL
+ && (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !dh_set_gen_seed(gctx, p->data, p->data_size)))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS)) != NULL
+ && !OSSL_PARAM_get_size_t(p, &gctx->qbits))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->mdname);
+ gctx->mdname = OPENSSL_strdup(p->data);
+ if (gctx->mdname == NULL)
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST_PROPS);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->mdprops);
+ gctx->mdprops = OPENSSL_strdup(p->data);
+ if (gctx->mdprops == NULL)
+ return 0;
+ }
+
+ /* Parameters that are not allowed for DHX */
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR);
+ if (p != NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_UNSUPPORTED);
+ return 0;
+ }
+ return 1;
+}
+
+static int dh_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct dh_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (!dh_gen_common_set_params(genctx, params))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DH_GENERATOR);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &gctx->generator))
+ return 0;
+
+ /* Parameters that are not allowed for DH */
+ if (OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX) != NULL
+ || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER) != NULL
+ || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H) != NULL
+ || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED) != NULL
+ || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS) != NULL
+ || OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST) != NULL
+ || OSSL_PARAM_locate_const(params,
+ OSSL_PKEY_PARAM_FFC_DIGEST_PROPS) != NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ return 1;
+}
+
+static int dh_gencb(int p, int n, BN_GENCB *cb)
+{
+ struct dh_gen_ctx *gctx = BN_GENCB_get_arg(cb);
+ OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+ params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+ params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+ return gctx->cb(params, gctx->cbarg);
+}
+
+static void *dh_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ int ret = 0;
+ struct dh_gen_ctx *gctx = genctx;
+ DH *dh = NULL;
+ BN_GENCB *gencb = NULL;
+ FFC_PARAMS *ffc;
+
+ if (!ossl_prov_is_running() || gctx == NULL)
+ return NULL;
+
+ /*
+ * If a group name is selected then the type is group regardless of what
+ * the user selected. This overrides rather than errors for backwards
+ * compatibility.
+ */
+ if (gctx->group_nid != NID_undef)
+ gctx->gen_type = DH_PARAMGEN_TYPE_GROUP;
+
+ /*
+ * Do a bounds check on context gen_type. Must be in range:
+ * DH_PARAMGEN_TYPE_GENERATOR <= gen_type <= DH_PARAMGEN_TYPE_GROUP
+ * Noted here as this needs to be adjusted if a new group type is
+ * added.
+ */
+ if (!ossl_assert((gctx->gen_type >= DH_PARAMGEN_TYPE_GENERATOR)
+ && (gctx->gen_type <= DH_PARAMGEN_TYPE_GROUP))) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "gen_type set to unsupported value %d", gctx->gen_type);
+ return NULL;
+ }
+
+ /* For parameter generation - If there is a group name just create it */
+ if (gctx->gen_type == DH_PARAMGEN_TYPE_GROUP
+ && gctx->ffc_params == NULL) {
+ /* Select a named group if there is not one already */
+ if (gctx->group_nid == NID_undef)
+ gctx->group_nid = ossl_dh_get_named_group_uid_from_size(gctx->pbits);
+ if (gctx->group_nid == NID_undef)
+ return NULL;
+ dh = ossl_dh_new_by_nid_ex(gctx->libctx, gctx->group_nid);
+ if (dh == NULL)
+ return NULL;
+ ffc = ossl_dh_get0_params(dh);
+ } else {
+ dh = ossl_dh_new_ex(gctx->libctx);
+ if (dh == NULL)
+ return NULL;
+ ffc = ossl_dh_get0_params(dh);
+
+ /* Copy the template value if one was passed */
+ if (gctx->ffc_params != NULL
+ && !ossl_ffc_params_copy(ffc, gctx->ffc_params))
+ goto end;
+
+ if (!ossl_ffc_params_set_seed(ffc, gctx->seed, gctx->seedlen))
+ goto end;
+ if (gctx->gindex != -1) {
+ ossl_ffc_params_set_gindex(ffc, gctx->gindex);
+ if (gctx->pcounter != -1)
+ ossl_ffc_params_set_pcounter(ffc, gctx->pcounter);
+ } else if (gctx->hindex != 0) {
+ ossl_ffc_params_set_h(ffc, gctx->hindex);
+ }
+ if (gctx->mdname != NULL)
+ ossl_ffc_set_digest(ffc, gctx->mdname, gctx->mdprops);
+ gctx->cb = osslcb;
+ gctx->cbarg = cbarg;
+ gencb = BN_GENCB_new();
+ if (gencb != NULL)
+ BN_GENCB_set(gencb, dh_gencb, genctx);
+
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ /*
+ * NOTE: The old safe prime generator code is not used in fips mode,
+ * (i.e internally it ignores the generator and chooses a named
+ * group based on pbits.
+ */
+ if (gctx->gen_type == DH_PARAMGEN_TYPE_GENERATOR)
+ ret = DH_generate_parameters_ex(dh, gctx->pbits,
+ gctx->generator, gencb);
+ else
+ ret = ossl_dh_generate_ffc_parameters(dh, gctx->gen_type,
+ gctx->pbits, gctx->qbits,
+ gencb);
+ if (ret <= 0)
+ goto end;
+ }
+ }
+
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ if (ffc->p == NULL || ffc->g == NULL)
+ goto end;
+ if (gctx->priv_len > 0)
+ DH_set_length(dh, (long)gctx->priv_len);
+ ossl_ffc_params_enable_flags(ffc, FFC_PARAM_FLAG_VALIDATE_LEGACY,
+ gctx->gen_type == DH_PARAMGEN_TYPE_FIPS_186_2);
+ if (DH_generate_key(dh) <= 0)
+ goto end;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_self_testing()) {
+ ret = ossl_dh_check_pairwise(dh, 0);
+ if (ret <= 0) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ goto end;
+ }
+ }
+#endif /* FIPS_MODULE */
+ }
+ DH_clear_flags(dh, DH_FLAG_TYPE_MASK);
+ DH_set_flags(dh, gctx->dh_type);
+
+ ret = 1;
+end:
+ if (ret <= 0) {
+ DH_free(dh);
+ dh = NULL;
+ }
+ BN_GENCB_free(gencb);
+ return dh;
+}
+
+static void dh_gen_cleanup(void *genctx)
+{
+ struct dh_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_free(gctx->mdname);
+ OPENSSL_free(gctx->mdprops);
+ OPENSSL_clear_free(gctx->seed, gctx->seedlen);
+ OPENSSL_free(gctx);
+}
+
+static void *dh_load(const void *reference, size_t reference_sz)
+{
+ DH *dh = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(dh)) {
+ /* The contents of the reference is the address to our object */
+ dh = *(DH **)reference;
+ /* We grabbed, so we detach it */
+ *(DH **)reference = NULL;
+ return dh;
+ }
+ return NULL;
+}
+
+static void *dh_dup(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_dh_dup(keydata_from, selection);
+ return NULL;
+}
+
+const OSSL_DISPATCH ossl_dh_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dh_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dh_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dh_gen_set_template },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dh_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))dh_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dh_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dh_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dh_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dh_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dh_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dh_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))dh_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))dh_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dh_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dh_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dh_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dh_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dh_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dh_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dh_dup },
+ OSSL_DISPATCH_END
+};
+
+/* For any DH key, we use the "DH" algorithms regardless of sub-type. */
+static const char *dhx_query_operation_name(int operation_id)
+{
+ return "DH";
+}
+
+const OSSL_DISPATCH ossl_dhx_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dhx_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dhx_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dh_gen_set_template },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dhx_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))dhx_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dh_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dh_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dh_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dh_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dh_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dh_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))dh_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))dh_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dh_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dh_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dh_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dh_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dh_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dh_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dh_export_types },
+ { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+ (void (*)(void))dhx_query_operation_name },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dh_dup },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c
new file mode 100644
index 000000000000..92f661b1f3ab
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/dsa_kmgmt.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "crypto/dsa.h"
+#include "internal/sizes.h"
+#include "internal/nelem.h"
+#include "internal/param_build_set.h"
+
+static OSSL_FUNC_keymgmt_new_fn dsa_newdata;
+static OSSL_FUNC_keymgmt_free_fn dsa_freedata;
+static OSSL_FUNC_keymgmt_gen_init_fn dsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn dsa_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn dsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn dsa_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_get_params_fn dsa_gen_get_params;
+static OSSL_FUNC_keymgmt_gen_gettable_params_fn dsa_gen_gettable_params;
+static OSSL_FUNC_keymgmt_gen_fn dsa_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn dsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_load_fn dsa_load;
+static OSSL_FUNC_keymgmt_get_params_fn dsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn dsa_gettable_params;
+static OSSL_FUNC_keymgmt_has_fn dsa_has;
+static OSSL_FUNC_keymgmt_match_fn dsa_match;
+static OSSL_FUNC_keymgmt_validate_fn dsa_validate;
+static OSSL_FUNC_keymgmt_import_fn dsa_import;
+static OSSL_FUNC_keymgmt_import_types_fn dsa_import_types;
+static OSSL_FUNC_keymgmt_export_fn dsa_export;
+static OSSL_FUNC_keymgmt_export_types_fn dsa_export_types;
+static OSSL_FUNC_keymgmt_dup_fn dsa_dup;
+
+#define DSA_DEFAULT_MD "SHA256"
+#define DSA_POSSIBLE_SELECTIONS \
+ (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS)
+
+struct dsa_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+
+ FFC_PARAMS *ffc_params;
+ int selection;
+ /* All these parameters are used for parameter generation only */
+ size_t pbits;
+ size_t qbits;
+ unsigned char *seed; /* optional FIPS186-4 param for testing */
+ size_t seedlen;
+ int gindex; /* optional FIPS186-4 generator index (ignored if -1) */
+ int gen_type; /* DSA_PARAMGEN_TYPE_FIPS_186_2 or DSA_PARAMGEN_TYPE_FIPS_186_4 */
+ int pcounter;
+ int hindex;
+ char *mdname;
+ char *mdprops;
+ OSSL_CALLBACK *cb;
+ void *cbarg;
+ OSSL_FIPS_IND_DECLARE
+};
+typedef struct dh_name2id_st{
+ const char *name;
+ int id;
+} DSA_GENTYPE_NAME2ID;
+
+static const DSA_GENTYPE_NAME2ID dsatype2id[] = {
+#ifdef FIPS_MODULE
+ { "default", DSA_PARAMGEN_TYPE_FIPS_186_4 },
+#else
+ { "default", DSA_PARAMGEN_TYPE_FIPS_DEFAULT },
+#endif
+ { "fips186_4", DSA_PARAMGEN_TYPE_FIPS_186_4 },
+ { "fips186_2", DSA_PARAMGEN_TYPE_FIPS_186_2 },
+};
+
+static int dsa_gen_type_name2id(const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(dsatype2id); ++i) {
+ if (OPENSSL_strcasecmp(dsatype2id[i].name, name) == 0)
+ return dsatype2id[i].id;
+ }
+ return -1;
+}
+
+static int dsa_key_todata(DSA *dsa, OSSL_PARAM_BLD *bld, OSSL_PARAM params[],
+ int include_private)
+{
+ const BIGNUM *priv = NULL, *pub = NULL;
+
+ if (dsa == NULL)
+ return 0;
+
+ DSA_get0_key(dsa, &pub, &priv);
+ if (include_private
+ && priv != NULL
+ && !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_PRIV_KEY, priv))
+ return 0;
+ if (pub != NULL
+ && !ossl_param_build_set_bn(bld, params, OSSL_PKEY_PARAM_PUB_KEY, pub))
+ return 0;
+
+ return 1;
+}
+
+static void *dsa_newdata(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return NULL;
+ return ossl_dsa_new(PROV_LIBCTX_OF(provctx));
+}
+
+static void dsa_freedata(void *keydata)
+{
+ DSA_free(keydata);
+}
+
+static int dsa_has(const void *keydata, int selection)
+{
+ const DSA *dsa = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dsa == NULL)
+ return 0;
+ if ((selection & DSA_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* the selection is not missing */
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && (DSA_get0_pub_key(dsa) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && (DSA_get0_priv_key(dsa) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && (DSA_get0_p(dsa) != NULL && DSA_get0_g(dsa) != NULL);
+ return ok;
+}
+
+static int dsa_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const DSA *dsa1 = keydata1;
+ const DSA *dsa2 = keydata2;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ const BIGNUM *pa = DSA_get0_pub_key(dsa1);
+ const BIGNUM *pb = DSA_get0_pub_key(dsa2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const BIGNUM *pa = DSA_get0_priv_key(dsa1);
+ const BIGNUM *pb = DSA_get0_priv_key(dsa2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ ok = ok && key_checked;
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ FFC_PARAMS *dsaparams1 = ossl_dsa_get0_params((DSA *)dsa1);
+ FFC_PARAMS *dsaparams2 = ossl_dsa_get0_params((DSA *)dsa2);
+
+ ok = ok && ossl_ffc_params_cmp(dsaparams1, dsaparams2, 1);
+ }
+ return ok;
+}
+
+static int dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ DSA *dsa = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dsa == NULL)
+ return 0;
+
+ if ((selection & DSA_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ /* a key without parameters is meaningless */
+ ok = ok && ossl_dsa_ffc_params_fromdata(dsa, params);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_dsa_key_fromdata(dsa, params, include_private);
+ }
+
+ return ok;
+}
+
+static int dsa_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ DSA *dsa = keydata;
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || dsa == NULL)
+ return 0;
+
+ if ((selection & DSA_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
+ ok = ok && ossl_ffc_params_todata(ossl_dsa_get0_params(dsa), tmpl, NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && dsa_key_todata(dsa, tmpl, NULL, include_private);
+ }
+
+ if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) {
+ ok = 0;
+ goto err;
+ }
+
+ ok = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ok;
+}
+
+/* IMEXPORT = IMPORT + EXPORT */
+
+# define DSA_IMEXPORTABLE_PARAMETERS \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_P, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_Q, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_G, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_FFC_COFACTOR, NULL, 0), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0)
+# define DSA_IMEXPORTABLE_PUBLIC_KEY \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
+# define DSA_IMEXPORTABLE_PRIVATE_KEY \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+static const OSSL_PARAM dsa_all_types[] = {
+ DSA_IMEXPORTABLE_PARAMETERS,
+ DSA_IMEXPORTABLE_PUBLIC_KEY,
+ DSA_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM dsa_parameter_types[] = {
+ DSA_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM dsa_key_types[] = {
+ DSA_IMEXPORTABLE_PUBLIC_KEY,
+ DSA_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *dsa_types[] = {
+ NULL, /* Index 0 = none of them */
+ dsa_parameter_types, /* Index 1 = parameter types */
+ dsa_key_types, /* Index 2 = key types */
+ dsa_all_types /* Index 3 = 1 + 2 */
+};
+
+static const OSSL_PARAM *dsa_imexport_types(int selection)
+{
+ int type_select = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_ALL_PARAMETERS) != 0)
+ type_select += 1;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ type_select += 2;
+ return dsa_types[type_select];
+}
+
+static const OSSL_PARAM *dsa_import_types(int selection)
+{
+ return dsa_imexport_types(selection);
+}
+
+static const OSSL_PARAM *dsa_export_types(int selection)
+{
+ return dsa_imexport_types(selection);
+}
+
+static ossl_inline int dsa_get_params(void *key, OSSL_PARAM params[])
+{
+ DSA *dsa = key;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, DSA_bits(dsa)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, DSA_security_bits(dsa)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, DSA_size(dsa)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+ && !OSSL_PARAM_set_utf8_string(p, DSA_DEFAULT_MD))
+ return 0;
+ return ossl_ffc_params_todata(ossl_dsa_get0_params(dsa), NULL, params)
+ && dsa_key_todata(dsa, NULL, params, 1);
+}
+
+static const OSSL_PARAM dsa_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+ DSA_IMEXPORTABLE_PARAMETERS,
+ DSA_IMEXPORTABLE_PUBLIC_KEY,
+ DSA_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dsa_gettable_params(void *provctx)
+{
+ return dsa_params;
+}
+
+static int dsa_validate_domparams(const DSA *dsa, int checktype)
+{
+ int status = 0;
+
+ return ossl_dsa_check_params(dsa, checktype, &status);
+}
+
+static int dsa_validate_public(const DSA *dsa)
+{
+ int status = 0;
+ const BIGNUM *pub_key = NULL;
+
+ DSA_get0_key(dsa, &pub_key, NULL);
+ if (pub_key == NULL)
+ return 0;
+ return ossl_dsa_check_pub_key(dsa, pub_key, &status);
+}
+
+static int dsa_validate_private(const DSA *dsa)
+{
+ int status = 0;
+ const BIGNUM *priv_key = NULL;
+
+ DSA_get0_key(dsa, NULL, &priv_key);
+ if (priv_key == NULL)
+ return 0;
+ return ossl_dsa_check_priv_key(dsa, priv_key, &status);
+}
+
+static int dsa_validate(const void *keydata, int selection, int checktype)
+{
+ const DSA *dsa = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & DSA_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && dsa_validate_domparams(dsa, checktype);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && dsa_validate_public(dsa);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && dsa_validate_private(dsa);
+
+ /* If the whole key is selected, we do a pairwise validation */
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+ == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ ok = ok && ossl_dsa_check_pairwise(dsa);
+ return ok;
+}
+
+static void *dsa_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct dsa_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running() || (selection & DSA_POSSIBLE_SELECTIONS) == 0)
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->selection = selection;
+ gctx->libctx = libctx;
+ gctx->pbits = 2048;
+ gctx->qbits = 224;
+#ifdef FIPS_MODULE
+ gctx->gen_type = DSA_PARAMGEN_TYPE_FIPS_186_4;
+#else
+ gctx->gen_type = DSA_PARAMGEN_TYPE_FIPS_DEFAULT;
+#endif
+ gctx->gindex = -1;
+ gctx->pcounter = -1;
+ gctx->hindex = 0;
+ OSSL_FIPS_IND_INIT(gctx)
+ }
+ if (!dsa_gen_set_params(gctx, params)) {
+ dsa_gen_cleanup(gctx);
+ gctx = NULL;
+ }
+ return gctx;
+}
+
+static int dsa_gen_set_template(void *genctx, void *templ)
+{
+ struct dsa_gen_ctx *gctx = genctx;
+ DSA *dsa = templ;
+
+ if (!ossl_prov_is_running() || gctx == NULL || dsa == NULL)
+ return 0;
+ gctx->ffc_params = ossl_dsa_get0_params(dsa);
+ return 1;
+}
+
+static int dsa_set_gen_seed(struct dsa_gen_ctx *gctx, unsigned char *seed,
+ size_t seedlen)
+{
+ OPENSSL_clear_free(gctx->seed, gctx->seedlen);
+ gctx->seed = NULL;
+ gctx->seedlen = 0;
+ if (seed != NULL && seedlen > 0) {
+ gctx->seed = OPENSSL_memdup(seed, seedlen);
+ if (gctx->seed == NULL)
+ return 0;
+ gctx->seedlen = seedlen;
+ }
+ return 1;
+}
+
+static int dsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct dsa_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+ int gen_type = -1;
+
+ if (gctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(gctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_PKEY_PARAM_FIPS_SIGN_CHECK))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_TYPE);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || ((gen_type = dsa_gen_type_name2id(p->data)) == -1)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ /*
+ * Only assign context gen_type if it was set by dsa_gen_type_name2id
+ * must be in range:
+ * DSA_PARAMGEN_TYPE_FIPS_186_4 <= gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT
+ */
+ if (gen_type != -1)
+ gctx->gen_type = gen_type;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_GINDEX);
+ if (p != NULL
+ && !OSSL_PARAM_get_int(p, &gctx->gindex))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PCOUNTER);
+ if (p != NULL
+ && !OSSL_PARAM_get_int(p, &gctx->pcounter))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_H);
+ if (p != NULL
+ && !OSSL_PARAM_get_int(p, &gctx->hindex))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_SEED);
+ if (p != NULL
+ && (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !dsa_set_gen_seed(gctx, p->data, p->data_size)))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_PBITS)) != NULL
+ && !OSSL_PARAM_get_size_t(p, &gctx->pbits))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_QBITS)) != NULL
+ && !OSSL_PARAM_get_size_t(p, &gctx->qbits))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->mdname);
+ gctx->mdname = OPENSSL_strdup(p->data);
+ if (gctx->mdname == NULL)
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_FFC_DIGEST_PROPS);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->mdprops);
+ gctx->mdprops = OPENSSL_strdup(p->data);
+ if (gctx->mdprops == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *dsa_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_TYPE, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_PBITS, NULL),
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_FFC_QBITS, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_FFC_DIGEST_PROPS, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_GINDEX, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_FFC_SEED, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_PCOUNTER, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_FFC_H, NULL),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_PKEY_PARAM_FIPS_SIGN_CHECK)
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static int dsa_gen_get_params(void *genctx, OSSL_PARAM *params)
+{
+ struct dsa_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(gctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *dsa_gen_gettable_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM dsa_gen_gettable_params_table[] = {
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+
+ return dsa_gen_gettable_params_table;
+}
+
+static int dsa_gencb(int p, int n, BN_GENCB *cb)
+{
+ struct dsa_gen_ctx *gctx = BN_GENCB_get_arg(cb);
+ OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+ params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+ params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+
+ return gctx->cb(params, gctx->cbarg);
+}
+
+static void *dsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct dsa_gen_ctx *gctx = genctx;
+ DSA *dsa = NULL;
+ BN_GENCB *gencb = NULL;
+ int ret = 0;
+ FFC_PARAMS *ffc;
+
+ if (!ossl_prov_is_running() || gctx == NULL)
+ return NULL;
+
+#ifdef FIPS_MODULE
+ /*
+ * DSA signing is not approved in FIPS 140-3, so there is no
+ * need for DSA keygen either.
+ */
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(gctx, OSSL_FIPS_IND_SETTABLE0,
+ gctx->libctx, "DSA", "Keygen",
+ ossl_fips_config_dsa_sign_disallowed))
+ return 0;
+#endif
+
+ dsa = ossl_dsa_new(gctx->libctx);
+ if (dsa == NULL)
+ return NULL;
+
+ if (gctx->gen_type == DSA_PARAMGEN_TYPE_FIPS_DEFAULT)
+ gctx->gen_type = (gctx->pbits >= 2048 ? DSA_PARAMGEN_TYPE_FIPS_186_4 :
+ DSA_PARAMGEN_TYPE_FIPS_186_2);
+
+ /*
+ * Do a bounds check on context gen_type. Must be in range:
+ * DSA_PARAMGEN_TYPE_FIPS_186_4 <= gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT
+ * Noted here as this needs to be adjusted if a new type is
+ * added.
+ */
+ if (!ossl_assert((gctx->gen_type >= DSA_PARAMGEN_TYPE_FIPS_186_4)
+ && (gctx->gen_type <= DSA_PARAMGEN_TYPE_FIPS_DEFAULT))) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "gen_type set to unsupported value %d", gctx->gen_type);
+ goto end;
+ }
+
+ gctx->cb = osslcb;
+ gctx->cbarg = cbarg;
+ gencb = BN_GENCB_new();
+ if (gencb != NULL)
+ BN_GENCB_set(gencb, dsa_gencb, genctx);
+
+ ffc = ossl_dsa_get0_params(dsa);
+ /* Copy the template value if one was passed */
+ if (gctx->ffc_params != NULL
+ && !ossl_ffc_params_copy(ffc, gctx->ffc_params))
+ goto end;
+
+ if (gctx->seed != NULL
+ && !ossl_ffc_params_set_seed(ffc, gctx->seed, gctx->seedlen))
+ goto end;
+ if (gctx->gindex != -1) {
+ ossl_ffc_params_set_gindex(ffc, gctx->gindex);
+ if (gctx->pcounter != -1)
+ ossl_ffc_params_set_pcounter(ffc, gctx->pcounter);
+ } else if (gctx->hindex != 0) {
+ ossl_ffc_params_set_h(ffc, gctx->hindex);
+ }
+ if (gctx->mdname != NULL)
+ ossl_ffc_set_digest(ffc, gctx->mdname, gctx->mdprops);
+
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+
+ if (ossl_dsa_generate_ffc_parameters(dsa, gctx->gen_type,
+ gctx->pbits, gctx->qbits,
+ gencb) <= 0)
+ goto end;
+ }
+ ossl_ffc_params_enable_flags(ffc, FFC_PARAM_FLAG_VALIDATE_LEGACY,
+ gctx->gen_type == DSA_PARAMGEN_TYPE_FIPS_186_2);
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ if (ffc->p == NULL
+ || ffc->q == NULL
+ || ffc->g == NULL)
+ goto end;
+ if (DSA_generate_key(dsa) <= 0)
+ goto end;
+ }
+ ret = 1;
+end:
+ if (ret <= 0) {
+ DSA_free(dsa);
+ dsa = NULL;
+ }
+ BN_GENCB_free(gencb);
+ return dsa;
+}
+
+static void dsa_gen_cleanup(void *genctx)
+{
+ struct dsa_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_free(gctx->mdname);
+ OPENSSL_free(gctx->mdprops);
+ OPENSSL_clear_free(gctx->seed, gctx->seedlen);
+ OPENSSL_free(gctx);
+}
+
+static void *dsa_load(const void *reference, size_t reference_sz)
+{
+ DSA *dsa = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(dsa)) {
+ /* The contents of the reference is the address to our object */
+ dsa = *(DSA **)reference;
+ /* We grabbed, so we detach it */
+ *(DSA **)reference = NULL;
+ return dsa;
+ }
+ return NULL;
+}
+
+static void *dsa_dup(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_dsa_dup(keydata_from, selection);
+ return NULL;
+}
+
+const OSSL_DISPATCH ossl_dsa_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))dsa_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))dsa_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE, (void (*)(void))dsa_gen_set_template },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))dsa_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))dsa_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN_GET_PARAMS, (void (*)(void))dsa_gen_get_params },
+ { OSSL_FUNC_KEYMGMT_GEN_GETTABLE_PARAMS,
+ (void (*)(void))dsa_gen_gettable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))dsa_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))dsa_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))dsa_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))dsa_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))dsa_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))dsa_gettable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))dsa_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))dsa_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))dsa_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))dsa_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))dsa_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))dsa_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))dsa_export_types },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))dsa_dup },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c
new file mode 100644
index 000000000000..a1d04bc3fdd3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt.c
@@ -0,0 +1,1536 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * ECDH/ECDSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/objects.h>
+#include <openssl/proverr.h>
+#include <openssl/self_test.h>
+#include "crypto/bn.h"
+#include "crypto/ec.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+# include "crypto/sm2.h"
+# endif
+#endif
+
+static OSSL_FUNC_keymgmt_new_fn ec_newdata;
+static OSSL_FUNC_keymgmt_gen_init_fn ec_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_template_fn ec_gen_set_template;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ec_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn ec_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_get_params_fn ec_gen_get_params;
+static OSSL_FUNC_keymgmt_gen_gettable_params_fn ec_gen_gettable_params;
+static OSSL_FUNC_keymgmt_gen_fn ec_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn ec_gen_cleanup;
+static OSSL_FUNC_keymgmt_load_fn ec_load;
+static OSSL_FUNC_keymgmt_free_fn ec_freedata;
+static OSSL_FUNC_keymgmt_get_params_fn ec_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ec_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn ec_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ec_settable_params;
+static OSSL_FUNC_keymgmt_has_fn ec_has;
+static OSSL_FUNC_keymgmt_match_fn ec_match;
+static OSSL_FUNC_keymgmt_validate_fn ec_validate;
+static OSSL_FUNC_keymgmt_import_fn ec_import;
+static OSSL_FUNC_keymgmt_import_types_fn ec_import_types;
+static OSSL_FUNC_keymgmt_export_fn ec_export;
+static OSSL_FUNC_keymgmt_export_types_fn ec_export_types;
+static OSSL_FUNC_keymgmt_query_operation_name_fn ec_query_operation_name;
+static OSSL_FUNC_keymgmt_dup_fn ec_dup;
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static OSSL_FUNC_keymgmt_new_fn sm2_newdata;
+static OSSL_FUNC_keymgmt_gen_init_fn sm2_gen_init;
+static OSSL_FUNC_keymgmt_gen_fn sm2_gen;
+static OSSL_FUNC_keymgmt_get_params_fn sm2_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn sm2_gettable_params;
+static OSSL_FUNC_keymgmt_settable_params_fn sm2_settable_params;
+static OSSL_FUNC_keymgmt_import_fn sm2_import;
+static OSSL_FUNC_keymgmt_query_operation_name_fn sm2_query_operation_name;
+static OSSL_FUNC_keymgmt_validate_fn sm2_validate;
+# endif
+#endif
+
+#define EC_DEFAULT_MD "SHA256"
+#define EC_POSSIBLE_SELECTIONS \
+ (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_ALL_PARAMETERS)
+#define SM2_DEFAULT_MD "SM3"
+
+static
+const char *ec_query_operation_name(int operation_id)
+{
+ switch (operation_id) {
+ case OSSL_OP_KEYEXCH:
+ return "ECDH";
+ case OSSL_OP_SIGNATURE:
+ return "ECDSA";
+ }
+ return NULL;
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static
+const char *sm2_query_operation_name(int operation_id)
+{
+ switch (operation_id) {
+ case OSSL_OP_SIGNATURE:
+ return "SM2";
+ }
+ return NULL;
+}
+# endif
+#endif
+
+/*
+ * Callers of key_to_params MUST make sure that domparams_to_params is also
+ * called!
+ *
+ * This function only exports the bare keypair, domain parameters and other
+ * parameters are exported separately.
+ */
+static ossl_inline
+int key_to_params(const EC_KEY *eckey, OSSL_PARAM_BLD *tmpl,
+ OSSL_PARAM params[], int include_private,
+ unsigned char **pub_key)
+{
+ BIGNUM *x = NULL, *y = NULL;
+ const BIGNUM *priv_key = NULL;
+ const EC_POINT *pub_point = NULL;
+ const EC_GROUP *ecg = NULL;
+ size_t pub_key_len = 0;
+ int ret = 0;
+ BN_CTX *bnctx = NULL;
+
+ if (eckey == NULL
+ || (ecg = EC_KEY_get0_group(eckey)) == NULL)
+ return 0;
+
+ priv_key = EC_KEY_get0_private_key(eckey);
+ pub_point = EC_KEY_get0_public_key(eckey);
+
+ if (pub_point != NULL) {
+ OSSL_PARAM *p = NULL, *px = NULL, *py = NULL;
+ /*
+ * EC_POINT_point2buf() can generate random numbers in some
+ * implementations so we need to ensure we use the correct libctx.
+ */
+ bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eckey));
+ if (bnctx == NULL)
+ goto err;
+
+
+ /* If we are doing a get then check first before decoding the point */
+ if (tmpl == NULL) {
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+ px = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_X);
+ py = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_EC_PUB_Y);
+ }
+
+ if (p != NULL || tmpl != NULL) {
+ /* convert pub_point to a octet string according to the SECG standard */
+ point_conversion_form_t format = EC_KEY_get_conv_form(eckey);
+
+ if ((pub_key_len = EC_POINT_point2buf(ecg, pub_point,
+ format,
+ pub_key, bnctx)) == 0
+ || !ossl_param_build_set_octet_string(tmpl, p,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ *pub_key, pub_key_len))
+ goto err;
+ }
+ if (px != NULL || py != NULL) {
+ if (px != NULL) {
+ x = BN_CTX_get(bnctx);
+ if (x == NULL)
+ goto err;
+ }
+ if (py != NULL) {
+ y = BN_CTX_get(bnctx);
+ if (y == NULL)
+ goto err;
+ }
+
+ if (!EC_POINT_get_affine_coordinates(ecg, pub_point, x, y, bnctx))
+ goto err;
+ if (px != NULL
+ && !ossl_param_build_set_bn(tmpl, px,
+ OSSL_PKEY_PARAM_EC_PUB_X, x))
+ goto err;
+ if (py != NULL
+ && !ossl_param_build_set_bn(tmpl, py,
+ OSSL_PKEY_PARAM_EC_PUB_Y, y))
+ goto err;
+ }
+ }
+
+ if (priv_key != NULL && include_private) {
+ size_t sz;
+ int ecbits;
+
+ /*
+ * Key import/export should never leak the bit length of the secret
+ * scalar in the key.
+ *
+ * For this reason, on export we use padded BIGNUMs with fixed length.
+ *
+ * When importing we also should make sure that, even if short lived,
+ * the newly created BIGNUM is marked with the BN_FLG_CONSTTIME flag as
+ * soon as possible, so that any processing of this BIGNUM might opt for
+ * constant time implementations in the backend.
+ *
+ * Setting the BN_FLG_CONSTTIME flag alone is never enough, we also have
+ * to preallocate the BIGNUM internal buffer to a fixed public size big
+ * enough that operations performed during the processing never trigger
+ * a realloc which would leak the size of the scalar through memory
+ * accesses.
+ *
+ * Fixed Length
+ * ------------
+ *
+ * The order of the large prime subgroup of the curve is our choice for
+ * a fixed public size, as that is generally the upper bound for
+ * generating a private key in EC cryptosystems and should fit all valid
+ * secret scalars.
+ *
+ * For padding on export we just use the bit length of the order
+ * converted to bytes (rounding up).
+ *
+ * For preallocating the BIGNUM storage we look at the number of "words"
+ * required for the internal representation of the order, and we
+ * preallocate 2 extra "words" in case any of the subsequent processing
+ * might temporarily overflow the order length.
+ */
+ ecbits = EC_GROUP_order_bits(ecg);
+ if (ecbits <= 0)
+ goto err;
+ sz = (ecbits + 7) / 8;
+
+ if (!ossl_param_build_set_bn_pad(tmpl, params,
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ priv_key, sz))
+ goto err;
+ }
+ ret = 1;
+ err:
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+static ossl_inline
+int otherparams_to_params(const EC_KEY *ec, OSSL_PARAM_BLD *tmpl,
+ OSSL_PARAM params[])
+{
+ int ecdh_cofactor_mode = 0, group_check = 0;
+ const char *name = NULL;
+ point_conversion_form_t format;
+
+ if (ec == NULL)
+ return 0;
+
+ format = EC_KEY_get_conv_form(ec);
+ name = ossl_ec_pt_format_id2name((int)format);
+ if (name != NULL
+ && !ossl_param_build_set_utf8_string(tmpl, params,
+ OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+ name))
+ return 0;
+
+ group_check = EC_KEY_get_flags(ec) & EC_FLAG_CHECK_NAMED_GROUP_MASK;
+ name = ossl_ec_check_group_type_id2name(group_check);
+ if (name != NULL
+ && !ossl_param_build_set_utf8_string(tmpl, params,
+ OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE,
+ name))
+ return 0;
+
+ if ((EC_KEY_get_enc_flags(ec) & EC_PKEY_NO_PUBKEY) != 0
+ && !ossl_param_build_set_int(tmpl, params,
+ OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, 0))
+ return 0;
+
+ ecdh_cofactor_mode =
+ (EC_KEY_get_flags(ec) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+ return ossl_param_build_set_int(tmpl, params,
+ OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
+ ecdh_cofactor_mode);
+}
+
+static
+void *ec_newdata(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return NULL;
+ return EC_KEY_new_ex(PROV_LIBCTX_OF(provctx), NULL);
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static
+void *sm2_newdata(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return NULL;
+ return EC_KEY_new_by_curve_name_ex(PROV_LIBCTX_OF(provctx), NULL, NID_sm2);
+}
+# endif
+#endif
+
+static
+void ec_freedata(void *keydata)
+{
+ EC_KEY_free(keydata);
+}
+
+static
+int ec_has(const void *keydata, int selection)
+{
+ const EC_KEY *ec = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || ec == NULL)
+ return 0;
+ if ((selection & EC_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* the selection is not missing */
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && (EC_KEY_get0_public_key(ec) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && (EC_KEY_get0_private_key(ec) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && (EC_KEY_get0_group(ec) != NULL);
+ /*
+ * We consider OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS to always be
+ * available, so no extra check is needed other than the previous one
+ * against EC_POSSIBLE_SELECTIONS.
+ */
+ return ok;
+}
+
+static int ec_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const EC_KEY *ec1 = keydata1;
+ const EC_KEY *ec2 = keydata2;
+ const EC_GROUP *group_a = EC_KEY_get0_group(ec1);
+ const EC_GROUP *group_b = EC_KEY_get0_group(ec2);
+ BN_CTX *ctx = NULL;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec1));
+ if (ctx == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && group_a != NULL && group_b != NULL
+ && EC_GROUP_cmp(group_a, group_b, ctx) == 0;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ const EC_POINT *pa = EC_KEY_get0_public_key(ec1);
+ const EC_POINT *pb = EC_KEY_get0_public_key(ec2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && EC_POINT_cmp(group_b, pa, pb, ctx) == 0;
+ key_checked = 1;
+ }
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const BIGNUM *pa = EC_KEY_get0_private_key(ec1);
+ const BIGNUM *pb = EC_KEY_get0_private_key(ec2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ ok = ok && key_checked;
+ }
+ BN_CTX_free(ctx);
+ return ok;
+}
+
+static int common_check_sm2(const EC_KEY *ec, int sm2_wanted)
+{
+ const EC_GROUP *ecg = NULL;
+
+ /*
+ * sm2_wanted: import the keys or domparams only on SM2 Curve
+ * !sm2_wanted: import the keys or domparams only not on SM2 Curve
+ */
+ if ((ecg = EC_KEY_get0_group(ec)) == NULL
+ || (sm2_wanted ^ (EC_GROUP_get_curve_name(ecg) == NID_sm2)))
+ return 0;
+ return 1;
+}
+
+static
+int common_import(void *keydata, int selection, const OSSL_PARAM params[],
+ int sm2_wanted)
+{
+ EC_KEY *ec = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || ec == NULL)
+ return 0;
+
+ /*
+ * In this implementation, we can export/import only keydata in the
+ * following combinations:
+ * - domain parameters (+optional other params)
+ * - public key with associated domain parameters (+optional other params)
+ * - private key with associated domain parameters and optional public key
+ * (+optional other params)
+ *
+ * This means:
+ * - domain parameters must always be requested
+ * - private key must be requested alongside public key
+ * - other parameters are always optional
+ */
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
+ return 0;
+
+ ok = ok && ossl_ec_group_fromdata(ec, params);
+
+ if (!common_check_sm2(ec, sm2_wanted))
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_ec_key_fromdata(ec, params, include_private);
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+ ok = ok && ossl_ec_key_otherparams_fromdata(ec, params);
+
+ return ok;
+}
+
+static
+int ec_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ return common_import(keydata, selection, params, 0);
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static
+int sm2_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ return common_import(keydata, selection, params, 1);
+}
+# endif
+#endif
+
+static
+int ec_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ EC_KEY *ec = keydata;
+ OSSL_PARAM_BLD *tmpl = NULL;
+ OSSL_PARAM *params = NULL;
+ unsigned char *pub_key = NULL, *genbuf = NULL;
+ BN_CTX *bnctx = NULL;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || ec == NULL)
+ return 0;
+
+ /*
+ * In this implementation, we can export/import only keydata in the
+ * following combinations:
+ * - domain parameters (+optional other params)
+ * - public key with associated domain parameters (+optional other params)
+ * - private key with associated public key and domain parameters
+ * (+optional other params)
+ *
+ * This means:
+ * - domain parameters must always be requested
+ * - private key must be requested alongside public key
+ * - other parameters are always optional
+ */
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) == 0)
+ return 0;
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+ && (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
+ if (bnctx == NULL) {
+ ok = 0;
+ goto end;
+ }
+ BN_CTX_start(bnctx);
+ ok = ok && ossl_ec_group_todata(EC_KEY_get0_group(ec), tmpl, NULL,
+ ossl_ec_key_get_libctx(ec),
+ ossl_ec_key_get0_propq(ec),
+ bnctx, &genbuf);
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && key_to_params(ec, tmpl, NULL, include_private, &pub_key);
+ }
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+ ok = ok && otherparams_to_params(ec, tmpl, NULL);
+
+ if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) {
+ ok = 0;
+ goto end;
+ }
+
+ ok = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+end:
+ OSSL_PARAM_BLD_free(tmpl);
+ OPENSSL_free(pub_key);
+ OPENSSL_free(genbuf);
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+ return ok;
+}
+
+/* IMEXPORT = IMPORT + EXPORT */
+
+# define EC_IMEXPORTABLE_DOM_PARAMETERS \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),\
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL)
+
+# define EC_IMEXPORTABLE_PUBLIC_KEY \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0)
+# define EC_IMEXPORTABLE_PRIVATE_KEY \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+# define EC_IMEXPORTABLE_OTHER_PARAMETERS \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL)
+
+/*
+ * Include all the possible combinations of OSSL_PARAM arrays for
+ * ec_imexport_types().
+ *
+ * They are in a separate file as it is ~100 lines of unreadable and
+ * uninteresting machine generated stuff.
+ */
+#include "ec_kmgmt_imexport.inc"
+
+static ossl_inline
+const OSSL_PARAM *ec_imexport_types(int selection)
+{
+ int type_select = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ type_select += 1;
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ type_select += 2;
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ type_select += 4;
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+ type_select += 8;
+ return ec_types[type_select];
+}
+
+static
+const OSSL_PARAM *ec_import_types(int selection)
+{
+ return ec_imexport_types(selection);
+}
+
+static
+const OSSL_PARAM *ec_export_types(int selection)
+{
+ return ec_imexport_types(selection);
+}
+
+static int ec_get_ecm_params(const EC_GROUP *group, OSSL_PARAM params[])
+{
+#ifdef OPENSSL_NO_EC2M
+ return 1;
+#else
+ int ret = 0, m;
+ unsigned int k1 = 0, k2 = 0, k3 = 0;
+ int basis_nid;
+ const char *basis_name = NULL;
+ int fid = EC_GROUP_get_field_type(group);
+
+ if (fid != NID_X9_62_characteristic_two_field)
+ return 1;
+
+ basis_nid = EC_GROUP_get_basis_type(group);
+ if (basis_nid == NID_X9_62_tpBasis)
+ basis_name = SN_X9_62_tpBasis;
+ else if (basis_nid == NID_X9_62_ppBasis)
+ basis_name = SN_X9_62_ppBasis;
+ else
+ goto err;
+
+ m = EC_GROUP_get_degree(group);
+ if (!ossl_param_build_set_int(NULL, params, OSSL_PKEY_PARAM_EC_CHAR2_M, m)
+ || !ossl_param_build_set_utf8_string(NULL, params,
+ OSSL_PKEY_PARAM_EC_CHAR2_TYPE,
+ basis_name))
+ goto err;
+
+ if (basis_nid == NID_X9_62_tpBasis) {
+ if (!EC_GROUP_get_trinomial_basis(group, &k1)
+ || !ossl_param_build_set_int(NULL, params,
+ OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS,
+ (int)k1))
+ goto err;
+ } else {
+ if (!EC_GROUP_get_pentanomial_basis(group, &k1, &k2, &k3)
+ || !ossl_param_build_set_int(NULL, params,
+ OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, (int)k1)
+ || !ossl_param_build_set_int(NULL, params,
+ OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, (int)k2)
+ || !ossl_param_build_set_int(NULL, params,
+ OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, (int)k3))
+ goto err;
+ }
+ ret = 1;
+err:
+ return ret;
+#endif /* OPENSSL_NO_EC2M */
+}
+
+static
+int common_get_params(void *key, OSSL_PARAM params[], int sm2)
+{
+ int ret = 0;
+ EC_KEY *eck = key;
+ const EC_GROUP *ecg = NULL;
+ OSSL_PARAM *p;
+ unsigned char *pub_key = NULL, *genbuf = NULL;
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+ BN_CTX *bnctx = NULL;
+
+ ecg = EC_KEY_get0_group(eck);
+ if (ecg == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET);
+ return 0;
+ }
+
+ libctx = ossl_ec_key_get_libctx(eck);
+ propq = ossl_ec_key_get0_propq(eck);
+
+ bnctx = BN_CTX_new_ex(libctx);
+ if (bnctx == NULL)
+ return 0;
+ BN_CTX_start(bnctx);
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, ECDSA_size(eck)))
+ goto err;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, EC_GROUP_order_bits(ecg)))
+ goto err;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL) {
+ int ecbits, sec_bits;
+
+ ecbits = EC_GROUP_order_bits(ecg);
+
+ /*
+ * The following estimates are based on the values published
+ * in Table 2 of "NIST Special Publication 800-57 Part 1 Revision 4"
+ * at http://dx.doi.org/10.6028/NIST.SP.800-57pt1r4 .
+ *
+ * Note that the above reference explicitly categorizes algorithms in a
+ * discrete set of values {80, 112, 128, 192, 256}, and that it is
+ * relevant only for NIST approved Elliptic Curves, while OpenSSL
+ * applies the same logic also to other curves.
+ *
+ * Classifications produced by other standardazing bodies might differ,
+ * so the results provided for "bits of security" by this provider are
+ * to be considered merely indicative, and it is the users'
+ * responsibility to compare these values against the normative
+ * references that may be relevant for their intent and purposes.
+ */
+ if (ecbits >= 512)
+ sec_bits = 256;
+ else if (ecbits >= 384)
+ sec_bits = 192;
+ else if (ecbits >= 256)
+ sec_bits = 128;
+ else if (ecbits >= 224)
+ sec_bits = 112;
+ else if (ecbits >= 160)
+ sec_bits = 80;
+ else
+ sec_bits = ecbits / 2;
+
+ if (!OSSL_PARAM_set_int(p, sec_bits))
+ goto err;
+ }
+
+ if ((p = OSSL_PARAM_locate(params,
+ OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS))
+ != NULL) {
+ int explicitparams = EC_KEY_decoded_from_explicit_params(eck);
+
+ if (explicitparams < 0
+ || !OSSL_PARAM_set_int(p, explicitparams))
+ goto err;
+ }
+
+ if (!sm2) {
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+ && !OSSL_PARAM_set_utf8_string(p, EC_DEFAULT_MD))
+ goto err;
+ } else {
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+ && !OSSL_PARAM_set_utf8_string(p, SM2_DEFAULT_MD))
+ goto err;
+ }
+
+ /* SM2 doesn't support this PARAM */
+ if (!sm2) {
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH);
+ if (p != NULL) {
+ int ecdh_cofactor_mode = 0;
+
+ ecdh_cofactor_mode =
+ (EC_KEY_get_flags(eck) & EC_FLAG_COFACTOR_ECDH) ? 1 : 0;
+
+ if (!OSSL_PARAM_set_int(p, ecdh_cofactor_mode))
+ goto err;
+ }
+ }
+ if ((p = OSSL_PARAM_locate(params,
+ OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) {
+ const EC_POINT *ecp = EC_KEY_get0_public_key(key);
+
+ if (ecp == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PUBLIC_KEY);
+ goto err;
+ }
+ p->return_size = EC_POINT_point2oct(ecg, ecp,
+ POINT_CONVERSION_UNCOMPRESSED,
+ p->data, p->data_size, bnctx);
+ if (p->return_size == 0)
+ goto err;
+ }
+
+ ret = ec_get_ecm_params(ecg, params)
+ && ossl_ec_group_todata(ecg, NULL, params, libctx, propq, bnctx,
+ &genbuf)
+ && key_to_params(eck, NULL, params, 1, &pub_key)
+ && otherparams_to_params(eck, NULL, params);
+err:
+ OPENSSL_free(genbuf);
+ OPENSSL_free(pub_key);
+ BN_CTX_end(bnctx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+static
+int ec_get_params(void *key, OSSL_PARAM params[])
+{
+ return common_get_params(key, params, 0);
+}
+
+#ifndef OPENSSL_NO_EC2M
+# define EC2M_GETTABLE_DOM_PARAMS \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_M, NULL), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_CHAR2_TYPE, NULL, 0), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_TP_BASIS, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K1, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K2, NULL), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_CHAR2_PP_K3, NULL),
+#else
+# define EC2M_GETTABLE_DOM_PARAMS
+#endif
+
+static const OSSL_PARAM ec_known_gettable_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL),
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC2M_GETTABLE_DOM_PARAMS
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0),
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ec_gettable_params(void *provctx)
+{
+ return ec_known_gettable_params;
+}
+
+static const OSSL_PARAM ec_known_settable_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_INCLUDE_PUBLIC, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *ec_settable_params(void *provctx)
+{
+ return ec_known_settable_params;
+}
+
+static
+int ec_set_params(void *key, const OSSL_PARAM params[])
+{
+ EC_KEY *eck = key;
+ const OSSL_PARAM *p;
+
+ if (key == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!ossl_ec_group_set_params((EC_GROUP *)EC_KEY_get0_group(key), params))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p != NULL) {
+ BN_CTX *ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(key));
+ int ret = 1;
+
+ if (ctx == NULL
+ || p->data_type != OSSL_PARAM_OCTET_STRING
+ || !EC_KEY_oct2key(key, p->data, p->data_size, ctx))
+ ret = 0;
+ BN_CTX_free(ctx);
+ if (!ret)
+ return 0;
+ }
+
+ return ossl_ec_key_otherparams_fromdata(eck, params);
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static
+int sm2_get_params(void *key, OSSL_PARAM params[])
+{
+ return common_get_params(key, params, 1);
+}
+
+static const OSSL_PARAM sm2_known_gettable_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_EC_DECODED_FROM_EXPLICIT_PARAMS, NULL),
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_X, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_PUB_Y, NULL, 0),
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *sm2_gettable_params(ossl_unused void *provctx)
+{
+ return sm2_known_gettable_params;
+}
+
+static const OSSL_PARAM sm2_known_settable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static
+const OSSL_PARAM *sm2_settable_params(ossl_unused void *provctx)
+{
+ return sm2_known_settable_params;
+}
+
+static
+int sm2_validate(const void *keydata, int selection, int checktype)
+{
+ const EC_KEY *eck = keydata;
+ int ok = 1;
+ BN_CTX *ctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & EC_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eck));
+ if (ctx == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && EC_GROUP_check(EC_KEY_get0_group(eck), ctx);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK)
+ ok = ok && ossl_ec_key_public_check_quick(eck, ctx);
+ else
+ ok = ok && ossl_ec_key_public_check(eck, ctx);
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && ossl_sm2_key_private_check(eck);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ ok = ok && ossl_ec_key_pairwise_check(eck, ctx);
+
+ BN_CTX_free(ctx);
+ return ok;
+}
+# endif
+#endif
+
+static
+int ec_validate(const void *keydata, int selection, int checktype)
+{
+ const EC_KEY *eck = keydata;
+ int ok = 1;
+ BN_CTX *ctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & EC_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ ctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(eck));
+ if (ctx == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0) {
+ int flags = EC_KEY_get_flags(eck);
+
+ if ((flags & EC_FLAG_CHECK_NAMED_GROUP) != 0)
+ ok = ok && EC_GROUP_check_named_curve(EC_KEY_get0_group(eck),
+ (flags & EC_FLAG_CHECK_NAMED_GROUP_NIST) != 0, ctx) > 0;
+ else
+ ok = ok && EC_GROUP_check(EC_KEY_get0_group(eck), ctx);
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ if (checktype == OSSL_KEYMGMT_VALIDATE_QUICK_CHECK)
+ ok = ok && ossl_ec_key_public_check_quick(eck, ctx);
+ else
+ ok = ok && ossl_ec_key_public_check(eck, ctx);
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && ossl_ec_key_private_check(eck);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ ok = ok && ossl_ec_key_pairwise_check(eck, ctx);
+
+ BN_CTX_free(ctx);
+ return ok;
+}
+
+struct ec_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+ char *group_name;
+ char *encoding;
+ char *pt_format;
+ char *group_check;
+ char *field_type;
+ BIGNUM *p, *a, *b, *order, *cofactor;
+ unsigned char *gen, *seed;
+ size_t gen_len, seed_len;
+ int selection;
+ int ecdh_mode;
+ EC_GROUP *gen_group;
+ unsigned char *dhkem_ikm;
+ size_t dhkem_ikmlen;
+ OSSL_FIPS_IND_DECLARE
+};
+
+static void *ec_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct ec_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running() || (selection & (EC_POSSIBLE_SELECTIONS)) == 0)
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ gctx->selection = selection;
+ gctx->ecdh_mode = 0;
+ OSSL_FIPS_IND_INIT(gctx)
+ if (!ec_gen_set_params(gctx, params)) {
+ OPENSSL_free(gctx);
+ gctx = NULL;
+ }
+ }
+ return gctx;
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static void *sm2_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ struct ec_gen_ctx *gctx = ec_gen_init(provctx, selection, params);
+
+ if (gctx != NULL) {
+ if (gctx->group_name != NULL)
+ return gctx;
+ if ((gctx->group_name = OPENSSL_strdup("sm2")) != NULL)
+ return gctx;
+ ec_gen_cleanup(gctx);
+ }
+ return NULL;
+}
+# endif
+#endif
+
+static int ec_gen_set_group(void *genctx, const EC_GROUP *src)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_GROUP *group;
+
+ group = EC_GROUP_dup(src);
+ if (group == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+ return 0;
+ }
+ EC_GROUP_free(gctx->gen_group);
+ gctx->gen_group = group;
+ return 1;
+}
+
+static int ec_gen_set_template(void *genctx, void *templ)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_KEY *ec = templ;
+ const EC_GROUP *ec_group;
+
+ if (!ossl_prov_is_running() || gctx == NULL || ec == NULL)
+ return 0;
+ if ((ec_group = EC_KEY_get0_group(ec)) == NULL)
+ return 0;
+ return ec_gen_set_group(gctx, ec_group);
+}
+
+#define COPY_INT_PARAM(params, key, val) \
+p = OSSL_PARAM_locate_const(params, key); \
+if (p != NULL && !OSSL_PARAM_get_int(p, &val)) \
+ goto err;
+
+#define COPY_UTF8_PARAM(params, key, val) \
+p = OSSL_PARAM_locate_const(params, key); \
+if (p != NULL) { \
+ if (p->data_type != OSSL_PARAM_UTF8_STRING) \
+ goto err; \
+ OPENSSL_free(val); \
+ val = OPENSSL_strdup(p->data); \
+ if (val == NULL) \
+ goto err; \
+}
+
+#define COPY_OCTET_PARAM(params, key, val, len) \
+p = OSSL_PARAM_locate_const(params, key); \
+if (p != NULL) { \
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) \
+ goto err; \
+ OPENSSL_free(val); \
+ len = p->data_size; \
+ val = OPENSSL_memdup(p->data, p->data_size); \
+ if (val == NULL) \
+ goto err; \
+}
+
+#define COPY_BN_PARAM(params, key, bn) \
+p = OSSL_PARAM_locate_const(params, key); \
+if (p != NULL) { \
+ if (bn == NULL) \
+ bn = BN_new(); \
+ if (bn == NULL || !OSSL_PARAM_get_BN(p, &bn)) \
+ goto err; \
+}
+
+static int ec_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ int ret = 0;
+ struct ec_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(gctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_PKEY_PARAM_FIPS_KEY_CHECK))
+ goto err;
+
+ COPY_INT_PARAM(params, OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, gctx->ecdh_mode);
+
+ COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_GROUP_NAME, gctx->group_name);
+ COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_FIELD_TYPE, gctx->field_type);
+ COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_ENCODING, gctx->encoding);
+ COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, gctx->pt_format);
+ COPY_UTF8_PARAM(params, OSSL_PKEY_PARAM_EC_GROUP_CHECK_TYPE, gctx->group_check);
+
+ COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_P, gctx->p);
+ COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_A, gctx->a);
+ COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_B, gctx->b);
+ COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_ORDER, gctx->order);
+ COPY_BN_PARAM(params, OSSL_PKEY_PARAM_EC_COFACTOR, gctx->cofactor);
+
+ COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_SEED, gctx->seed, gctx->seed_len);
+ COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_EC_GENERATOR, gctx->gen,
+ gctx->gen_len);
+
+ COPY_OCTET_PARAM(params, OSSL_PKEY_PARAM_DHKEM_IKM, gctx->dhkem_ikm,
+ gctx->dhkem_ikmlen);
+
+ ret = 1;
+err:
+ return ret;
+}
+
+static int ec_gen_set_group_from_params(struct ec_gen_ctx *gctx)
+{
+ int ret = 0;
+ OSSL_PARAM_BLD *bld;
+ OSSL_PARAM *params = NULL;
+ EC_GROUP *group = NULL;
+
+ bld = OSSL_PARAM_BLD_new();
+ if (bld == NULL)
+ return 0;
+
+ if (gctx->encoding != NULL
+ && !OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_ENCODING,
+ gctx->encoding, 0))
+ goto err;
+
+ if (gctx->pt_format != NULL
+ && !OSSL_PARAM_BLD_push_utf8_string(bld,
+ OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT,
+ gctx->pt_format, 0))
+ goto err;
+
+ if (gctx->group_name != NULL) {
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME,
+ gctx->group_name, 0))
+ goto err;
+ /* Ignore any other parameters if there is a group name */
+ goto build;
+ } else if (gctx->field_type != NULL) {
+ if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE,
+ gctx->field_type, 0))
+ goto err;
+ } else {
+ goto err;
+ }
+ if (gctx->p == NULL
+ || gctx->a == NULL
+ || gctx->b == NULL
+ || gctx->order == NULL
+ || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, gctx->p)
+ || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, gctx->a)
+ || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, gctx->b)
+ || !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, gctx->order))
+ goto err;
+
+ if (gctx->cofactor != NULL
+ && !OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR,
+ gctx->cofactor))
+ goto err;
+
+ if (gctx->seed != NULL
+ && !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED,
+ gctx->seed, gctx->seed_len))
+ goto err;
+
+ if (gctx->gen == NULL
+ || !OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR,
+ gctx->gen, gctx->gen_len))
+ goto err;
+build:
+ params = OSSL_PARAM_BLD_to_param(bld);
+ if (params == NULL)
+ goto err;
+ group = EC_GROUP_new_from_params(params, gctx->libctx, NULL);
+ if (group == NULL)
+ goto err;
+
+ EC_GROUP_free(gctx->gen_group);
+ gctx->gen_group = group;
+
+ ret = 1;
+err:
+ OSSL_PARAM_free(params);
+ OSSL_PARAM_BLD_free(bld);
+ return ret;
+}
+
+static const OSSL_PARAM *ec_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_ENCODING, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_POINT_CONVERSION_FORMAT, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_EC_FIELD_TYPE, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_P, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_A, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_B, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_GENERATOR, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_ORDER, NULL, 0),
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_EC_COFACTOR, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_EC_SEED, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_PKEY_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static const OSSL_PARAM *ec_gen_gettable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_ec_gen_gettable_ctx_params[] = {
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_ec_gen_gettable_ctx_params;
+}
+
+static int ec_gen_get_params(void *genctx, OSSL_PARAM *params)
+{
+ struct ec_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(gctx, params))
+ return 0;
+
+ return 1;
+}
+
+static int ec_gen_assign_group(EC_KEY *ec, EC_GROUP *group)
+{
+ if (group == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_PARAMETERS_SET);
+ return 0;
+ }
+ return EC_KEY_set_group(ec, group) > 0;
+}
+
+/*
+ * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation
+ */
+static void *ec_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_KEY *ec = NULL;
+ int ret = 0;
+
+ if (!ossl_prov_is_running()
+ || gctx == NULL
+ || (ec = EC_KEY_new_ex(gctx->libctx, NULL)) == NULL)
+ return NULL;
+
+ if (gctx->gen_group == NULL) {
+ if (!ec_gen_set_group_from_params(gctx))
+ goto err;
+ } else {
+ if (gctx->encoding != NULL) {
+ int flags = ossl_ec_encoding_name2id(gctx->encoding);
+
+ if (flags < 0)
+ goto err;
+ EC_GROUP_set_asn1_flag(gctx->gen_group, flags);
+ }
+ if (gctx->pt_format != NULL) {
+ int format = ossl_ec_pt_format_name2id(gctx->pt_format);
+
+ if (format < 0)
+ goto err;
+ EC_GROUP_set_point_conversion_form(gctx->gen_group, format);
+ }
+ }
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(gctx),
+ OSSL_FIPS_IND_SETTABLE0, gctx->libctx,
+ gctx->gen_group, "EC KeyGen", 1))
+ goto err;
+#endif
+
+ /* We must always assign a group, no matter what */
+ ret = ec_gen_assign_group(ec, gctx->gen_group);
+
+ /* Whether you want it or not, you get a keypair, not just one half */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+#ifndef FIPS_MODULE
+ if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0)
+ ret = ret && ossl_ec_generate_key_dhkem(ec, gctx->dhkem_ikm,
+ gctx->dhkem_ikmlen);
+ else
+#endif
+ ret = ret && EC_KEY_generate_key(ec);
+ }
+
+ if (gctx->ecdh_mode != -1)
+ ret = ret && ossl_ec_set_ecdh_cofactor_mode(ec, gctx->ecdh_mode);
+
+ if (gctx->group_check != NULL)
+ ret = ret && ossl_ec_set_check_group_type_from_name(ec,
+ gctx->group_check);
+#ifdef FIPS_MODULE
+ if (ret > 0
+ && !ossl_fips_self_testing()
+ && EC_KEY_get0_public_key(ec) != NULL
+ && EC_KEY_get0_private_key(ec) != NULL
+ && EC_KEY_get0_group(ec) != NULL) {
+ BN_CTX *bnctx = BN_CTX_new_ex(ossl_ec_key_get_libctx(ec));
+
+ ret = bnctx != NULL && ossl_ec_key_pairwise_check(ec, bnctx);
+ BN_CTX_free(bnctx);
+ if (ret <= 0)
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ }
+#endif /* FIPS_MODULE */
+
+ if (ret)
+ return ec;
+err:
+ /* Something went wrong, throw the key away */
+ EC_KEY_free(ec);
+ return NULL;
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+/*
+ * The callback arguments (osslcb & cbarg) are not used by EC_KEY generation
+ */
+static void *sm2_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct ec_gen_ctx *gctx = genctx;
+ EC_KEY *ec = NULL;
+ int ret = 1;
+
+ if (gctx == NULL
+ || (ec = EC_KEY_new_ex(gctx->libctx, NULL)) == NULL)
+ return NULL;
+
+ if (gctx->gen_group == NULL) {
+ if (!ec_gen_set_group_from_params(gctx))
+ goto err;
+ } else {
+ if (gctx->encoding) {
+ int flags = ossl_ec_encoding_name2id(gctx->encoding);
+
+ if (flags < 0)
+ goto err;
+ EC_GROUP_set_asn1_flag(gctx->gen_group, flags);
+ }
+ if (gctx->pt_format != NULL) {
+ int format = ossl_ec_pt_format_name2id(gctx->pt_format);
+
+ if (format < 0)
+ goto err;
+ EC_GROUP_set_point_conversion_form(gctx->gen_group, format);
+ }
+ }
+
+ /* We must always assign a group, no matter what */
+ ret = ec_gen_assign_group(ec, gctx->gen_group);
+
+ /* Whether you want it or not, you get a keypair, not just one half */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ ret = ret && EC_KEY_generate_key(ec);
+
+ if (ret)
+ return ec;
+err:
+ /* Something went wrong, throw the key away */
+ EC_KEY_free(ec);
+ return NULL;
+}
+# endif
+#endif
+
+static void ec_gen_cleanup(void *genctx)
+{
+ struct ec_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen);
+ EC_GROUP_free(gctx->gen_group);
+ BN_free(gctx->p);
+ BN_free(gctx->a);
+ BN_free(gctx->b);
+ BN_free(gctx->order);
+ BN_free(gctx->cofactor);
+ OPENSSL_free(gctx->group_name);
+ OPENSSL_free(gctx->field_type);
+ OPENSSL_free(gctx->pt_format);
+ OPENSSL_free(gctx->encoding);
+ OPENSSL_free(gctx->seed);
+ OPENSSL_free(gctx->gen);
+ OPENSSL_free(gctx);
+}
+
+static void *common_load(const void *reference, size_t reference_sz,
+ int sm2_wanted)
+{
+ EC_KEY *ec = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(ec)) {
+ /* The contents of the reference is the address to our object */
+ ec = *(EC_KEY **)reference;
+
+ if (!common_check_sm2(ec, sm2_wanted))
+ return NULL;
+
+ /* We grabbed, so we detach it */
+ *(EC_KEY **)reference = NULL;
+ return ec;
+ }
+ return NULL;
+}
+
+static void *ec_load(const void *reference, size_t reference_sz)
+{
+ return common_load(reference, reference_sz, 0);
+}
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+static void *sm2_load(const void *reference, size_t reference_sz)
+{
+ return common_load(reference, reference_sz, 1);
+}
+# endif
+#endif
+
+static void *ec_dup(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_ec_key_dup(keydata_from, selection);
+ return NULL;
+}
+
+const OSSL_DISPATCH ossl_ec_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ec_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))ec_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+ (void (*)(void))ec_gen_set_template },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ec_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))ec_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN_GET_PARAMS, (void (*)(void))ec_gen_get_params },
+ { OSSL_FUNC_KEYMGMT_GEN_GETTABLE_PARAMS,
+ (void (*)(void))ec_gen_gettable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ec_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ec_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ec_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))ec_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))ec_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))ec_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ec_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ec_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ec_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types },
+ { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+ (void (*)(void))ec_query_operation_name },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ec_dup },
+ OSSL_DISPATCH_END
+};
+
+#ifndef FIPS_MODULE
+# ifndef OPENSSL_NO_SM2
+const OSSL_DISPATCH ossl_sm2_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))sm2_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))sm2_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_TEMPLATE,
+ (void (*)(void))ec_gen_set_template },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ec_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))ec_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))sm2_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ec_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))sm2_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ec_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))sm2_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))sm2_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))ec_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))sm2_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ec_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ec_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))sm2_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))sm2_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ec_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ec_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ec_export_types },
+ { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+ (void (*)(void))sm2_query_operation_name },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ec_dup },
+ OSSL_DISPATCH_END
+};
+# endif
+#endif
diff --git a/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc
new file mode 100644
index 000000000000..b142e0df0c46
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/ec_kmgmt_imexport.inc
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+*/
+
+/*
+ * This file is meant to be included from ec_kmgmt.c
+ */
+
+static const OSSL_PARAM ec_private_key_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_public_key_types[] = {
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_key_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_dom_parameters_types[] = {
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_5_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_6_types[] = {
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_key_domp_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_other_parameters_types[] = {
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_9_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_10_types[] = {
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_11_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_all_parameters_types[] = {
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_13_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_14_types[] = {
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM ec_all_types[] = {
+ EC_IMEXPORTABLE_PRIVATE_KEY,
+ EC_IMEXPORTABLE_PUBLIC_KEY,
+ EC_IMEXPORTABLE_DOM_PARAMETERS,
+ EC_IMEXPORTABLE_OTHER_PARAMETERS,
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ec_types[] = {
+ NULL,
+ ec_private_key_types,
+ ec_public_key_types,
+ ec_key_types,
+ ec_dom_parameters_types,
+ ec_5_types,
+ ec_6_types,
+ ec_key_domp_types,
+ ec_other_parameters_types,
+ ec_9_types,
+ ec_10_types,
+ ec_11_types,
+ ec_all_parameters_types,
+ ec_13_types,
+ ec_14_types,
+ ec_all_types
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c
new file mode 100644
index 000000000000..0ebe8b4d59b1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/ecx_kmgmt.c
@@ -0,0 +1,1292 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/self_test.h>
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+#include <openssl/param_build.h>
+#include "crypto/ecx.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/ecx.h"
+#include "prov/securitycheck.h"
+#ifdef S390X_EC_ASM
+# include "s390x_arch.h"
+# include <openssl/sha.h> /* For SHA512_DIGEST_LENGTH */
+#endif
+
+static OSSL_FUNC_keymgmt_new_fn x25519_new_key;
+static OSSL_FUNC_keymgmt_new_fn x448_new_key;
+static OSSL_FUNC_keymgmt_new_fn ed25519_new_key;
+static OSSL_FUNC_keymgmt_new_fn ed448_new_key;
+static OSSL_FUNC_keymgmt_gen_init_fn x25519_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn x448_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ed25519_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ed448_gen_init;
+static OSSL_FUNC_keymgmt_gen_fn x25519_gen;
+static OSSL_FUNC_keymgmt_gen_fn x448_gen;
+static OSSL_FUNC_keymgmt_gen_fn ed25519_gen;
+static OSSL_FUNC_keymgmt_gen_fn ed448_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn ecx_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ecx_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn ecx_gen_settable_params;
+static OSSL_FUNC_keymgmt_load_fn ecx_load;
+static OSSL_FUNC_keymgmt_get_params_fn x25519_get_params;
+static OSSL_FUNC_keymgmt_get_params_fn x448_get_params;
+static OSSL_FUNC_keymgmt_get_params_fn ed25519_get_params;
+static OSSL_FUNC_keymgmt_get_params_fn ed448_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn x25519_gettable_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn x448_gettable_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ed25519_gettable_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ed448_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn x25519_set_params;
+static OSSL_FUNC_keymgmt_set_params_fn x448_set_params;
+static OSSL_FUNC_keymgmt_set_params_fn ed25519_set_params;
+static OSSL_FUNC_keymgmt_set_params_fn ed448_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn x25519_settable_params;
+static OSSL_FUNC_keymgmt_settable_params_fn x448_settable_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ed25519_settable_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ed448_settable_params;
+static OSSL_FUNC_keymgmt_has_fn ecx_has;
+static OSSL_FUNC_keymgmt_match_fn ecx_match;
+static OSSL_FUNC_keymgmt_validate_fn x25519_validate;
+static OSSL_FUNC_keymgmt_validate_fn x448_validate;
+static OSSL_FUNC_keymgmt_validate_fn ed25519_validate;
+static OSSL_FUNC_keymgmt_validate_fn ed448_validate;
+static OSSL_FUNC_keymgmt_import_fn ecx_import;
+static OSSL_FUNC_keymgmt_import_types_fn ecx_imexport_types;
+static OSSL_FUNC_keymgmt_export_fn ecx_export;
+static OSSL_FUNC_keymgmt_export_types_fn ecx_imexport_types;
+static OSSL_FUNC_keymgmt_dup_fn ecx_dup;
+
+#define ECX_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR)
+
+struct ecx_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ ECX_KEY_TYPE type;
+ int selection;
+ unsigned char *dhkem_ikm;
+ size_t dhkem_ikmlen;
+};
+
+#ifdef S390X_EC_ASM
+static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx);
+static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx);
+static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx);
+static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx);
+#endif
+
+#ifdef FIPS_MODULE
+static int ecd_fips140_pairwise_test(const ECX_KEY *ecx, int type, int self_test);
+#endif /* FIPS_MODULE */
+
+static ossl_inline int ecx_key_type_is_ed(ECX_KEY_TYPE type)
+{
+ return type == ECX_KEY_TYPE_ED25519 || type == ECX_KEY_TYPE_ED448;
+}
+
+static void *x25519_new_key(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_X25519, 0,
+ NULL);
+}
+
+static void *x448_new_key(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_X448, 0,
+ NULL);
+}
+
+static void *ed25519_new_key(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_ED25519, 0,
+ NULL);
+}
+
+static void *ed448_new_key(void *provctx)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ecx_key_new(PROV_LIBCTX_OF(provctx), ECX_KEY_TYPE_ED448, 0,
+ NULL);
+}
+
+static int ecx_has(const void *keydata, int selection)
+{
+ const ECX_KEY *key = keydata;
+ int ok = 0;
+
+ if (ossl_prov_is_running() && key != NULL) {
+ /*
+ * ECX keys always have all the parameters they need (i.e. none).
+ * Therefore we always return with 1, if asked about parameters.
+ */
+ ok = 1;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && key->haspubkey;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && key->privkey != NULL;
+ }
+ return ok;
+}
+
+static int ecx_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const ECX_KEY *key1 = keydata1;
+ const ECX_KEY *key2 = keydata2;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && key1->type == key2->type;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ const unsigned char *pa = key1->haspubkey ? key1->pubkey : NULL;
+ const unsigned char *pb = key2->haspubkey ? key2->pubkey : NULL;
+ size_t pal = key1->keylen;
+ size_t pbl = key2->keylen;
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok
+ && key1->type == key2->type
+ && pal == pbl
+ && CRYPTO_memcmp(pa, pb, pal) == 0;
+ key_checked = 1;
+ }
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const unsigned char *pa = key1->privkey;
+ const unsigned char *pb = key2->privkey;
+ size_t pal = key1->keylen;
+ size_t pbl = key2->keylen;
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok
+ && key1->type == key2->type
+ && pal == pbl
+ && CRYPTO_memcmp(pa, pb, pal) == 0;
+ key_checked = 1;
+ }
+ }
+ ok = ok && key_checked;
+ }
+ return ok;
+}
+
+static int ecx_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ ECX_KEY *key = keydata;
+ int ok = 1;
+ int include_private;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+ ok = ok && ossl_ecx_key_fromdata(key, params, include_private);
+
+ return ok;
+}
+
+static int key_to_params(ECX_KEY *key, OSSL_PARAM_BLD *tmpl,
+ OSSL_PARAM params[], int include_private)
+{
+ if (key == NULL)
+ return 0;
+
+ if (!ossl_param_build_set_octet_string(tmpl, params,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ key->pubkey, key->keylen))
+ return 0;
+
+ if (include_private
+ && key->privkey != NULL
+ && !ossl_param_build_set_octet_string(tmpl, params,
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ key->privkey, key->keylen))
+ return 0;
+
+ return 1;
+}
+
+static int ecx_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ ECX_KEY *key = keydata;
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ret = 0;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+
+ if (!key_to_params(key, tmpl, NULL, include_private))
+ goto err;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ret;
+}
+
+#define ECX_KEY_TYPES() \
+OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \
+OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+
+static const OSSL_PARAM ecx_key_types[] = {
+ ECX_KEY_TYPES(),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *ecx_imexport_types(int selection)
+{
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return ecx_key_types;
+ return NULL;
+}
+
+static int ecx_get_params(void *key, OSSL_PARAM params[], int bits, int secbits,
+ int size)
+{
+ ECX_KEY *ecx = key;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, bits))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, secbits))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, size))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL
+ && (ecx->type == ECX_KEY_TYPE_X25519
+ || ecx->type == ECX_KEY_TYPE_X448)) {
+ if (!OSSL_PARAM_set_octet_string(p, ecx->pubkey, ecx->keylen))
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ {
+ /* X25519 and X448 are not approved */
+ int approved = 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_ALG_PARAM_FIPS_APPROVED_INDICATOR);
+ if (p != NULL && !OSSL_PARAM_set_int(p, approved))
+ return 0;
+ }
+#endif
+
+ return key_to_params(ecx, NULL, params, 1);
+}
+
+static int ed_get_params(void *key, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params,
+ OSSL_PKEY_PARAM_MANDATORY_DIGEST)) != NULL
+ && !OSSL_PARAM_set_utf8_string(p, ""))
+ return 0;
+ return 1;
+}
+
+static int x25519_get_params(void *key, OSSL_PARAM params[])
+{
+ return ecx_get_params(key, params, X25519_BITS, X25519_SECURITY_BITS,
+ X25519_KEYLEN);
+}
+
+static int x448_get_params(void *key, OSSL_PARAM params[])
+{
+ return ecx_get_params(key, params, X448_BITS, X448_SECURITY_BITS,
+ X448_KEYLEN);
+}
+
+static int ed25519_get_params(void *key, OSSL_PARAM params[])
+{
+ return ecx_get_params(key, params, ED25519_BITS, ED25519_SECURITY_BITS,
+ ED25519_SIGSIZE)
+ && ed_get_params(key, params);
+}
+
+static int ed448_get_params(void *key, OSSL_PARAM params[])
+{
+ return ecx_get_params(key, params, ED448_BITS, ED448_SECURITY_BITS,
+ ED448_SIGSIZE)
+ && ed_get_params(key, params);
+}
+
+static const OSSL_PARAM ecx_gettable_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ ECX_KEY_TYPES(),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ed_gettable_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0),
+ ECX_KEY_TYPES(),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *x25519_gettable_params(void *provctx)
+{
+ return ecx_gettable_params;
+}
+
+static const OSSL_PARAM *x448_gettable_params(void *provctx)
+{
+ return ecx_gettable_params;
+}
+
+static const OSSL_PARAM *ed25519_gettable_params(void *provctx)
+{
+ return ed_gettable_params;
+}
+
+static const OSSL_PARAM *ed448_gettable_params(void *provctx)
+{
+ return ed_gettable_params;
+}
+
+static int set_property_query(ECX_KEY *ecxkey, const char *propq)
+{
+ OPENSSL_free(ecxkey->propq);
+ ecxkey->propq = NULL;
+ if (propq != NULL) {
+ ecxkey->propq = OPENSSL_strdup(propq);
+ if (ecxkey->propq == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static int ecx_set_params(void *key, const OSSL_PARAM params[])
+{
+ ECX_KEY *ecxkey = key;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p != NULL) {
+ void *buf = ecxkey->pubkey;
+
+ if (p->data_size != ecxkey->keylen
+ || !OSSL_PARAM_get_octet_string(p, &buf, sizeof(ecxkey->pubkey),
+ NULL))
+ return 0;
+ OPENSSL_clear_free(ecxkey->privkey, ecxkey->keylen);
+ ecxkey->privkey = NULL;
+ ecxkey->haspubkey = 1;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || !set_property_query(ecxkey, p->data))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int x25519_set_params(void *key, const OSSL_PARAM params[])
+{
+ return ecx_set_params(key, params);
+}
+
+static int x448_set_params(void *key, const OSSL_PARAM params[])
+{
+ return ecx_set_params(key, params);
+}
+
+static int ed25519_set_params(void *key, const OSSL_PARAM params[])
+{
+ return 1;
+}
+
+static int ed448_set_params(void *key, const OSSL_PARAM params[])
+{
+ return 1;
+}
+
+static const OSSL_PARAM ecx_settable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM ed_settable_params[] = {
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *x25519_settable_params(void *provctx)
+{
+ return ecx_settable_params;
+}
+
+static const OSSL_PARAM *x448_settable_params(void *provctx)
+{
+ return ecx_settable_params;
+}
+
+static const OSSL_PARAM *ed25519_settable_params(void *provctx)
+{
+ return ed_settable_params;
+}
+
+static const OSSL_PARAM *ed448_settable_params(void *provctx)
+{
+ return ed_settable_params;
+}
+
+static void *ecx_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[], ECX_KEY_TYPE type,
+ const char *algdesc)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct ecx_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ gctx->type = type;
+ gctx->selection = selection;
+#ifdef FIPS_MODULE
+ /* X25519/X448 are not FIPS approved, (ED25519/ED448 are approved) */
+ if (algdesc != NULL
+ && !ossl_FIPS_IND_callback(libctx, algdesc, "KeyGen Init")) {
+ OPENSSL_free(gctx);
+ return NULL;
+ }
+#endif
+ } else {
+ return NULL;
+ }
+ if (!ecx_gen_set_params(gctx, params)) {
+ ecx_gen_cleanup(gctx);
+ gctx = NULL;
+ }
+ return gctx;
+}
+
+static void *x25519_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_X25519, "X25519");
+}
+
+static void *x448_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_X448, "X448");
+}
+
+static void *ed25519_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_ED25519, NULL);
+}
+
+static void *ed448_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return ecx_gen_init(provctx, selection, params, ECX_KEY_TYPE_ED448, NULL);
+}
+
+static int ecx_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct ecx_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_GROUP_NAME);
+ if (p != NULL) {
+ const char *groupname = NULL;
+
+ /*
+ * We optionally allow setting a group name - but each algorithm only
+ * support one such name, so all we do is verify that it is the one we
+ * expected.
+ */
+ switch (gctx->type) {
+ case ECX_KEY_TYPE_X25519:
+ groupname = "x25519";
+ break;
+ case ECX_KEY_TYPE_X448:
+ groupname = "x448";
+ break;
+ default:
+ /* We only support this for key exchange at the moment */
+ break;
+ }
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || groupname == NULL
+ || OPENSSL_strcasecmp(p->data, groupname) != 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_KDF_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->propq);
+ gctx->propq = OPENSSL_strdup(p->data);
+ if (gctx->propq == NULL)
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DHKEM_IKM);
+ if (p != NULL) {
+ if (p->data_size != 0 && p->data != NULL) {
+ OPENSSL_free(gctx->dhkem_ikm);
+ gctx->dhkem_ikm = NULL;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&gctx->dhkem_ikm, 0,
+ &gctx->dhkem_ikmlen))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *ecx_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_KDF_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+#ifdef FIPS_MODULE
+/*
+ * Refer: FIPS 140-3 IG 10.3.A Additional Comment 1
+ * Perform a pairwise test for EDDSA by signing and verifying signature.
+ *
+ * The parameter `self_test` is used to indicate whether to create OSSL_SELF_TEST
+ * instance.
+ */
+static int ecd_fips140_pairwise_test(const ECX_KEY *ecx, int type, int self_test)
+{
+ int ret = 0;
+ OSSL_SELF_TEST *st = NULL;
+ OSSL_CALLBACK *cb = NULL;
+ void *cbarg = NULL;
+
+ unsigned char msg[16] = {0};
+ size_t msg_len = sizeof(msg);
+ unsigned char sig[ED448_SIGSIZE] = {0};
+
+ int is_ed25519 = (type == ECX_KEY_TYPE_ED25519) ? 1 : 0;
+ int operation_result = 0;
+
+ /*
+ * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`
+ * is NULL.
+ */
+ if (self_test) {
+ OSSL_SELF_TEST_get_callback(ecx->libctx, &cb, &cbarg);
+
+ st = OSSL_SELF_TEST_new(cb, cbarg);
+ if (st == NULL)
+ return 0;
+ }
+
+ OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT,
+ OSSL_SELF_TEST_DESC_PCT_EDDSA);
+
+ if (is_ed25519)
+ operation_result = ossl_ed25519_sign(sig, msg, msg_len, ecx->pubkey,
+ ecx->privkey, 0, 0, 0, NULL, 0,
+ ecx->libctx, ecx->propq);
+ else
+ operation_result = ossl_ed448_sign(ecx->libctx, sig, msg, msg_len,
+ ecx->pubkey, ecx->privkey, NULL, 0,
+ 0, ecx->propq);
+ if (operation_result != 1)
+ goto err;
+
+ OSSL_SELF_TEST_oncorrupt_byte(st, sig);
+
+ if (is_ed25519)
+ operation_result = ossl_ed25519_verify(msg, msg_len, sig, ecx->pubkey,
+ 0, 0, 0, NULL, 0, ecx->libctx,
+ ecx->propq);
+ else
+ operation_result = ossl_ed448_verify(ecx->libctx, msg, msg_len, sig,
+ ecx->pubkey, NULL, 0, 0, ecx->propq);
+ if (operation_result != 1)
+ goto err;
+
+ ret = 1;
+err:
+ OSSL_SELF_TEST_onend(st, ret);
+ OSSL_SELF_TEST_free(st);
+ return ret;
+}
+#endif
+
+static void *ecx_gen(struct ecx_gen_ctx *gctx)
+{
+ ECX_KEY *key;
+ unsigned char *privkey;
+
+ if (gctx == NULL)
+ return NULL;
+ if ((key = ossl_ecx_key_new(gctx->libctx, gctx->type, 0,
+ gctx->propq)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ return NULL;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ if ((privkey = ossl_ecx_key_allocate_privkey(key)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+#ifndef FIPS_MODULE
+ if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) {
+ if (ecx_key_type_is_ed(gctx->type))
+ goto err;
+ if (!ossl_ecx_dhkem_derive_private(key, privkey,
+ gctx->dhkem_ikm, gctx->dhkem_ikmlen))
+ goto err;
+ } else
+#endif
+ {
+ if (RAND_priv_bytes_ex(gctx->libctx, privkey, key->keylen, 0) <= 0)
+ goto err;
+ }
+
+ switch (gctx->type) {
+ case ECX_KEY_TYPE_X25519:
+ privkey[0] &= 248;
+ privkey[X25519_KEYLEN - 1] &= 127;
+ privkey[X25519_KEYLEN - 1] |= 64;
+ ossl_x25519_public_from_private(key->pubkey, privkey);
+ break;
+ case ECX_KEY_TYPE_X448:
+ privkey[0] &= 252;
+ privkey[X448_KEYLEN - 1] |= 128;
+ ossl_x448_public_from_private(key->pubkey, privkey);
+ break;
+ case ECX_KEY_TYPE_ED25519:
+ if (!ossl_ed25519_public_from_private(gctx->libctx, key->pubkey, privkey,
+ gctx->propq))
+ goto err;
+ break;
+ case ECX_KEY_TYPE_ED448:
+ if (!ossl_ed448_public_from_private(gctx->libctx, key->pubkey, privkey,
+ gctx->propq))
+ goto err;
+ break;
+ }
+ key->haspubkey = 1;
+ return key;
+err:
+ ossl_ecx_key_free(key);
+ return NULL;
+}
+
+static void *x25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct ecx_gen_ctx *gctx = genctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X25519))
+ return s390x_ecx_keygen25519(gctx);
+#endif
+ return ecx_gen(gctx);
+}
+
+static void *x448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct ecx_gen_ctx *gctx = genctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X448))
+ return s390x_ecx_keygen448(gctx);
+#endif
+ return ecx_gen(gctx);
+}
+
+static void *ed25519_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ ECX_KEY *key = NULL;
+ struct ecx_gen_ctx *gctx = genctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED25519)
+ && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED25519)
+ && OPENSSL_s390xcap_P.kdsa[0]
+ & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED25519)) {
+ key = s390x_ecd_keygen25519(gctx);
+ } else
+#endif
+ {
+ key = ecx_gen(gctx);
+ }
+
+#ifdef FIPS_MODULE
+ /* Exit if keygen failed OR we are doing parameter generation (blank key) */
+ if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0))
+ return key;
+ if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED25519, 1) != 1) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ ossl_ecx_key_free(key);
+ return NULL;
+ }
+#endif
+
+ return key;
+}
+
+static void *ed448_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ ECX_KEY *key = NULL;
+ struct ecx_gen_ctx *gctx = genctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_ED448)
+ && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_ED448)
+ && OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_ED448)) {
+ key = s390x_ecd_keygen448(gctx);
+ } else
+#endif
+ {
+ key = ecx_gen(gctx);
+ }
+
+#ifdef FIPS_MODULE
+ /* Exit if keygen failed OR we are doing parameter generation (blank key) */
+ if (!key || ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0))
+ return key;
+ if (ecd_fips140_pairwise_test(key, ECX_KEY_TYPE_ED448, 1) != 1) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ ossl_ecx_key_free(key);
+ return NULL;
+ }
+#endif
+
+ return key;
+}
+
+static void ecx_gen_cleanup(void *genctx)
+{
+ struct ecx_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_clear_free(gctx->dhkem_ikm, gctx->dhkem_ikmlen);
+ OPENSSL_free(gctx->propq);
+ OPENSSL_free(gctx);
+}
+
+void *ecx_load(const void *reference, size_t reference_sz)
+{
+ ECX_KEY *key = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
+ /* The contents of the reference is the address to our object */
+ key = *(ECX_KEY **)reference;
+ /* We grabbed, so we detach it */
+ *(ECX_KEY **)reference = NULL;
+ return key;
+ }
+ return NULL;
+}
+
+static void *ecx_dup(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_ecx_key_dup(keydata_from, selection);
+ return NULL;
+}
+
+static int ecx_key_pairwise_check(const ECX_KEY *ecx, int type)
+{
+ uint8_t pub[64];
+
+ switch (type) {
+ case ECX_KEY_TYPE_X25519:
+ ossl_x25519_public_from_private(pub, ecx->privkey);
+ break;
+ case ECX_KEY_TYPE_X448:
+ ossl_x448_public_from_private(pub, ecx->privkey);
+ break;
+ default:
+ return 0;
+ }
+ return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0;
+}
+
+#ifdef FIPS_MODULE
+/*
+ * FIPS ACVP testing requires the ability to check if the public key is valid
+ * This is not required normally since the ED signature verify does the test
+ * internally.
+ */
+static int ecd_key_pub_check(const ECX_KEY *ecx, int type)
+{
+ switch (type) {
+ case ECX_KEY_TYPE_ED25519:
+ return ossl_ed25519_pubkey_verify(ecx->pubkey, ecx->keylen);
+ case ECX_KEY_TYPE_ED448:
+ return ossl_ed448_pubkey_verify(ecx->pubkey, ecx->keylen);
+ default:
+ return 1;
+ }
+}
+#endif
+
+#ifdef FIPS_MODULE
+static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type)
+{
+ return ecd_fips140_pairwise_test(ecx, type, 0);
+}
+#else
+static int ecd_key_pairwise_check(const ECX_KEY *ecx, int type)
+{
+ uint8_t pub[64];
+
+ switch (type) {
+ case ECX_KEY_TYPE_ED25519:
+ if (!ossl_ed25519_public_from_private(ecx->libctx, pub, ecx->privkey,
+ ecx->propq))
+ return 0;
+ break;
+ case ECX_KEY_TYPE_ED448:
+ if (!ossl_ed448_public_from_private(ecx->libctx, pub, ecx->privkey,
+ ecx->propq))
+ return 0;
+ break;
+ default:
+ return 0;
+ }
+ return CRYPTO_memcmp(ecx->pubkey, pub, ecx->keylen) == 0;
+}
+#endif
+
+static int ecx_validate(const void *keydata, int selection, int type,
+ size_t keylen)
+{
+ const ECX_KEY *ecx = keydata;
+ int ok = keylen == ecx->keylen;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & ECX_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ if (!ok) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH);
+ return 0;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ ok = ok && ecx->haspubkey;
+#ifdef FIPS_MODULE
+ ok = ok && ecd_key_pub_check(ecx, type);
+#endif
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && ecx->privkey != NULL;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != OSSL_KEYMGMT_SELECT_KEYPAIR)
+ return ok;
+
+ if (ecx_key_type_is_ed(type))
+ ok = ok && ecd_key_pairwise_check(ecx, type);
+ else
+ ok = ok && ecx_key_pairwise_check(ecx, type);
+
+ return ok;
+}
+
+static int x25519_validate(const void *keydata, int selection, int checktype)
+{
+ return ecx_validate(keydata, selection, ECX_KEY_TYPE_X25519, X25519_KEYLEN);
+}
+
+static int x448_validate(const void *keydata, int selection, int checktype)
+{
+ return ecx_validate(keydata, selection, ECX_KEY_TYPE_X448, X448_KEYLEN);
+}
+
+static int ed25519_validate(const void *keydata, int selection, int checktype)
+{
+ return ecx_validate(keydata, selection, ECX_KEY_TYPE_ED25519, ED25519_KEYLEN);
+}
+
+static int ed448_validate(const void *keydata, int selection, int checktype)
+{
+ return ecx_validate(keydata, selection, ECX_KEY_TYPE_ED448, ED448_KEYLEN);
+}
+
+#define MAKE_KEYMGMT_FUNCTIONS(alg) \
+ const OSSL_DISPATCH ossl_##alg##_keymgmt_functions[] = { \
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))alg##_new_key }, \
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ossl_ecx_key_free }, \
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))alg##_get_params }, \
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))alg##_gettable_params }, \
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))alg##_set_params }, \
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))alg##_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ecx_has }, \
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ecx_match }, \
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))alg##_validate }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ecx_import }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ecx_imexport_types }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ecx_export }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ecx_imexport_types }, \
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))alg##_gen_init }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))ecx_gen_set_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \
+ (void (*)(void))ecx_gen_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))alg##_gen }, \
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ecx_gen_cleanup }, \
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))ecx_load }, \
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ecx_dup }, \
+ OSSL_DISPATCH_END \
+ };
+
+MAKE_KEYMGMT_FUNCTIONS(x25519)
+MAKE_KEYMGMT_FUNCTIONS(x448)
+MAKE_KEYMGMT_FUNCTIONS(ed25519)
+MAKE_KEYMGMT_FUNCTIONS(ed448)
+
+#ifdef S390X_EC_ASM
+# include "s390x_arch.h"
+
+static void *s390x_ecx_keygen25519(struct ecx_gen_ctx *gctx)
+{
+ static const unsigned char generator[] = {
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_X25519, 1,
+ gctx->propq);
+ unsigned char *privkey = NULL, *pubkey;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ pubkey = key->pubkey;
+
+ privkey = ossl_ecx_key_allocate_privkey(key);
+ if (privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+#ifndef FIPS_MODULE
+ if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) {
+ if (gctx->type != ECX_KEY_TYPE_X25519)
+ goto err;
+ if (!ossl_ecx_dhkem_derive_private(key, privkey,
+ gctx->dhkem_ikm, gctx->dhkem_ikmlen))
+ goto err;
+ } else
+#endif
+ {
+ if (RAND_priv_bytes_ex(gctx->libctx, privkey, X25519_KEYLEN, 0) <= 0)
+ goto err;
+ }
+
+ privkey[0] &= 248;
+ privkey[31] &= 127;
+ privkey[31] |= 64;
+
+ if (s390x_x25519_mul(pubkey, generator, privkey) != 1)
+ goto err;
+ key->haspubkey = 1;
+ return key;
+ err:
+ ossl_ecx_key_free(key);
+ return NULL;
+}
+
+static void *s390x_ecx_keygen448(struct ecx_gen_ctx *gctx)
+{
+ static const unsigned char generator[] = {
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_X448, 1,
+ gctx->propq);
+ unsigned char *privkey = NULL, *pubkey;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ pubkey = key->pubkey;
+
+ privkey = ossl_ecx_key_allocate_privkey(key);
+ if (privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+#ifndef FIPS_MODULE
+ if (gctx->dhkem_ikm != NULL && gctx->dhkem_ikmlen != 0) {
+ if (gctx->type != ECX_KEY_TYPE_X448)
+ goto err;
+ if (!ossl_ecx_dhkem_derive_private(key, privkey,
+ gctx->dhkem_ikm, gctx->dhkem_ikmlen))
+ goto err;
+ } else
+#endif
+ {
+ if (RAND_priv_bytes_ex(gctx->libctx, privkey, X448_KEYLEN, 0) <= 0)
+ goto err;
+ }
+
+ privkey[0] &= 252;
+ privkey[55] |= 128;
+
+ if (s390x_x448_mul(pubkey, generator, privkey) != 1)
+ goto err;
+ key->haspubkey = 1;
+ return key;
+ err:
+ ossl_ecx_key_free(key);
+ return NULL;
+}
+
+static void *s390x_ecd_keygen25519(struct ecx_gen_ctx *gctx)
+{
+ static const unsigned char generator_x[] = {
+ 0x1a, 0xd5, 0x25, 0x8f, 0x60, 0x2d, 0x56, 0xc9, 0xb2, 0xa7, 0x25, 0x95,
+ 0x60, 0xc7, 0x2c, 0x69, 0x5c, 0xdc, 0xd6, 0xfd, 0x31, 0xe2, 0xa4, 0xc0,
+ 0xfe, 0x53, 0x6e, 0xcd, 0xd3, 0x36, 0x69, 0x21
+ };
+ static const unsigned char generator_y[] = {
+ 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ };
+ unsigned char x_dst[32], buff[SHA512_DIGEST_LENGTH];
+ ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_ED25519, 1,
+ gctx->propq);
+ unsigned char *privkey = NULL, *pubkey;
+ unsigned int sz;
+ EVP_MD *sha = NULL;
+ int j;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ pubkey = key->pubkey;
+
+ privkey = ossl_ecx_key_allocate_privkey(key);
+ if (privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ if (RAND_priv_bytes_ex(gctx->libctx, privkey, ED25519_KEYLEN, 0) <= 0)
+ goto err;
+
+ sha = EVP_MD_fetch(gctx->libctx, "SHA512", gctx->propq);
+ if (sha == NULL)
+ goto err;
+ j = EVP_Digest(privkey, 32, buff, &sz, sha, NULL);
+ EVP_MD_free(sha);
+ if (!j)
+ goto err;
+
+ buff[0] &= 248;
+ buff[31] &= 63;
+ buff[31] |= 64;
+
+ if (s390x_ed25519_mul(x_dst, pubkey,
+ generator_x, generator_y, buff) != 1)
+ goto err;
+
+ pubkey[31] |= ((x_dst[0] & 0x01) << 7);
+ key->haspubkey = 1;
+ return key;
+ err:
+ ossl_ecx_key_free(key);
+ return NULL;
+}
+
+static void *s390x_ecd_keygen448(struct ecx_gen_ctx *gctx)
+{
+ static const unsigned char generator_x[] = {
+ 0x5e, 0xc0, 0x0c, 0xc7, 0x2b, 0xa8, 0x26, 0x26, 0x8e, 0x93, 0x00, 0x8b,
+ 0xe1, 0x80, 0x3b, 0x43, 0x11, 0x65, 0xb6, 0x2a, 0xf7, 0x1a, 0xae, 0x12,
+ 0x64, 0xa4, 0xd3, 0xa3, 0x24, 0xe3, 0x6d, 0xea, 0x67, 0x17, 0x0f, 0x47,
+ 0x70, 0x65, 0x14, 0x9e, 0xda, 0x36, 0xbf, 0x22, 0xa6, 0x15, 0x1d, 0x22,
+ 0xed, 0x0d, 0xed, 0x6b, 0xc6, 0x70, 0x19, 0x4f, 0x00
+ };
+ static const unsigned char generator_y[] = {
+ 0x14, 0xfa, 0x30, 0xf2, 0x5b, 0x79, 0x08, 0x98, 0xad, 0xc8, 0xd7, 0x4e,
+ 0x2c, 0x13, 0xbd, 0xfd, 0xc4, 0x39, 0x7c, 0xe6, 0x1c, 0xff, 0xd3, 0x3a,
+ 0xd7, 0xc2, 0xa0, 0x05, 0x1e, 0x9c, 0x78, 0x87, 0x40, 0x98, 0xa3, 0x6c,
+ 0x73, 0x73, 0xea, 0x4b, 0x62, 0xc7, 0xc9, 0x56, 0x37, 0x20, 0x76, 0x88,
+ 0x24, 0xbc, 0xb6, 0x6e, 0x71, 0x46, 0x3f, 0x69, 0x00
+ };
+ unsigned char x_dst[57], buff[114];
+ ECX_KEY *key = ossl_ecx_key_new(gctx->libctx, ECX_KEY_TYPE_ED448, 1,
+ gctx->propq);
+ unsigned char *privkey = NULL, *pubkey;
+ EVP_MD_CTX *hashctx = NULL;
+ EVP_MD *shake = NULL;
+
+ if (key == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ pubkey = key->pubkey;
+
+ privkey = ossl_ecx_key_allocate_privkey(key);
+ if (privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EC_LIB);
+ goto err;
+ }
+
+ shake = EVP_MD_fetch(gctx->libctx, "SHAKE256", gctx->propq);
+ if (shake == NULL)
+ goto err;
+ if (RAND_priv_bytes_ex(gctx->libctx, privkey, ED448_KEYLEN, 0) <= 0)
+ goto err;
+
+ hashctx = EVP_MD_CTX_new();
+ if (hashctx == NULL)
+ goto err;
+ if (EVP_DigestInit_ex(hashctx, shake, NULL) != 1)
+ goto err;
+ if (EVP_DigestUpdate(hashctx, privkey, 57) != 1)
+ goto err;
+ if (EVP_DigestFinalXOF(hashctx, buff, sizeof(buff)) != 1)
+ goto err;
+
+ buff[0] &= -4;
+ buff[55] |= 0x80;
+ buff[56] = 0;
+
+ if (s390x_ed448_mul(x_dst, pubkey,
+ generator_x, generator_y, buff) != 1)
+ goto err;
+
+ pubkey[56] |= ((x_dst[0] & 0x01) << 7);
+ EVP_MD_CTX_free(hashctx);
+ EVP_MD_free(shake);
+ key->haspubkey = 1;
+ return key;
+ err:
+ ossl_ecx_key_free(key);
+ EVP_MD_CTX_free(hashctx);
+ EVP_MD_free(shake);
+ return NULL;
+}
+#endif
diff --git a/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c
new file mode 100644
index 000000000000..deb49600066d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/kdf_legacy_kmgmt.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This implemments a dummy key manager for legacy KDFs that still support the
+ * old way of performing a KDF via EVP_PKEY_derive(). New KDFs should not be
+ * implemented this way. In reality there is no key data for such KDFs, so this
+ * key manager does very little.
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/kdfexchange.h"
+
+static OSSL_FUNC_keymgmt_new_fn kdf_newdata;
+static OSSL_FUNC_keymgmt_free_fn kdf_freedata;
+static OSSL_FUNC_keymgmt_has_fn kdf_has;
+
+KDF_DATA *ossl_kdf_data_new(void *provctx)
+{
+ KDF_DATA *kdfdata;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ kdfdata = OPENSSL_zalloc(sizeof(*kdfdata));
+ if (kdfdata == NULL)
+ return NULL;
+
+ if (!CRYPTO_NEW_REF(&kdfdata->refcnt, 1)) {
+ OPENSSL_free(kdfdata);
+ return NULL;
+ }
+ kdfdata->libctx = PROV_LIBCTX_OF(provctx);
+
+ return kdfdata;
+}
+
+void ossl_kdf_data_free(KDF_DATA *kdfdata)
+{
+ int ref = 0;
+
+ if (kdfdata == NULL)
+ return;
+
+ CRYPTO_DOWN_REF(&kdfdata->refcnt, &ref);
+ if (ref > 0)
+ return;
+
+ CRYPTO_FREE_REF(&kdfdata->refcnt);
+ OPENSSL_free(kdfdata);
+}
+
+int ossl_kdf_data_up_ref(KDF_DATA *kdfdata)
+{
+ int ref = 0;
+
+ /* This is effectively doing a new operation on the KDF_DATA and should be
+ * adequately guarded again modules' error states. However, both current
+ * calls here are guarded properly in exchange/kdf_exch.c. Thus, it
+ * could be removed here. The concern is that something in the future
+ * might call this function without adequate guards. It's a cheap call,
+ * it seems best to leave it even though it is currently redundant.
+ */
+ if (!ossl_prov_is_running())
+ return 0;
+
+ CRYPTO_UP_REF(&kdfdata->refcnt, &ref);
+ return 1;
+}
+
+static void *kdf_newdata(void *provctx)
+{
+ return ossl_kdf_data_new(provctx);
+}
+
+static void kdf_freedata(void *kdfdata)
+{
+ ossl_kdf_data_free(kdfdata);
+}
+
+static int kdf_has(const void *keydata, int selection)
+{
+ return 1; /* nothing is missing */
+}
+
+const OSSL_DISPATCH ossl_kdf_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))kdf_newdata },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))kdf_freedata },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))kdf_has },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c
new file mode 100644
index 000000000000..a83017e3aebf
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/mac_legacy_kmgmt.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* We need to use some engine deprecated APIs */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/proverr.h>
+#include <openssl/param_build.h>
+#ifndef FIPS_MODULE
+# include <openssl/engine.h>
+#endif
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/macsignature.h"
+
+static OSSL_FUNC_keymgmt_new_fn mac_new;
+static OSSL_FUNC_keymgmt_free_fn mac_free;
+static OSSL_FUNC_keymgmt_gen_init_fn mac_gen_init;
+static OSSL_FUNC_keymgmt_gen_fn mac_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn mac_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn mac_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn mac_gen_settable_params;
+static OSSL_FUNC_keymgmt_get_params_fn mac_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn mac_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn mac_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn mac_settable_params;
+static OSSL_FUNC_keymgmt_has_fn mac_has;
+static OSSL_FUNC_keymgmt_match_fn mac_match;
+static OSSL_FUNC_keymgmt_import_fn mac_import;
+static OSSL_FUNC_keymgmt_import_types_fn mac_imexport_types;
+static OSSL_FUNC_keymgmt_export_fn mac_export;
+static OSSL_FUNC_keymgmt_export_types_fn mac_imexport_types;
+
+static OSSL_FUNC_keymgmt_new_fn mac_new_cmac;
+static OSSL_FUNC_keymgmt_gettable_params_fn cmac_gettable_params;
+static OSSL_FUNC_keymgmt_import_types_fn cmac_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn cmac_imexport_types;
+static OSSL_FUNC_keymgmt_gen_init_fn cmac_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_params_fn cmac_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn cmac_gen_settable_params;
+
+struct mac_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+ int selection;
+ unsigned char *priv_key;
+ size_t priv_key_len;
+ PROV_CIPHER cipher;
+};
+
+MAC_KEY *ossl_mac_key_new(OSSL_LIB_CTX *libctx, int cmac)
+{
+ MAC_KEY *mackey;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ mackey = OPENSSL_zalloc(sizeof(*mackey));
+ if (mackey == NULL)
+ return NULL;
+
+ if (!CRYPTO_NEW_REF(&mackey->refcnt, 1)) {
+ OPENSSL_free(mackey);
+ return NULL;
+ }
+ mackey->libctx = libctx;
+ mackey->cmac = cmac;
+
+ return mackey;
+}
+
+void ossl_mac_key_free(MAC_KEY *mackey)
+{
+ int ref = 0;
+
+ if (mackey == NULL)
+ return;
+
+ CRYPTO_DOWN_REF(&mackey->refcnt, &ref);
+ if (ref > 0)
+ return;
+
+ OPENSSL_secure_clear_free(mackey->priv_key, mackey->priv_key_len);
+ OPENSSL_free(mackey->properties);
+ ossl_prov_cipher_reset(&mackey->cipher);
+ CRYPTO_FREE_REF(&mackey->refcnt);
+ OPENSSL_free(mackey);
+}
+
+int ossl_mac_key_up_ref(MAC_KEY *mackey)
+{
+ int ref = 0;
+
+ /* This is effectively doing a new operation on the MAC_KEY and should be
+ * adequately guarded again modules' error states. However, both current
+ * calls here are guarded properly in signature/mac_legacy.c. Thus, it
+ * could be removed here. The concern is that something in the future
+ * might call this function without adequate guards. It's a cheap call,
+ * it seems best to leave it even though it is currently redundant.
+ */
+ if (!ossl_prov_is_running())
+ return 0;
+
+ CRYPTO_UP_REF(&mackey->refcnt, &ref);
+ return 1;
+}
+
+static void *mac_new(void *provctx)
+{
+ return ossl_mac_key_new(PROV_LIBCTX_OF(provctx), 0);
+}
+
+static void *mac_new_cmac(void *provctx)
+{
+ return ossl_mac_key_new(PROV_LIBCTX_OF(provctx), 1);
+}
+
+static void mac_free(void *mackey)
+{
+ ossl_mac_key_free(mackey);
+}
+
+static int mac_has(const void *keydata, int selection)
+{
+ const MAC_KEY *key = keydata;
+ int ok = 0;
+
+ if (ossl_prov_is_running() && key != NULL) {
+ /*
+ * MAC keys always have all the parameters they need (i.e. none).
+ * Therefore we always return with 1, if asked about parameters.
+ * Similarly for public keys.
+ */
+ ok = 1;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = key->priv_key != NULL;
+ }
+ return ok;
+}
+
+static int mac_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const MAC_KEY *key1 = keydata1;
+ const MAC_KEY *key2 = keydata2;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ if ((key1->priv_key == NULL && key2->priv_key != NULL)
+ || (key1->priv_key != NULL && key2->priv_key == NULL)
+ || key1->priv_key_len != key2->priv_key_len
+ || (key1->cipher.cipher == NULL && key2->cipher.cipher != NULL)
+ || (key1->cipher.cipher != NULL && key2->cipher.cipher == NULL))
+ ok = 0;
+ else
+ ok = ok && (key1->priv_key == NULL /* implies key2->privkey == NULL */
+ || CRYPTO_memcmp(key1->priv_key, key2->priv_key,
+ key1->priv_key_len) == 0);
+ if (key1->cipher.cipher != NULL)
+ ok = ok && EVP_CIPHER_is_a(key1->cipher.cipher,
+ EVP_CIPHER_get0_name(key2->cipher.cipher));
+ }
+ return ok;
+}
+
+static int mac_key_fromdata(MAC_KEY *key, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ OPENSSL_secure_clear_free(key->priv_key, key->priv_key_len);
+ /* allocate at least one byte to distinguish empty key from no key set */
+ key->priv_key = OPENSSL_secure_malloc(p->data_size > 0 ? p->data_size : 1);
+ if (key->priv_key == NULL)
+ return 0;
+ memcpy(key->priv_key, p->data, p->data_size);
+ key->priv_key_len = p->data_size;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ OPENSSL_free(key->properties);
+ key->properties = OPENSSL_strdup(p->data);
+ if (key->properties == NULL)
+ return 0;
+ }
+
+ if (key->cmac && !ossl_prov_cipher_load_from_params(&key->cipher, params,
+ key->libctx)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ if (key->priv_key != NULL)
+ return 1;
+
+ return 0;
+}
+
+static int mac_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ MAC_KEY *key = keydata;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0)
+ return 0;
+
+ return mac_key_fromdata(key, params);
+}
+
+static int key_to_params(MAC_KEY *key, OSSL_PARAM_BLD *tmpl,
+ OSSL_PARAM params[])
+{
+ if (key == NULL)
+ return 0;
+
+ if (key->priv_key != NULL
+ && !ossl_param_build_set_octet_string(tmpl, params,
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ key->priv_key, key->priv_key_len))
+ return 0;
+
+ if (key->cipher.cipher != NULL
+ && !ossl_param_build_set_utf8_string(tmpl, params,
+ OSSL_PKEY_PARAM_CIPHER,
+ EVP_CIPHER_get0_name(key->cipher.cipher)))
+ return 0;
+
+#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
+ if (key->cipher.engine != NULL
+ && !ossl_param_build_set_utf8_string(tmpl, params,
+ OSSL_PKEY_PARAM_ENGINE,
+ ENGINE_get_id(key->cipher.engine)))
+ return 0;
+#endif
+
+ return 1;
+}
+
+static int mac_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ MAC_KEY *key = keydata;
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ret = 0;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0
+ && !key_to_params(key, tmpl, NULL))
+ goto err;
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ret;
+}
+
+static const OSSL_PARAM mac_key_types[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *mac_imexport_types(int selection)
+{
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ return mac_key_types;
+ return NULL;
+}
+
+static const OSSL_PARAM cmac_key_types[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_ENGINE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *cmac_imexport_types(int selection)
+{
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ return cmac_key_types;
+ return NULL;
+}
+
+static int mac_get_params(void *key, OSSL_PARAM params[])
+{
+ return key_to_params(key, NULL, params);
+}
+
+static const OSSL_PARAM *mac_gettable_params(void *provctx)
+{
+ static const OSSL_PARAM gettable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return gettable_params;
+}
+
+static const OSSL_PARAM *cmac_gettable_params(void *provctx)
+{
+ static const OSSL_PARAM gettable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_ENGINE, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return gettable_params;
+}
+
+static int mac_set_params(void *keydata, const OSSL_PARAM params[])
+{
+ MAC_KEY *key = keydata;
+ const OSSL_PARAM *p;
+
+ if (key == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL)
+ return mac_key_fromdata(key, params);
+
+ return 1;
+}
+
+static const OSSL_PARAM *mac_settable_params(void *provctx)
+{
+ static const OSSL_PARAM settable_params[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable_params;
+}
+
+static void *mac_gen_init_common(void *provctx, int selection)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct mac_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ gctx->selection = selection;
+ }
+ return gctx;
+}
+
+static void *mac_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ struct mac_gen_ctx *gctx = mac_gen_init_common(provctx, selection);
+
+ if (gctx != NULL && !mac_gen_set_params(gctx, params)) {
+ mac_gen_cleanup(gctx);
+ gctx = NULL;
+ }
+ return gctx;
+}
+
+static void *cmac_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ struct mac_gen_ctx *gctx = mac_gen_init_common(provctx, selection);
+
+ if (gctx != NULL && !cmac_gen_set_params(gctx, params)) {
+ mac_gen_cleanup(gctx);
+ gctx = NULL;
+ }
+ return gctx;
+}
+
+static int mac_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct mac_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+ gctx->priv_key = OPENSSL_secure_malloc(p->data_size);
+ if (gctx->priv_key == NULL)
+ return 0;
+ memcpy(gctx->priv_key, p->data, p->data_size);
+ gctx->priv_key_len = p->data_size;
+ }
+
+ return 1;
+}
+
+static int cmac_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct mac_gen_ctx *gctx = genctx;
+
+ if (!mac_gen_set_params(genctx, params))
+ return 0;
+
+ if (!ossl_prov_cipher_load_from_params(&gctx->cipher, params,
+ gctx->libctx)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *mac_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static const OSSL_PARAM *cmac_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static void *mac_gen(void *genctx, OSSL_CALLBACK *cb, void *cbarg)
+{
+ struct mac_gen_ctx *gctx = genctx;
+ MAC_KEY *key;
+
+ if (!ossl_prov_is_running() || gctx == NULL)
+ return NULL;
+
+ if ((key = ossl_mac_key_new(gctx->libctx, 0)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);
+ return NULL;
+ }
+
+ /* If we're doing parameter generation then we just return a blank key */
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ if (gctx->priv_key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ ossl_mac_key_free(key);
+ return NULL;
+ }
+
+ /*
+ * This is horrible but required for backwards compatibility. We don't
+ * actually do real key generation at all. We simply copy the key that was
+ * previously set in the gctx. Hopefully at some point in the future all
+ * of this can be removed and we will only support the EVP_KDF APIs.
+ */
+ if (!ossl_prov_cipher_copy(&key->cipher, &gctx->cipher)) {
+ ossl_mac_key_free(key);
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return NULL;
+ }
+ ossl_prov_cipher_reset(&gctx->cipher);
+ key->priv_key = gctx->priv_key;
+ key->priv_key_len = gctx->priv_key_len;
+ gctx->priv_key_len = 0;
+ gctx->priv_key = NULL;
+
+ return key;
+}
+
+static void mac_gen_cleanup(void *genctx)
+{
+ struct mac_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_secure_clear_free(gctx->priv_key, gctx->priv_key_len);
+ ossl_prov_cipher_reset(&gctx->cipher);
+ OPENSSL_free(gctx);
+}
+
+const OSSL_DISPATCH ossl_mac_legacy_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))mac_new },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))mac_free },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))mac_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))mac_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))mac_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))mac_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))mac_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))mac_match },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))mac_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))mac_imexport_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))mac_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))mac_imexport_types },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))mac_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))mac_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))mac_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))mac_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))mac_gen_cleanup },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_cmac_legacy_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))mac_new_cmac },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))mac_free },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))mac_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))cmac_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))mac_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))mac_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))mac_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))mac_match },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))mac_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))cmac_imexport_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))mac_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))cmac_imexport_types },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))cmac_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))cmac_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))cmac_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))mac_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))mac_gen_cleanup },
+ OSSL_DISPATCH_END
+};
+
diff --git a/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c
new file mode 100644
index 000000000000..6b99e093c6d5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/ml_dsa_kmgmt.c
@@ -0,0 +1,595 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/param_build.h>
+#include <openssl/proverr.h>
+#include <openssl/self_test.h>
+#include "crypto/ml_dsa.h"
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/ml_dsa.h"
+
+static OSSL_FUNC_keymgmt_free_fn ml_dsa_free_key;
+static OSSL_FUNC_keymgmt_has_fn ml_dsa_has;
+static OSSL_FUNC_keymgmt_match_fn ml_dsa_match;
+static OSSL_FUNC_keymgmt_import_fn ml_dsa_import;
+static OSSL_FUNC_keymgmt_export_fn ml_dsa_export;
+static OSSL_FUNC_keymgmt_import_types_fn ml_dsa_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn ml_dsa_imexport_types;
+static OSSL_FUNC_keymgmt_dup_fn ml_dsa_dup_key;
+static OSSL_FUNC_keymgmt_get_params_fn ml_dsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ml_dsa_gettable_params;
+static OSSL_FUNC_keymgmt_validate_fn ml_dsa_validate;
+static OSSL_FUNC_keymgmt_gen_init_fn ml_dsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn ml_dsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ml_dsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn ml_dsa_gen_settable_params;
+#ifndef FIPS_MODULE
+static OSSL_FUNC_keymgmt_load_fn ml_dsa_load;
+#endif
+
+struct ml_dsa_gen_ctx {
+ PROV_CTX *provctx;
+ char *propq;
+ uint8_t entropy[32];
+ size_t entropy_len;
+};
+
+#ifdef FIPS_MODULE
+static int ml_dsa_pairwise_test(const ML_DSA_KEY *key)
+{
+ OSSL_SELF_TEST *st = NULL;
+ OSSL_CALLBACK *cb = NULL;
+ OSSL_LIB_CTX *ctx;
+ void *cbarg = NULL;
+ static const uint8_t msg[] = { 80, 108, 117, 103, 104 };
+ uint8_t rnd[ML_DSA_ENTROPY_LEN];
+ uint8_t sig[ML_DSA_87_SIG_LEN];
+ size_t sig_len = 0;
+ int ret = 0;
+
+ if (!ml_dsa_has(key, OSSL_KEYMGMT_SELECT_KEYPAIR)
+ || ossl_fips_self_testing())
+ return 1;
+
+ /*
+ * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`
+ * is NULL.
+ */
+ ctx = ossl_ml_dsa_key_get0_libctx(key);
+ OSSL_SELF_TEST_get_callback(ctx, &cb, &cbarg);
+
+ if ((st = OSSL_SELF_TEST_new(cb, cbarg)) == NULL)
+ return 0;
+
+ OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT,
+ OSSL_SELF_TEST_DESC_PCT_ML_DSA);
+
+ memset(rnd, 0, sizeof(rnd));
+ memset(sig, 0, sizeof(sig));
+
+ if (ossl_ml_dsa_sign(key, 0, msg, sizeof(msg), NULL, 0, rnd, sizeof(rnd), 0,
+ sig, &sig_len, sizeof(sig)) <= 0)
+ goto err;
+
+ OSSL_SELF_TEST_oncorrupt_byte(st, sig);
+
+ if (ossl_ml_dsa_verify(key, 0, msg, sizeof(msg), NULL, 0, 0,
+ sig, sig_len) <= 0)
+ goto err;
+
+ ret = 1;
+ err:
+ OSSL_SELF_TEST_onend(st, ret);
+ OSSL_SELF_TEST_free(st);
+ return ret;
+}
+#endif
+
+ML_DSA_KEY *ossl_prov_ml_dsa_new(PROV_CTX *ctx, const char *propq, int evp_type)
+{
+ ML_DSA_KEY *key;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ key = ossl_ml_dsa_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type);
+ /*
+ * When decoding, if the key ends up "loaded" into the same provider, these
+ * are the correct config settings, otherwise, new values will be assigned
+ * on import into a different provider. The "load" API does not pass along
+ * the provider context.
+ */
+ if (key != NULL) {
+ int flags_set = 0, flags_clr = 0;
+
+ if (ossl_prov_ctx_get_bool_param(
+ ctx, OSSL_PKEY_PARAM_ML_DSA_RETAIN_SEED, 1))
+ flags_set |= ML_DSA_KEY_RETAIN_SEED;
+ else
+ flags_clr = ML_DSA_KEY_RETAIN_SEED;
+
+ if (ossl_prov_ctx_get_bool_param(
+ ctx, OSSL_PKEY_PARAM_ML_DSA_PREFER_SEED, 1))
+ flags_set |= ML_DSA_KEY_PREFER_SEED;
+ else
+ flags_clr |= ML_DSA_KEY_PREFER_SEED;
+
+ ossl_ml_dsa_set_prekey(key, flags_set, flags_clr, NULL, 0, NULL, 0);
+ }
+ return key;
+}
+
+static void ml_dsa_free_key(void *keydata)
+{
+ ossl_ml_dsa_key_free((ML_DSA_KEY *)keydata);
+}
+
+static void *ml_dsa_dup_key(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_ml_dsa_key_dup(keydata_from, selection);
+ return NULL;
+}
+
+static int ml_dsa_has(const void *keydata, int selection)
+{
+ const ML_DSA_KEY *key = keydata;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 1; /* the selection is not missing */
+
+ return ossl_ml_dsa_key_has(key, selection);
+}
+
+static int ml_dsa_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const ML_DSA_KEY *key1 = keydata1;
+ const ML_DSA_KEY *key2 = keydata2;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (key1 == NULL || key2 == NULL)
+ return 0;
+ return ossl_ml_dsa_key_equal(key1, key2, selection);
+}
+
+static int ml_dsa_validate(const void *key_data, int selection, int check_type)
+{
+ const ML_DSA_KEY *key = key_data;
+
+ if (!ml_dsa_has(key, selection))
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ return ossl_ml_dsa_key_pairwise_check(key);
+ return 1;
+}
+
+/**
+ * @brief Load a ML_DSA key from raw data.
+ *
+ * @param key An ML_DSA key to load into
+ * @param params An array of parameters containing key data.
+ * @param include_private Set to 1 to optionally include the private key data
+ * if it exists.
+ * @returns 1 on success, or 0 on failure.
+ */
+static int ml_dsa_key_fromdata(ML_DSA_KEY *key, const OSSL_PARAM params[],
+ int include_private)
+{
+ const OSSL_PARAM *p = NULL;
+ const ML_DSA_PARAMS *key_params = ossl_ml_dsa_key_params(key);
+ const uint8_t *pk = NULL, *sk = NULL, *seed = NULL;
+ size_t pk_len = 0, sk_len = 0, seed_len = 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (p != NULL
+ && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&pk, &pk_len))
+ return 0;
+ if (pk != NULL && pk_len != key_params->pk_len) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH,
+ "Invalid %s public key length", key_params->alg);
+ return 0;
+ }
+
+ /* Private key is optional */
+ if (include_private) {
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_DSA_SEED);
+ if (p != NULL
+ && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&seed,
+ &seed_len))
+ return 0;
+ if (seed != NULL && seed_len != ML_DSA_SEED_BYTES) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL
+ && !OSSL_PARAM_get_octet_string_ptr(p, (const void **)&sk, &sk_len))
+ return 0;
+ if (sk != NULL && sk_len != key_params->sk_len) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH,
+ "Invalid %s private key length", key_params->alg);
+ return 0;
+ }
+ }
+
+ /* The caller MUST specify at least one of seed, private or public keys. */
+ if (seed_len == 0 && pk_len == 0 && sk_len == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ if (seed_len != 0
+ && (sk_len == 0
+ || (ossl_ml_dsa_key_get_prov_flags(key) & ML_DSA_KEY_PREFER_SEED))) {
+ if (!ossl_ml_dsa_set_prekey(key, 0, 0, seed, seed_len, sk, sk_len))
+ return 0;
+ if (!ossl_ml_dsa_generate_key(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ return 0;
+ }
+ } else if (sk_len > 0) {
+ if (!ossl_ml_dsa_sk_decode(key, sk, sk_len))
+ return 0;
+ } else if (pk_len > 0) {
+ if (!ossl_ml_dsa_pk_decode(key, pk, pk_len))
+ return 0;
+ }
+
+ /* Error if the supplied public key does not match the generated key */
+ if (pk_len == 0
+ || seed_len + sk_len == 0
+ || memcmp(ossl_ml_dsa_key_get_pub(key), pk, pk_len) == 0)
+ return 1;
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "explicit %s public key does not match private",
+ key_params->alg);
+ ossl_ml_dsa_key_reset(key);
+ return 0;
+}
+
+static int ml_dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ ML_DSA_KEY *key = keydata;
+ int include_priv;
+ int res;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_priv = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+ res = ml_dsa_key_fromdata(key, params, include_priv);
+#ifdef FIPS_MODULE
+ if (res > 0) {
+ res = ml_dsa_pairwise_test(key);
+ if (!res) {
+ ossl_ml_dsa_key_reset(key);
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT_IMPORT);
+ }
+ }
+#endif /* FIPS_MODULE */
+ return res;
+}
+
+#define ML_DSA_IMEXPORTABLE_PARAMETERS \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+
+static const OSSL_PARAM ml_dsa_key_types[] = {
+ ML_DSA_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *ml_dsa_imexport_types(int selection)
+{
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return NULL;
+ return ml_dsa_key_types;
+}
+
+static const OSSL_PARAM ml_dsa_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0),
+ ML_DSA_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *ml_dsa_gettable_params(void *provctx)
+{
+ return ml_dsa_params;
+}
+
+static int ml_dsa_get_params(void *keydata, OSSL_PARAM params[])
+{
+ ML_DSA_KEY *key = keydata;
+ OSSL_PARAM *p;
+ const uint8_t *pub, *priv, *seed;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, 8 * ossl_ml_dsa_key_get_pub_len(key)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, ossl_ml_dsa_key_get_collision_strength_bits(key)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, ossl_ml_dsa_key_get_sig_len(key)))
+ return 0;
+
+ pub = ossl_ml_dsa_key_get_pub(key);
+ priv = ossl_ml_dsa_key_get_priv(key);
+ seed = ossl_ml_dsa_key_get_seed(key);
+
+ if (seed != NULL
+ && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_DSA_SEED)) != NULL
+ && !OSSL_PARAM_set_octet_string(p, seed, ML_DSA_SEED_BYTES))
+ return 0;
+ if (priv != NULL
+ && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL
+ && !OSSL_PARAM_set_octet_string(p, priv,
+ ossl_ml_dsa_key_get_priv_len(key)))
+ return 0;
+ if (pub != NULL
+ && (p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL
+ && !OSSL_PARAM_set_octet_string(p, pub,
+ ossl_ml_dsa_key_get_pub_len(key)))
+ return 0;
+ /*
+ * This allows apps to use an empty digest, so that the old API
+ * for digest signing can be used.
+ */
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, ""))
+ return 0;
+
+ return 1;
+}
+
+static int ml_dsa_export(void *keydata, int selection,
+ OSSL_CALLBACK *param_cb, void *cbarg)
+{
+ ML_DSA_KEY *key = keydata;
+ OSSL_PARAM params[4];
+ const uint8_t *buf;
+ int include_private, pnum = 0;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+
+ /*
+ * Note that if the seed is present, both the seed and the private key are
+ * exported. The recipient will have a choice.
+ */
+ if (include_private) {
+ if ((buf = ossl_ml_dsa_key_get_seed(key)) != NULL) {
+ params[pnum++] = OSSL_PARAM_construct_octet_string
+ (OSSL_PKEY_PARAM_ML_DSA_SEED, (void *)buf, ML_DSA_SEED_BYTES);
+ }
+ if ((buf = ossl_ml_dsa_key_get_priv(key)) != NULL) {
+ params[pnum++] = OSSL_PARAM_construct_octet_string
+ (OSSL_PKEY_PARAM_PRIV_KEY, (void *)buf,
+ ossl_ml_dsa_key_get_priv_len(key));
+ }
+ }
+ if (((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ && ((buf = ossl_ml_dsa_key_get_pub(key)) != NULL)) {
+ params[pnum++] = OSSL_PARAM_construct_octet_string
+ (OSSL_PKEY_PARAM_PUB_KEY, (void *)buf,
+ ossl_ml_dsa_key_get_pub_len(key));
+ }
+ if (pnum == 0)
+ return 0;
+ params[pnum] = OSSL_PARAM_construct_end();
+ return param_cb(params, cbarg);
+}
+
+#ifndef FIPS_MODULE
+static void *ml_dsa_load(const void *reference, size_t reference_sz)
+{
+ ML_DSA_KEY *key = NULL;
+ const ML_DSA_PARAMS *key_params;
+ const uint8_t *sk, *seed;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
+ /* The contents of the reference is the address to our object */
+ key = *(ML_DSA_KEY **)reference;
+ /* We grabbed, so we detach it */
+ *(ML_DSA_KEY **)reference = NULL;
+ /* All done, if the pubkey is present. */
+ if (key == NULL || ossl_ml_dsa_key_get_pub(key) != NULL)
+ return key;
+ /* Handle private prekey inputs. */
+ sk = ossl_ml_dsa_key_get_priv(key);
+ seed = ossl_ml_dsa_key_get_seed(key);
+ if (seed != NULL
+ && (sk == NULL || (ossl_ml_dsa_key_get_prov_flags(key)
+ & ML_DSA_KEY_PREFER_SEED))) {
+ if (ossl_ml_dsa_generate_key(key))
+ return key;
+ } else if (sk != NULL) {
+ if (ossl_ml_dsa_sk_decode(key, sk,
+ ossl_ml_dsa_key_get_priv_len(key)))
+ return key;
+ key_params = ossl_ml_dsa_key_params(key);
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "error parsing %s private key",
+ key_params->alg);
+ } else {
+ return key;
+ }
+ }
+
+ ossl_ml_dsa_key_free(key);
+ return NULL;
+}
+#endif
+
+static void *ml_dsa_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ struct ml_dsa_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->provctx = provctx;
+ if (!ml_dsa_gen_set_params(gctx, params)) {
+ OPENSSL_free(gctx);
+ gctx = NULL;
+ }
+ }
+ return gctx;
+}
+
+static void *ml_dsa_gen(void *genctx, int evp_type)
+{
+ struct ml_dsa_gen_ctx *gctx = genctx;
+ ML_DSA_KEY *key = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ key = ossl_prov_ml_dsa_new(gctx->provctx, gctx->propq, evp_type);
+ if (key == NULL)
+ return NULL;
+ if (gctx->entropy_len != 0
+ && !ossl_ml_dsa_set_prekey(key, 0, 0,
+ gctx->entropy, gctx->entropy_len, NULL, 0))
+ goto err;
+ if (!ossl_ml_dsa_generate_key(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GENERATE_KEY);
+ goto err;
+ }
+#ifdef FIPS_MODULE
+ if (!ml_dsa_pairwise_test(key)) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ goto err;
+ }
+#endif
+ return key;
+ err:
+ ossl_ml_dsa_key_free(key);
+ return NULL;
+}
+
+static int ml_dsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct ml_dsa_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_DSA_SEED);
+ if (p != NULL) {
+ void *vp = gctx->entropy;
+ size_t len = sizeof(gctx->entropy);
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp, len, &(gctx->entropy_len))) {
+ gctx->entropy_len = 0;
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ OPENSSL_free(gctx->propq);
+ gctx->propq = NULL;
+ if (!OSSL_PARAM_get_utf8_string(p, &gctx->propq, 0))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *ml_dsa_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_DSA_SEED, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static void ml_dsa_gen_cleanup(void *genctx)
+{
+ struct ml_dsa_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_cleanse(gctx->entropy, gctx->entropy_len);
+ OPENSSL_free(gctx->propq);
+ OPENSSL_free(gctx);
+}
+
+#ifndef FIPS_MODULE
+# define DISPATCH_LOAD_FN \
+ { OSSL_FUNC_KEYMGMT_LOAD, (OSSL_FUNC) ml_dsa_load },
+#else
+# define DISPATCH_LOAD_FN /* Non-FIPS only */
+#endif
+
+#define MAKE_KEYMGMT_FUNCTIONS(alg) \
+ static OSSL_FUNC_keymgmt_new_fn ml_dsa_##alg##_new_key; \
+ static OSSL_FUNC_keymgmt_gen_fn ml_dsa_##alg##_gen; \
+ static void *ml_dsa_##alg##_new_key(void *provctx) \
+ { \
+ return ossl_prov_ml_dsa_new(provctx, NULL, EVP_PKEY_ML_DSA_##alg); \
+ } \
+ static void *ml_dsa_##alg##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\
+ { \
+ return ml_dsa_gen(genctx, EVP_PKEY_ML_DSA_##alg); \
+ } \
+ const OSSL_DISPATCH ossl_ml_dsa_##alg##_keymgmt_functions[] = { \
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))ml_dsa_##alg##_new_key }, \
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))ml_dsa_free_key }, \
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))ml_dsa_has }, \
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))ml_dsa_match }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))ml_dsa_import }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))ml_dsa_imexport_types },\
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))ml_dsa_export }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))ml_dsa_imexport_types },\
+ DISPATCH_LOAD_FN \
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))ml_dsa_get_params }, \
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))ml_dsa_gettable_params },\
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))ml_dsa_validate }, \
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))ml_dsa_gen_init }, \
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))ml_dsa_##alg##_gen }, \
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))ml_dsa_gen_cleanup }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, \
+ (void (*)(void))ml_dsa_gen_set_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \
+ (void (*)(void))ml_dsa_gen_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))ml_dsa_dup_key }, \
+ OSSL_DISPATCH_END \
+ }
+
+MAKE_KEYMGMT_FUNCTIONS(44);
+MAKE_KEYMGMT_FUNCTIONS(65);
+MAKE_KEYMGMT_FUNCTIONS(87);
diff --git a/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c
new file mode 100644
index 000000000000..9b34fe1c0331
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/ml_kem_kmgmt.c
@@ -0,0 +1,859 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/provider.h>
+#include <openssl/rand.h>
+#include <openssl/self_test.h>
+#include <openssl/param_build.h>
+#include "crypto/ml_kem.h"
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+#include "prov/ml_kem.h"
+
+static OSSL_FUNC_keymgmt_new_fn ml_kem_512_new;
+static OSSL_FUNC_keymgmt_new_fn ml_kem_768_new;
+static OSSL_FUNC_keymgmt_new_fn ml_kem_1024_new;
+static OSSL_FUNC_keymgmt_gen_fn ml_kem_gen;
+static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_512_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_768_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn ml_kem_1024_gen_init;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn ml_kem_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn ml_kem_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn ml_kem_gen_settable_params;
+#ifndef FIPS_MODULE
+static OSSL_FUNC_keymgmt_load_fn ml_kem_load;
+#endif
+static OSSL_FUNC_keymgmt_get_params_fn ml_kem_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn ml_kem_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn ml_kem_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn ml_kem_settable_params;
+static OSSL_FUNC_keymgmt_has_fn ml_kem_has;
+static OSSL_FUNC_keymgmt_match_fn ml_kem_match;
+static OSSL_FUNC_keymgmt_validate_fn ml_kem_validate;
+static OSSL_FUNC_keymgmt_import_fn ml_kem_import;
+static OSSL_FUNC_keymgmt_export_fn ml_kem_export;
+static OSSL_FUNC_keymgmt_import_types_fn ml_kem_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn ml_kem_imexport_types;
+static OSSL_FUNC_keymgmt_dup_fn ml_kem_dup;
+
+static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
+ | OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
+
+typedef struct ml_kem_gen_ctx_st {
+ PROV_CTX *provctx;
+ char *propq;
+ int selection;
+ int evp_type;
+ uint8_t seedbuf[ML_KEM_SEED_BYTES];
+ uint8_t *seed;
+} PROV_ML_KEM_GEN_CTX;
+
+static int ml_kem_pairwise_test(const ML_KEM_KEY *key, int key_flags)
+{
+#ifdef FIPS_MODULE
+ OSSL_SELF_TEST *st = NULL;
+ OSSL_CALLBACK *cb = NULL;
+ void *cbarg = NULL;
+#endif
+ unsigned char entropy[ML_KEM_RANDOM_BYTES];
+ unsigned char secret[ML_KEM_SHARED_SECRET_BYTES];
+ unsigned char out[ML_KEM_SHARED_SECRET_BYTES];
+ unsigned char *ctext = NULL;
+ const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key);
+ int operation_result = 0;
+ int ret = 0;
+
+ /* Unless we have both a public and private key, we can't do the test */
+ if (!ossl_ml_kem_have_prvkey(key)
+ || !ossl_ml_kem_have_pubkey(key)
+ || (key_flags & ML_KEM_KEY_PCT_TYPE) == 0)
+ return 1;
+#ifdef FIPS_MODULE
+ /* During self test, it is a waste to do this test */
+ if (ossl_fips_self_testing())
+ return 1;
+
+ /*
+ * The functions `OSSL_SELF_TEST_*` will return directly if parameter `st`
+ * is NULL.
+ */
+ OSSL_SELF_TEST_get_callback(key->libctx, &cb, &cbarg);
+
+ st = OSSL_SELF_TEST_new(cb, cbarg);
+ if (st == NULL)
+ return 0;
+
+ OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT,
+ OSSL_SELF_TEST_DESC_PCT_ML_KEM);
+#endif /* FIPS_MODULE */
+
+ ctext = OPENSSL_malloc(v->ctext_bytes);
+ if (ctext == NULL)
+ goto err;
+
+ memset(out, 0, sizeof(out));
+
+ /*
+ * The pairwise test is skipped unless either RANDOM or FIXED entropy PCTs
+ * are enabled.
+ */
+ if (key_flags & ML_KEM_KEY_RANDOM_PCT) {
+ operation_result = ossl_ml_kem_encap_rand(ctext, v->ctext_bytes,
+ secret, sizeof(secret), key);
+ } else {
+ memset(entropy, 0125, sizeof(entropy));
+ operation_result = ossl_ml_kem_encap_seed(ctext, v->ctext_bytes,
+ secret, sizeof(secret),
+ entropy, sizeof(entropy),
+ key);
+ }
+ if (operation_result != 1)
+ goto err;
+
+#ifdef FIPS_MODULE
+ OSSL_SELF_TEST_oncorrupt_byte(st, ctext);
+#endif
+
+ operation_result = ossl_ml_kem_decap(out, sizeof(out), ctext, v->ctext_bytes,
+ key);
+ if (operation_result != 1 || memcmp(out, secret, sizeof(out)) != 0)
+ goto err;
+
+ ret = 1;
+err:
+#ifdef FIPS_MODULE
+ OSSL_SELF_TEST_onend(st, ret);
+ OSSL_SELF_TEST_free(st);
+#else
+ if (ret == 0) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "public part of %s private key fails to match private",
+ v->algorithm_name);
+ }
+#endif
+ OPENSSL_free(ctext);
+ return ret;
+}
+
+ML_KEM_KEY *ossl_prov_ml_kem_new(PROV_CTX *ctx, const char *propq, int evp_type)
+{
+ ML_KEM_KEY *key;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ /*
+ * When decoding, if the key ends up "loaded" into the same provider, these
+ * are the correct config settings, otherwise, new values will be assigned
+ * on import into a different provider. The "load" API does not pass along
+ * the provider context.
+ */
+ if ((key = ossl_ml_kem_key_new(PROV_LIBCTX_OF(ctx), propq, evp_type)) != NULL) {
+ const char *pct_type = ossl_prov_ctx_get_param(
+ ctx, OSSL_PKEY_PARAM_ML_KEM_IMPORT_PCT_TYPE, "random");
+
+ if (ossl_prov_ctx_get_bool_param(
+ ctx, OSSL_PKEY_PARAM_ML_KEM_RETAIN_SEED, 1))
+ key->prov_flags |= ML_KEM_KEY_RETAIN_SEED;
+ else
+ key->prov_flags &= ~ML_KEM_KEY_RETAIN_SEED;
+ if (ossl_prov_ctx_get_bool_param(
+ ctx, OSSL_PKEY_PARAM_ML_KEM_PREFER_SEED, 1))
+ key->prov_flags |= ML_KEM_KEY_PREFER_SEED;
+ else
+ key->prov_flags &= ~ML_KEM_KEY_PREFER_SEED;
+ if (OPENSSL_strcasecmp(pct_type, "random") == 0)
+ key->prov_flags |= ML_KEM_KEY_RANDOM_PCT;
+ else if (OPENSSL_strcasecmp(pct_type, "fixed") == 0)
+ key->prov_flags |= ML_KEM_KEY_FIXED_PCT;
+ else
+ key->prov_flags &= ~ML_KEM_KEY_PCT_TYPE;
+ }
+ return key;
+}
+
+static int ml_kem_has(const void *vkey, int selection)
+{
+ const ML_KEM_KEY *key = vkey;
+
+ /* A NULL key MUST fail to have anything */
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
+ case 0:
+ return 1;
+ case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:
+ return ossl_ml_kem_have_pubkey(key);
+ default:
+ return ossl_ml_kem_have_prvkey(key);
+ }
+}
+
+static int ml_kem_match(const void *vkey1, const void *vkey2, int selection)
+{
+ const ML_KEM_KEY *key1 = vkey1;
+ const ML_KEM_KEY *key2 = vkey2;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* All we have that can be compared is key material */
+ if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR))
+ return 1;
+
+ return ossl_ml_kem_pubkey_cmp(key1, key2);
+}
+
+static int ml_kem_validate(const void *vkey, int selection, int check_type)
+{
+ const ML_KEM_KEY *key = vkey;
+
+ if (!ml_kem_has(key, selection))
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ return ml_kem_pairwise_test(key, ML_KEM_KEY_RANDOM_PCT);
+ return 1;
+}
+
+static int ml_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ ML_KEM_KEY *key = vkey;
+ OSSL_PARAM_BLD *tmpl = NULL;
+ OSSL_PARAM *params = NULL;
+ const ML_KEM_VINFO *v;
+ uint8_t *pubenc = NULL, *prvenc = NULL, *seedenc = NULL;
+ size_t prvlen = 0, seedlen = 0;
+ int ret = 0;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ v = ossl_ml_kem_key_vinfo(key);
+ if (!ossl_ml_kem_have_pubkey(key)) {
+ /* Fail when no key material can be returned */
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) == 0
+ || !ossl_ml_kem_decoded_key(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ } else if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ pubenc = OPENSSL_malloc(v->pubkey_bytes);
+ if (pubenc == NULL
+ || !ossl_ml_kem_encode_public_key(pubenc, v->pubkey_bytes, key))
+ goto err;
+ }
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ /*
+ * The seed and/or private key material are allocated on the secure
+ * heap if configured, ossl_param_build_set_octet_string(), will then
+ * also use the secure heap.
+ */
+ if (ossl_ml_kem_have_seed(key)) {
+ seedlen = ML_KEM_SEED_BYTES;
+ if ((seedenc = OPENSSL_secure_zalloc(seedlen)) == NULL
+ || !ossl_ml_kem_encode_seed(seedenc, seedlen, key))
+ goto err;
+ }
+ if (ossl_ml_kem_have_prvkey(key)) {
+ prvlen = v->prvkey_bytes;
+ if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL
+ || !ossl_ml_kem_encode_private_key(prvenc, prvlen, key))
+ goto err;
+ } else if (ossl_ml_kem_have_dkenc(key)) {
+ prvlen = v->prvkey_bytes;
+ if ((prvenc = OPENSSL_secure_zalloc(prvlen)) == NULL)
+ goto err;
+ memcpy(prvenc, key->encoded_dk, prvlen);
+ }
+ }
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ goto err;
+
+ /* The (d, z) seed, when available and private keys are requested. */
+ if (seedenc != NULL
+ && !ossl_param_build_set_octet_string(
+ tmpl, params, OSSL_PKEY_PARAM_ML_KEM_SEED, seedenc, seedlen))
+ goto err;
+
+ /* The private key in the FIPS 203 |dk| format, when requested. */
+ if (prvenc != NULL
+ && !ossl_param_build_set_octet_string(
+ tmpl, params, OSSL_PKEY_PARAM_PRIV_KEY, prvenc, prvlen))
+ goto err;
+
+ /* The public key on request; it is always available when either is */
+ if (pubenc != NULL
+ && !ossl_param_build_set_octet_string(
+ tmpl, params, OSSL_PKEY_PARAM_PUB_KEY, pubenc, v->pubkey_bytes))
+ goto err;
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ OPENSSL_secure_clear_free(seedenc, seedlen);
+ OPENSSL_secure_clear_free(prvenc, prvlen);
+ OPENSSL_free(pubenc);
+ return ret;
+}
+
+static const OSSL_PARAM *ml_kem_imexport_types(int selection)
+{
+ static const OSSL_PARAM key_types[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return key_types;
+ return NULL;
+}
+
+static int check_seed(const uint8_t *seed, const uint8_t *prvenc,
+ ML_KEM_KEY *key)
+{
+ size_t zlen = ML_KEM_RANDOM_BYTES;
+
+ if (memcmp(seed + ML_KEM_SEED_BYTES - zlen,
+ prvenc + key->vinfo->prvkey_bytes - zlen, zlen) == 0)
+ return 1;
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "private %s key implicit rejection secret does"
+ " not match seed", key->vinfo->algorithm_name);
+ return 0;
+}
+
+static int check_prvenc(const uint8_t *prvenc, ML_KEM_KEY *key)
+{
+ size_t len = key->vinfo->prvkey_bytes;
+ uint8_t *buf = OPENSSL_malloc(len);
+ int ret = 0;
+
+ if (buf != NULL
+ && ossl_ml_kem_encode_private_key(buf, len, key))
+ ret = memcmp(buf, prvenc, len) == 0;
+ OPENSSL_clear_free(buf, len);
+ if (ret)
+ return 1;
+
+ if (buf != NULL)
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "explicit %s private key does not match seed",
+ key->vinfo->algorithm_name);
+ ossl_ml_kem_key_reset(key);
+ return 0;
+}
+
+static int ml_kem_key_fromdata(ML_KEM_KEY *key,
+ const OSSL_PARAM params[],
+ int include_private)
+{
+ const OSSL_PARAM *p = NULL;
+ const void *pubenc = NULL, *prvenc = NULL, *seedenc = NULL;
+ size_t publen = 0, prvlen = 0, seedlen = 0, puboff;
+ const ML_KEM_VINFO *v;
+
+ /* Invalid attempt to mutate a key, what is the right error to report? */
+ if (key == NULL || ossl_ml_kem_have_pubkey(key))
+ return 0;
+ v = ossl_ml_kem_key_vinfo(key);
+
+ /*
+ * When a private key is provided, without a seed, any public key also
+ * provided will be ignored (apart from length), just as with the seed.
+ */
+ if (include_private) {
+ /*
+ * When a seed is provided, the private and public keys may be ignored,
+ * after validating just their lengths. Comparing encodings or hashes
+ * when applicable is possible, but not currently implemented.
+ */
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);
+ if (p != NULL
+ && OSSL_PARAM_get_octet_string_ptr(p, &seedenc, &seedlen) != 1)
+ return 0;
+ if (seedlen != 0 && seedlen != ML_KEM_SEED_BYTES) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL
+ && OSSL_PARAM_get_octet_string_ptr(p, &prvenc, &prvlen) != 1)
+ return 0;
+ if (prvlen != 0 && prvlen != v->prvkey_bytes) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+
+ /* Used only when no seed or private key is provided. */
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (p != NULL
+ && OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1)
+ return 0;
+ if (publen != 0 && publen != v->pubkey_bytes) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ /* The caller MUST specify at least one of seed, private or public keys. */
+ if (seedlen == 0 && publen == 0 && prvlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ /* Check any explicit public key against embedded value in private key */
+ if (publen > 0 && prvlen > 0) {
+ /* point to the ek offset in dk = DKpke||ek||H(ek)||z */
+ puboff = prvlen - ML_KEM_RANDOM_BYTES - ML_KEM_PKHASH_BYTES - publen;
+ if (memcmp(pubenc, (unsigned char *)prvenc + puboff, publen) != 0) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "explicit %s public key does not match private",
+ v->algorithm_name);
+ return 0;
+ }
+ }
+
+ if (seedlen != 0
+ && (prvlen == 0 || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
+ if (prvlen != 0 && !check_seed(seedenc, prvenc, key))
+ return 0;
+ if (!ossl_ml_kem_set_seed(seedenc, seedlen, key)
+ || !ossl_ml_kem_genkey(NULL, 0, key))
+ return 0;
+ return prvlen == 0 || check_prvenc(prvenc, key);
+ } else if (prvlen != 0) {
+ return ossl_ml_kem_parse_private_key(prvenc, prvlen, key);
+ }
+ return ossl_ml_kem_parse_public_key(pubenc, publen, key);
+}
+
+static int ml_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
+{
+ ML_KEM_KEY *key = vkey;
+ int include_private;
+ int res;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+ res = ml_kem_key_fromdata(key, params, include_private);
+ if (res > 0 && include_private
+ && !ml_kem_pairwise_test(key, key->prov_flags)) {
+#ifdef FIPS_MODULE
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT_IMPORT);
+#endif
+ ossl_ml_kem_key_reset(key);
+ res = 0;
+ }
+ return res;
+}
+
+static const OSSL_PARAM *ml_kem_gettable_params(void *provctx)
+{
+ static const OSSL_PARAM arr[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ /* Exported for import */
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),
+ /* Exported to EVP_PKEY_get_raw_private_key() */
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ /* Exported to EVP_PKEY_get_raw_public_key() */
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+ /* Needed by EVP_PKEY_get1_encoded_public_key() */
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return arr;
+}
+
+#ifndef FIPS_MODULE
+static void *ml_kem_load(const void *reference, size_t reference_sz)
+{
+ ML_KEM_KEY *key = NULL;
+ uint8_t *encoded_dk = NULL;
+ uint8_t seed[ML_KEM_SEED_BYTES];
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
+ /* The contents of the reference is the address to our object */
+ key = *(ML_KEM_KEY **)reference;
+ encoded_dk = key->encoded_dk;
+ key->encoded_dk = NULL;
+ /* We grabbed, so we detach it */
+ *(ML_KEM_KEY **)reference = NULL;
+ if (encoded_dk != NULL
+ && ossl_ml_kem_encode_seed(seed, sizeof(seed), key)
+ && !check_seed(seed, encoded_dk, key))
+ goto err;
+ /* Generate the key now, if it holds only a stashed seed. */
+ if (ossl_ml_kem_have_seed(key)
+ && (encoded_dk == NULL
+ || (key->prov_flags & ML_KEM_KEY_PREFER_SEED))) {
+ if (!ossl_ml_kem_genkey(NULL, 0, key)
+ || (encoded_dk != NULL && !check_prvenc(encoded_dk, key)))
+ goto err;
+ } else if (encoded_dk != NULL) {
+ if (!ossl_ml_kem_parse_private_key(encoded_dk,
+ key->vinfo->prvkey_bytes, key)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
+ "error parsing %s private key",
+ key->vinfo->algorithm_name);
+ goto err;
+ }
+ if (!ml_kem_pairwise_test(key, key->prov_flags))
+ goto err;
+ }
+ OPENSSL_free(encoded_dk);
+ return key;
+ }
+
+ err:
+ OPENSSL_free(encoded_dk);
+ ossl_ml_kem_key_free(key);
+ return NULL;
+}
+#endif
+
+/*
+ * It is assumed the key is guaranteed non-NULL here, and is from this provider
+ */
+static int ml_kem_get_params(void *vkey, OSSL_PARAM params[])
+{
+ ML_KEM_KEY *key = vkey;
+ const ML_KEM_VINFO *v = ossl_ml_kem_key_vinfo(key);
+ OSSL_PARAM *p;
+ const char *pubparams[] = {
+ OSSL_PKEY_PARAM_PUB_KEY,
+ OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY
+ };
+ int i;
+
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, v->bits))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, v->secbits))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, v->ctext_bytes))
+ return 0;
+
+ if (ossl_ml_kem_have_pubkey(key)) {
+ uint8_t *pubenc = NULL;
+
+ for (i = 0; i < 2; ++i) {
+ p = OSSL_PARAM_locate(params, pubparams[i]);
+ if (p == NULL)
+ continue;
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ p->return_size = v->pubkey_bytes;
+ if (p->data == NULL)
+ continue;
+ if (p->data_size < p->return_size)
+ return 0;
+ if (pubenc != NULL) {
+ memcpy(p->data, pubenc, p->return_size);
+ continue;
+ }
+ if (!ossl_ml_kem_encode_public_key(p->data, p->return_size, key))
+ return 0;
+ pubenc = p->data;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (p != NULL && ossl_ml_kem_have_prvkey(key)) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ p->return_size = v->prvkey_bytes;
+ if (p->data != NULL) {
+ if (p->data_size < p->return_size)
+ return 0;
+ if (!ossl_ml_kem_encode_private_key(p->data, p->return_size, key))
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ML_KEM_SEED);
+ if (p != NULL && ossl_ml_kem_have_seed(key)) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ p->return_size = ML_KEM_SEED_BYTES;
+ if (p->data != NULL) {
+ if (p->data_size < p->return_size)
+ return 0;
+ if (!ossl_ml_kem_encode_seed(p->data, p->return_size, key))
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM *ml_kem_settable_params(void *provctx)
+{
+ static const OSSL_PARAM arr[] = {
+ /* Used in TLS via EVP_PKEY_set1_encoded_public_key(). */
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return arr;
+}
+
+static int ml_kem_set_params(void *vkey, const OSSL_PARAM params[])
+{
+ ML_KEM_KEY *key = vkey;
+ const OSSL_PARAM *p;
+ const void *pubenc = NULL;
+ size_t publen = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p != NULL
+ && (OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen) != 1
+ || publen != key->vinfo->pubkey_bytes)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ if (publen == 0)
+ return 1;
+
+ /* Key mutation is reportedly generally not allowed */
+ if (ossl_ml_kem_have_pubkey(key)) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
+ "ML-KEM keys cannot be mutated");
+ return 0;
+ }
+
+ return ossl_ml_kem_parse_public_key(pubenc, publen, key);
+}
+
+static int ml_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->propq);
+ if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL)
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ML_KEM_SEED);
+ if (p != NULL) {
+ size_t len = ML_KEM_SEED_BYTES;
+
+ gctx->seed = gctx->seedbuf;
+ if (OSSL_PARAM_get_octet_string(p, (void **)&gctx->seed, len, &len)
+ && len == ML_KEM_SEED_BYTES)
+ return 1;
+
+ /* Possibly, but less likely wrong data type */
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ gctx->seed = NULL;
+ return 0;
+ }
+
+ return 1;
+}
+
+static void *ml_kem_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[], int evp_type)
+{
+ PROV_ML_KEM_GEN_CTX *gctx = NULL;
+
+ /*
+ * We can only generate private keys, check that the selection is
+ * appropriate.
+ */
+ if (!ossl_prov_is_running()
+ || (selection & minimal_selection) == 0
+ || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL)
+ return NULL;
+
+ gctx->selection = selection;
+ gctx->evp_type = evp_type;
+ gctx->provctx = provctx;
+ if (ml_kem_gen_set_params(gctx, params))
+ return gctx;
+
+ ml_kem_gen_cleanup(gctx);
+ return NULL;
+}
+
+static const OSSL_PARAM *ml_kem_gen_settable_params(ossl_unused void *vgctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ML_KEM_SEED, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static void *ml_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+ ML_KEM_KEY *key;
+ uint8_t *nopub = NULL;
+ uint8_t *seed;
+ int genok = 0;
+
+ if (gctx == NULL
+ || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
+ OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+ return NULL;
+ seed = gctx->seed;
+ key = ossl_prov_ml_kem_new(gctx->provctx, gctx->propq, gctx->evp_type);
+ if (key == NULL)
+ return NULL;
+
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ if (seed != NULL && !ossl_ml_kem_set_seed(seed, ML_KEM_SEED_BYTES, key))
+ return NULL;
+ genok = ossl_ml_kem_genkey(nopub, 0, key);
+
+ /* Erase the single-use seed */
+ if (seed != NULL)
+ OPENSSL_cleanse(seed, ML_KEM_SEED_BYTES);
+ gctx->seed = NULL;
+
+ if (genok) {
+#ifdef FIPS_MODULE
+ if (!ml_kem_pairwise_test(key, ML_KEM_KEY_FIXED_PCT)) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ ossl_ml_kem_key_free(key);
+ return NULL;
+ }
+#endif /* FIPS_MODULE */
+ return key;
+ }
+
+ ossl_ml_kem_key_free(key);
+ return NULL;
+}
+
+static void ml_kem_gen_cleanup(void *vgctx)
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+
+ if (gctx == NULL)
+ return;
+
+ if (gctx->seed != NULL)
+ OPENSSL_cleanse(gctx->seed, ML_KEM_RANDOM_BYTES);
+ OPENSSL_free(gctx->propq);
+ OPENSSL_free(gctx);
+}
+
+static void *ml_kem_dup(const void *vkey, int selection)
+{
+ const ML_KEM_KEY *key = vkey;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ return ossl_ml_kem_key_dup(key, selection);
+}
+
+#ifndef FIPS_MODULE
+# define DISPATCH_LOAD_FN \
+ { OSSL_FUNC_KEYMGMT_LOAD, (OSSL_FUNC) ml_kem_load },
+#else
+# define DISPATCH_LOAD_FN /* Non-FIPS only */
+#endif
+
+#define DECLARE_VARIANT(bits) \
+ static void *ml_kem_##bits##_new(void *provctx) \
+ { \
+ return ossl_prov_ml_kem_new(provctx, NULL, EVP_PKEY_ML_KEM_##bits); \
+ } \
+ static void *ml_kem_##bits##_gen_init(void *provctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ return ml_kem_gen_init(provctx, selection, params, \
+ EVP_PKEY_ML_KEM_##bits); \
+ } \
+ const OSSL_DISPATCH ossl_ml_kem_##bits##_keymgmt_functions[] = { \
+ { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) ml_kem_##bits##_new }, \
+ { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) ossl_ml_kem_key_free }, \
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) ml_kem_get_params }, \
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gettable_params }, \
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) ml_kem_set_params }, \
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) ml_kem_has }, \
+ { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) ml_kem_match }, \
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (OSSL_FUNC) ml_kem_validate }, \
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) ml_kem_##bits##_gen_init }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) ml_kem_gen_set_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) ml_kem_gen_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) ml_kem_gen }, \
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) ml_kem_gen_cleanup }, \
+ DISPATCH_LOAD_FN \
+ { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) ml_kem_dup }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) ml_kem_import }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) ml_kem_export }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) ml_kem_imexport_types }, \
+ OSSL_DISPATCH_END \
+ }
+DECLARE_VARIANT(512);
+DECLARE_VARIANT(768);
+DECLARE_VARIANT(1024);
diff --git a/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c
new file mode 100644
index 000000000000..bea878327609
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/mlx_kmgmt.c
@@ -0,0 +1,820 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/param_build.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+#include <openssl/rand.h>
+#include <openssl/self_test.h>
+#include "internal/nelem.h"
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/mlx_kem.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/securitycheck.h"
+
+static OSSL_FUNC_keymgmt_gen_fn mlx_kem_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn mlx_kem_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn mlx_kem_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn mlx_kem_gen_settable_params;
+static OSSL_FUNC_keymgmt_get_params_fn mlx_kem_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn mlx_kem_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn mlx_kem_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn mlx_kem_settable_params;
+static OSSL_FUNC_keymgmt_has_fn mlx_kem_has;
+static OSSL_FUNC_keymgmt_match_fn mlx_kem_match;
+static OSSL_FUNC_keymgmt_import_fn mlx_kem_import;
+static OSSL_FUNC_keymgmt_export_fn mlx_kem_export;
+static OSSL_FUNC_keymgmt_import_types_fn mlx_kem_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn mlx_kem_imexport_types;
+static OSSL_FUNC_keymgmt_dup_fn mlx_kem_dup;
+
+static const int minimal_selection = OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
+ | OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
+
+/* Must match DECLARE_DISPATCH invocations at the end of the file */
+static const ECDH_VINFO hybrid_vtable[] = {
+ { "EC", "P-256", 65, 32, 32, 1, EVP_PKEY_ML_KEM_768 },
+ { "EC", "P-384", 97, 48, 48, 1, EVP_PKEY_ML_KEM_1024 },
+#if !defined(OPENSSL_NO_ECX)
+ { "X25519", NULL, 32, 32, 32, 0, EVP_PKEY_ML_KEM_768 },
+ { "X448", NULL, 56, 56, 56, 0, EVP_PKEY_ML_KEM_1024 },
+#endif
+};
+
+typedef struct mlx_kem_gen_ctx_st {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ int selection;
+ unsigned int evp_type;
+} PROV_ML_KEM_GEN_CTX;
+
+static void mlx_kem_key_free(void *vkey)
+{
+ MLX_KEY *key = vkey;
+
+ if (key == NULL)
+ return;
+ OPENSSL_free(key->propq);
+ EVP_PKEY_free(key->mkey);
+ EVP_PKEY_free(key->xkey);
+ OPENSSL_free(key);
+}
+
+/* Takes ownership of propq */
+static void *
+mlx_kem_key_new(unsigned int v, OSSL_LIB_CTX *libctx, char *propq)
+{
+ MLX_KEY *key = NULL;
+ unsigned int ml_kem_variant;
+
+ if (!ossl_prov_is_running()
+ || v >= OSSL_NELEM(hybrid_vtable)
+ || (key = OPENSSL_malloc(sizeof(*key))) == NULL)
+ goto err;
+
+ ml_kem_variant = hybrid_vtable[v].ml_kem_variant;
+ key->libctx = libctx;
+ key->minfo = ossl_ml_kem_get_vinfo(ml_kem_variant);
+ key->xinfo = &hybrid_vtable[v];
+ key->xkey = key->mkey = NULL;
+ key->state = MLX_HAVE_NOKEYS;
+ key->propq = propq;
+ return key;
+
+ err:
+ OPENSSL_free(propq);
+ return NULL;
+}
+
+
+static int mlx_kem_has(const void *vkey, int selection)
+{
+ const MLX_KEY *key = vkey;
+
+ /* A NULL key MUST fail to have anything */
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
+ case 0:
+ return 1;
+ case OSSL_KEYMGMT_SELECT_PUBLIC_KEY:
+ return mlx_kem_have_pubkey(key);
+ default:
+ return mlx_kem_have_prvkey(key);
+ }
+}
+
+static int mlx_kem_match(const void *vkey1, const void *vkey2, int selection)
+{
+ const MLX_KEY *key1 = vkey1;
+ const MLX_KEY *key2 = vkey2;
+ int have_pub1 = mlx_kem_have_pubkey(key1);
+ int have_pub2 = mlx_kem_have_pubkey(key2);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* Compare domain parameters */
+ if (key1->xinfo != key2->xinfo)
+ return 0;
+
+ if (!(selection & OSSL_KEYMGMT_SELECT_KEYPAIR))
+ return 1;
+
+ if (have_pub1 ^ have_pub2)
+ return 0;
+
+ /* As in other providers, equal when both have no key material. */
+ if (!have_pub1)
+ return 1;
+
+ return EVP_PKEY_eq(key1->mkey, key2->mkey)
+ && EVP_PKEY_eq(key1->xkey, key2->xkey);
+}
+
+typedef struct export_cb_arg_st {
+ const char *algorithm_name;
+ uint8_t *pubenc;
+ uint8_t *prvenc;
+ int pubcount;
+ int prvcount;
+ size_t puboff;
+ size_t prvoff;
+ size_t publen;
+ size_t prvlen;
+} EXPORT_CB_ARG;
+
+/* Copy any exported key material into its storage slot */
+static int export_sub_cb(const OSSL_PARAM *params, void *varg)
+{
+ EXPORT_CB_ARG *sub_arg = varg;
+ const OSSL_PARAM *p = NULL;
+ size_t len;
+
+ /*
+ * The caller will decide whether anything essential is missing, but, if
+ * some key material was returned, it should have the right (parameter)
+ * data type and length.
+ */
+ if (ossl_param_is_empty(params))
+ return 1;
+ if (sub_arg->pubenc != NULL
+ && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY)) != NULL) {
+ void *pub = sub_arg->pubenc + sub_arg->puboff;
+
+ if (OSSL_PARAM_get_octet_string(p, &pub, sub_arg->publen, &len) != 1)
+ return 0;
+ if (len != sub_arg->publen) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "Unexpected %s public key length %lu != %lu",
+ sub_arg->algorithm_name, (unsigned long) len,
+ sub_arg->publen);
+ return 0;
+ }
+ ++sub_arg->pubcount;
+ }
+ if (sub_arg->prvenc != NULL
+ && (p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY)) != NULL) {
+ void *prv = sub_arg->prvenc + sub_arg->prvoff;
+
+ if (OSSL_PARAM_get_octet_string(p, &prv, sub_arg->prvlen, &len) != 1)
+ return 0;
+ if (len != sub_arg->prvlen) {
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR,
+ "Unexpected %s private key length %lu != %lu",
+ sub_arg->algorithm_name, (unsigned long) len,
+ (unsigned long) sub_arg->publen);
+ return 0;
+ }
+ ++sub_arg->prvcount;
+ }
+ return 1;
+}
+
+static int
+export_sub(EXPORT_CB_ARG *sub_arg, int selection, MLX_KEY *key)
+{
+ int slot;
+
+ /*
+ * The caller is responsible for initialising only the pubenc and prvenc
+ * pointer fields, the rest are set here or in the callback.
+ */
+ sub_arg->pubcount = 0;
+ sub_arg->prvcount = 0;
+
+ for (slot = 0; slot < 2; ++slot) {
+ int ml_kem_slot = key->xinfo->ml_kem_slot;
+ EVP_PKEY *pkey;
+
+ /* Export the parts of each component into its storage slot */
+ if (slot == ml_kem_slot) {
+ pkey = key->mkey;
+ sub_arg->algorithm_name = key->minfo->algorithm_name;
+ sub_arg->puboff = slot * key->xinfo->pubkey_bytes;
+ sub_arg->prvoff = slot * key->xinfo->prvkey_bytes;
+ sub_arg->publen = key->minfo->pubkey_bytes;
+ sub_arg->prvlen = key->minfo->prvkey_bytes;
+ } else {
+ pkey = key->xkey;
+ sub_arg->algorithm_name = key->xinfo->algorithm_name;
+ sub_arg->puboff = (1 - ml_kem_slot) * key->minfo->pubkey_bytes;
+ sub_arg->prvoff = (1 - ml_kem_slot) * key->minfo->prvkey_bytes;
+ sub_arg->publen = key->xinfo->pubkey_bytes;
+ sub_arg->prvlen = key->xinfo->prvkey_bytes;
+ }
+ if (!EVP_PKEY_export(pkey, selection, export_sub_cb, (void *)sub_arg))
+ return 0;
+ }
+ return 1;
+}
+
+static int mlx_kem_export(void *vkey, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ MLX_KEY *key = vkey;
+ OSSL_PARAM_BLD *tmpl = NULL;
+ OSSL_PARAM *params = NULL;
+ size_t publen;
+ size_t prvlen;
+ int ret = 0;
+ EXPORT_CB_ARG sub_arg;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ /* Fail when no key material has yet been provided */
+ if (!mlx_kem_have_pubkey(key)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+ publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
+ prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
+ memset(&sub_arg, 0, sizeof(sub_arg));
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ sub_arg.pubenc = OPENSSL_malloc(publen);
+ if (sub_arg.pubenc == NULL)
+ goto err;
+ }
+
+ if (mlx_kem_have_prvkey(key)
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ /*
+ * Allocated on the secure heap if configured, this is detected in
+ * ossl_param_build_set_octet_string(), which will then also use the
+ * secure heap.
+ */
+ sub_arg.prvenc = OPENSSL_secure_zalloc(prvlen);
+ if (sub_arg.prvenc == NULL)
+ goto err;
+ }
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ goto err;
+
+ /* Extract sub-component key material */
+ if (!export_sub(&sub_arg, selection, key))
+ goto err;
+
+ if (sub_arg.pubenc != NULL && sub_arg.pubcount == 2
+ && !ossl_param_build_set_octet_string(
+ tmpl, NULL, OSSL_PKEY_PARAM_PUB_KEY, sub_arg.pubenc, publen))
+ goto err;
+
+ if (sub_arg.prvenc != NULL && sub_arg.prvcount == 2
+ && !ossl_param_build_set_octet_string(
+ tmpl, NULL, OSSL_PKEY_PARAM_PRIV_KEY, sub_arg.prvenc, prvlen))
+ goto err;
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+
+ err:
+ OSSL_PARAM_BLD_free(tmpl);
+ OPENSSL_secure_clear_free(sub_arg.prvenc, prvlen);
+ OPENSSL_free(sub_arg.pubenc);
+ return ret;
+}
+
+static const OSSL_PARAM *mlx_kem_imexport_types(int selection)
+{
+ static const OSSL_PARAM key_types[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return key_types;
+ return NULL;
+}
+
+static int
+load_slot(OSSL_LIB_CTX *libctx, const char *propq, const char *pname,
+ int selection, MLX_KEY *key, int slot, const uint8_t *in,
+ int mbytes, int xbytes)
+{
+ EVP_PKEY_CTX *ctx;
+ EVP_PKEY **ppkey;
+ OSSL_PARAM parr[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+ const char *alg;
+ char *group = NULL;
+ size_t off, len;
+ void *val;
+ int ml_kem_slot = key->xinfo->ml_kem_slot;
+ int ret = 0;
+
+ if (slot == ml_kem_slot) {
+ alg = key->minfo->algorithm_name;
+ ppkey = &key->mkey;
+ off = slot * xbytes;
+ len = mbytes;
+ } else {
+ alg = key->xinfo->algorithm_name;
+ group = (char *) key->xinfo->group_name;
+ ppkey = &key->xkey;
+ off = (1 - ml_kem_slot) * mbytes;
+ len = xbytes;
+ }
+ val = (void *)(in + off);
+
+ if ((ctx = EVP_PKEY_CTX_new_from_name(libctx, alg, propq)) == NULL
+ || EVP_PKEY_fromdata_init(ctx) <= 0)
+ goto err;
+ parr[0] = OSSL_PARAM_construct_octet_string(pname, val, len);
+ if (group != NULL)
+ parr[1] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+ group, 0);
+ if (EVP_PKEY_fromdata(ctx, ppkey, selection, parr) > 0)
+ ret = 1;
+
+ err:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+static int
+load_keys(MLX_KEY *key,
+ const uint8_t *pubenc, size_t publen,
+ const uint8_t *prvenc, size_t prvlen)
+{
+ int slot;
+
+ for (slot = 0; slot < 2; ++slot) {
+ if (prvlen) {
+ /* Ignore public keys when private provided */
+ if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PRIV_KEY,
+ minimal_selection, key, slot, prvenc,
+ key->minfo->prvkey_bytes, key->xinfo->prvkey_bytes))
+ goto err;
+ } else if (publen) {
+ /* Absent private key data, import public keys */
+ if (!load_slot(key->libctx, key->propq, OSSL_PKEY_PARAM_PUB_KEY,
+ minimal_selection, key, slot, pubenc,
+ key->minfo->pubkey_bytes, key->xinfo->pubkey_bytes))
+ goto err;
+ }
+ }
+ key->state = prvlen ? MLX_HAVE_PRVKEY : MLX_HAVE_PUBKEY;
+ return 1;
+
+ err:
+ EVP_PKEY_free(key->mkey);
+ EVP_PKEY_free(key->xkey);
+ key->xkey = key->mkey = NULL;
+ key->state = MLX_HAVE_NOKEYS;
+ return 0;
+}
+
+static int mlx_kem_key_fromdata(MLX_KEY *key,
+ const OSSL_PARAM params[],
+ int include_private)
+{
+ const OSSL_PARAM *param_prv_key = NULL, *param_pub_key;
+ const void *pubenc = NULL, *prvenc = NULL;
+ size_t pubkey_bytes, prvkey_bytes;
+ size_t publen = 0, prvlen = 0;
+
+ /* Invalid attempt to mutate a key, what is the right error to report? */
+ if (key == NULL || mlx_kem_have_pubkey(key))
+ return 0;
+ pubkey_bytes = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
+ prvkey_bytes = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
+
+ /* What does the caller want to set? */
+ param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (param_pub_key != NULL &&
+ OSSL_PARAM_get_octet_string_ptr(param_pub_key, &pubenc, &publen) != 1)
+ return 0;
+ if (include_private)
+ param_prv_key = OSSL_PARAM_locate_const(params,
+ OSSL_PKEY_PARAM_PRIV_KEY);
+ if (param_prv_key != NULL &&
+ OSSL_PARAM_get_octet_string_ptr(param_prv_key, &prvenc, &prvlen) != 1)
+ return 0;
+
+ /* The caller MUST specify at least one of the public or private keys. */
+ if (publen == 0 && prvlen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ /*
+ * When a pubkey is provided, its length MUST be correct, if a private key
+ * is also provided, the public key will be otherwise ignored. We could
+ * look for a matching encoded block, but unclear this is useful.
+ */
+ if (publen != 0 && publen != pubkey_bytes) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (prvlen != 0 && prvlen != prvkey_bytes) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ return load_keys(key, pubenc, publen, prvenc, prvlen);
+}
+
+static int mlx_kem_import(void *vkey, int selection, const OSSL_PARAM params[])
+{
+ MLX_KEY *key = vkey;
+ int include_private;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+ return mlx_kem_key_fromdata(key, params, include_private);
+}
+
+static const OSSL_PARAM *mlx_kem_gettable_params(void *provctx)
+{
+ static const OSSL_PARAM arr[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return arr;
+}
+
+/*
+ * It is assumed the key is guaranteed non-NULL here, and is from this provider
+ */
+static int mlx_kem_get_params(void *vkey, OSSL_PARAM params[])
+{
+ MLX_KEY *key = vkey;
+ OSSL_PARAM *p, *pub, *prv = NULL;
+ EXPORT_CB_ARG sub_arg;
+ int selection;
+ size_t publen = key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes;
+ size_t prvlen = key->minfo->prvkey_bytes + key->xinfo->prvkey_bytes;
+
+ /* The reported "bit" count is those of the ML-KEM key */
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, key->minfo->bits))
+ return 0;
+
+ /* The reported security bits are those of the ML-KEM key */
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, key->minfo->secbits))
+ return 0;
+
+ /* The ciphertext sizes are additive */
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE);
+ if (p != NULL)
+ if (!OSSL_PARAM_set_int(p, key->minfo->ctext_bytes + key->xinfo->pubkey_bytes))
+ return 0;
+
+ if (!mlx_kem_have_pubkey(key))
+ return 1;
+
+ memset(&sub_arg, 0, sizeof(sub_arg));
+ pub = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (pub != NULL) {
+ if (pub->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ pub->return_size = publen;
+ if (pub->data == NULL) {
+ pub = NULL;
+ } else if (pub->data_size < publen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "public key output buffer too short: %lu < %lu",
+ (unsigned long) pub->data_size,
+ (unsigned long) publen);
+ return 0;
+ } else {
+ sub_arg.pubenc = pub->data;
+ }
+ }
+ if (mlx_kem_have_prvkey(key)) {
+ prv = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ if (prv != NULL) {
+ if (prv->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ prv->return_size = prvlen;
+ if (prv->data == NULL) {
+ prv = NULL;
+ } else if (prv->data_size < prvlen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "private key output buffer too short: %lu < %lu",
+ (unsigned long) prv->data_size,
+ (unsigned long) prvlen);
+ return 0;
+ } else {
+ sub_arg.prvenc = prv->data;
+ }
+ }
+ }
+ if (pub == NULL && prv == NULL)
+ return 1;
+
+ selection = prv == NULL ? 0 : OSSL_KEYMGMT_SELECT_PRIVATE_KEY;
+ selection |= pub == NULL ? 0 : OSSL_KEYMGMT_SELECT_PUBLIC_KEY;
+ if (key->xinfo->group_name != NULL)
+ selection |= OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS;
+
+ /* Extract sub-component key material */
+ if (!export_sub(&sub_arg, selection, key))
+ return 0;
+
+ if ((pub != NULL && sub_arg.pubcount != 2)
+ || (prv != NULL && sub_arg.prvcount != 2))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *mlx_kem_settable_params(void *provctx)
+{
+ static const OSSL_PARAM arr[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return arr;
+}
+
+static int mlx_kem_set_params(void *vkey, const OSSL_PARAM params[])
+{
+ MLX_KEY *key = vkey;
+ const OSSL_PARAM *p;
+ const void *pubenc = NULL;
+ size_t publen = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ /* Only one settable parameter is supported */
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p == NULL)
+ return 1;
+
+ /* Key mutation is reportedly generally not allowed */
+ if (mlx_kem_have_pubkey(key)) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE,
+ "keys cannot be mutated");
+ return 0;
+ }
+ /* An unlikely failure mode is the parameter having some unexpected type */
+ if (!OSSL_PARAM_get_octet_string_ptr(p, &pubenc, &publen))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ OPENSSL_free(key->propq);
+ key->propq = NULL;
+ if (!OSSL_PARAM_get_utf8_string(p, &key->propq, 0))
+ return 0;
+ }
+
+ if (publen != key->minfo->pubkey_bytes + key->xinfo->pubkey_bytes) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return 0;
+ }
+
+ return load_keys(key, pubenc, publen, NULL, 0);
+}
+
+static int mlx_kem_gen_set_params(void *vgctx, const OSSL_PARAM params[])
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->propq);
+ if ((gctx->propq = OPENSSL_strdup(p->data)) == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static void *mlx_kem_gen_init(int evp_type, OSSL_LIB_CTX *libctx,
+ int selection, const OSSL_PARAM params[])
+{
+ PROV_ML_KEM_GEN_CTX *gctx = NULL;
+
+ /*
+ * We can only generate private keys, check that the selection is
+ * appropriate.
+ */
+ if (!ossl_prov_is_running()
+ || (selection & minimal_selection) == 0
+ || (gctx = OPENSSL_zalloc(sizeof(*gctx))) == NULL)
+ return NULL;
+
+ gctx->evp_type = evp_type;
+ gctx->libctx = libctx;
+ gctx->selection = selection;
+ if (mlx_kem_gen_set_params(gctx, params))
+ return gctx;
+
+ mlx_kem_gen_cleanup(gctx);
+ return NULL;
+}
+
+static const OSSL_PARAM *mlx_kem_gen_settable_params(ossl_unused void *vgctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+
+ return settable;
+}
+
+static void *mlx_kem_gen(void *vgctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+ MLX_KEY *key;
+ char *propq;
+
+ if (gctx == NULL
+ || (gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) ==
+ OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
+ return NULL;
+
+ /* Lose ownership of propq */
+ propq = gctx->propq;
+ gctx->propq = NULL;
+ if ((key = mlx_kem_key_new(gctx->evp_type, gctx->libctx, propq)) == NULL)
+ return NULL;
+
+ if ((gctx->selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return key;
+
+ /* For now, using the same "propq" for all components */
+ key->mkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
+ key->minfo->algorithm_name);
+ key->xkey = EVP_PKEY_Q_keygen(key->libctx, key->propq,
+ key->xinfo->algorithm_name,
+ key->xinfo->group_name);
+ if (key->mkey != NULL && key->xkey != NULL) {
+ key->state = MLX_HAVE_PRVKEY;
+ return key;
+ }
+
+ mlx_kem_key_free(key);
+ return NULL;
+}
+
+static void mlx_kem_gen_cleanup(void *vgctx)
+{
+ PROV_ML_KEM_GEN_CTX *gctx = vgctx;
+
+ if (gctx == NULL)
+ return;
+ OPENSSL_free(gctx->propq);
+ OPENSSL_free(gctx);
+}
+
+static void *mlx_kem_dup(const void *vkey, int selection)
+{
+ const MLX_KEY *key = vkey;
+ MLX_KEY *ret;
+
+ if (!ossl_prov_is_running()
+ || (ret = OPENSSL_memdup(key, sizeof(*ret))) == NULL)
+ return NULL;
+
+ if (ret->propq != NULL
+ && (ret->propq = OPENSSL_strdup(ret->propq)) == NULL) {
+ OPENSSL_free(ret);
+ return NULL;
+ }
+
+ /* Absent key material, nothing left to do */
+ if (ret->mkey == NULL) {
+ if (ret->xkey == NULL)
+ return ret;
+ /* Fail if the source key is an inconsistent state */
+ OPENSSL_free(ret);
+ return NULL;
+ }
+
+ switch (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) {
+ case 0:
+ ret->xkey = ret->mkey = NULL;
+ return ret;
+ case OSSL_KEYMGMT_SELECT_KEYPAIR:
+ ret->mkey = EVP_PKEY_dup(key->mkey);
+ ret->xkey = EVP_PKEY_dup(key->xkey);
+ if (ret->xkey != NULL && ret->mkey != NULL)
+ return ret;
+ break;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_UNSUPPORTED_SELECTION,
+ "duplication of partial key material not supported");
+ break;
+ }
+
+ mlx_kem_key_free(ret);
+ return NULL;
+}
+
+#define DECLARE_DISPATCH(name, variant) \
+ static OSSL_FUNC_keymgmt_new_fn mlx_##name##_kem_new; \
+ static void *mlx_##name##_kem_new(void *provctx) \
+ { \
+ OSSL_LIB_CTX *libctx; \
+ \
+ libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \
+ return mlx_kem_key_new(variant, libctx, NULL); \
+ } \
+ static OSSL_FUNC_keymgmt_gen_init_fn mlx_##name##_kem_gen_init; \
+ static void *mlx_##name##_kem_gen_init(void *provctx, int selection, \
+ const OSSL_PARAM params[]) \
+ { \
+ OSSL_LIB_CTX *libctx; \
+ \
+ libctx = provctx == NULL ? NULL : PROV_LIBCTX_OF(provctx); \
+ return mlx_kem_gen_init(variant, libctx, selection, params); \
+ } \
+ const OSSL_DISPATCH ossl_mlx_##name##_kem_kmgmt_functions[] = { \
+ { OSSL_FUNC_KEYMGMT_NEW, (OSSL_FUNC) mlx_##name##_kem_new }, \
+ { OSSL_FUNC_KEYMGMT_FREE, (OSSL_FUNC) mlx_kem_key_free }, \
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (OSSL_FUNC) mlx_kem_get_params }, \
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gettable_params }, \
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (OSSL_FUNC) mlx_kem_set_params }, \
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_HAS, (OSSL_FUNC) mlx_kem_has }, \
+ { OSSL_FUNC_KEYMGMT_MATCH, (OSSL_FUNC) mlx_kem_match }, \
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (OSSL_FUNC) mlx_##name##_kem_gen_init }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (OSSL_FUNC) mlx_kem_gen_set_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (OSSL_FUNC) mlx_kem_gen_settable_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN, (OSSL_FUNC) mlx_kem_gen }, \
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (OSSL_FUNC) mlx_kem_gen_cleanup }, \
+ { OSSL_FUNC_KEYMGMT_DUP, (OSSL_FUNC) mlx_kem_dup }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT, (OSSL_FUNC) mlx_kem_import }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT, (OSSL_FUNC) mlx_kem_export }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (OSSL_FUNC) mlx_kem_imexport_types }, \
+ OSSL_DISPATCH_END \
+ }
+/* See |hybrid_vtable| above */
+DECLARE_DISPATCH(p256, 0);
+DECLARE_DISPATCH(p384, 1);
+#if !defined(OPENSSL_NO_ECX)
+DECLARE_DISPATCH(x25519, 2);
+DECLARE_DISPATCH(x448, 3);
+#endif
diff --git a/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c
new file mode 100644
index 000000000000..cd74275d604b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/rsa_kmgmt.c
@@ -0,0 +1,744 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/bn.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/evp.h>
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "crypto/rsa.h"
+#include "crypto/cryptlib.h"
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+
+static OSSL_FUNC_keymgmt_new_fn rsa_newdata;
+static OSSL_FUNC_keymgmt_new_fn rsapss_newdata;
+static OSSL_FUNC_keymgmt_gen_init_fn rsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_init_fn rsapss_gen_init;
+static OSSL_FUNC_keymgmt_gen_set_params_fn rsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn rsa_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn rsapss_gen_settable_params;
+static OSSL_FUNC_keymgmt_gen_fn rsa_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn rsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_load_fn rsa_load;
+static OSSL_FUNC_keymgmt_load_fn rsapss_load;
+static OSSL_FUNC_keymgmt_free_fn rsa_freedata;
+static OSSL_FUNC_keymgmt_get_params_fn rsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn rsa_gettable_params;
+static OSSL_FUNC_keymgmt_has_fn rsa_has;
+static OSSL_FUNC_keymgmt_match_fn rsa_match;
+static OSSL_FUNC_keymgmt_validate_fn rsa_validate;
+static OSSL_FUNC_keymgmt_import_fn rsa_import;
+static OSSL_FUNC_keymgmt_import_types_fn rsa_import_types;
+static OSSL_FUNC_keymgmt_export_fn rsa_export;
+static OSSL_FUNC_keymgmt_export_types_fn rsa_export_types;
+static OSSL_FUNC_keymgmt_query_operation_name_fn rsa_query_operation_name;
+static OSSL_FUNC_keymgmt_dup_fn rsa_dup;
+
+#define RSA_DEFAULT_MD "SHA256"
+#define RSA_POSSIBLE_SELECTIONS \
+ (OSSL_KEYMGMT_SELECT_KEYPAIR | OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS)
+
+DEFINE_STACK_OF(BIGNUM)
+DEFINE_SPECIAL_STACK_OF_CONST(BIGNUM_const, BIGNUM)
+
+static int pss_params_fromdata(RSA_PSS_PARAMS_30 *pss_params, int *defaults_set,
+ const OSSL_PARAM params[], int rsa_type,
+ OSSL_LIB_CTX *libctx)
+{
+ if (!ossl_rsa_pss_params_30_fromdata(pss_params, defaults_set,
+ params, libctx))
+ return 0;
+
+ /* If not a PSS type RSA, sending us PSS parameters is wrong */
+ if (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
+ && !ossl_rsa_pss_params_30_is_unrestricted(pss_params))
+ return 0;
+
+ return 1;
+}
+
+static void *rsa_newdata(void *provctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ RSA *rsa;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ rsa = ossl_rsa_new_with_ctx(libctx);
+ if (rsa != NULL) {
+ RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+ RSA_set_flags(rsa, RSA_FLAG_TYPE_RSA);
+ }
+ return rsa;
+}
+
+static void *rsapss_newdata(void *provctx)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ RSA *rsa;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ rsa = ossl_rsa_new_with_ctx(libctx);
+ if (rsa != NULL) {
+ RSA_clear_flags(rsa, RSA_FLAG_TYPE_MASK);
+ RSA_set_flags(rsa, RSA_FLAG_TYPE_RSASSAPSS);
+ }
+ return rsa;
+}
+
+static void rsa_freedata(void *keydata)
+{
+ RSA_free(keydata);
+}
+
+static int rsa_has(const void *keydata, int selection)
+{
+ const RSA *rsa = keydata;
+ int ok = 1;
+
+ if (rsa == NULL || !ossl_prov_is_running())
+ return 0;
+ if ((selection & RSA_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* the selection is not missing */
+
+ /* OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS are always available even if empty */
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ ok = ok && (RSA_get0_n(rsa) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && (RSA_get0_e(rsa) != NULL);
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && (RSA_get0_d(rsa) != NULL);
+ return ok;
+}
+
+static int rsa_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const RSA *rsa1 = keydata1;
+ const RSA *rsa2 = keydata2;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* There is always an |e| */
+ ok = ok && BN_cmp(RSA_get0_e(rsa1), RSA_get0_e(rsa2)) == 0;
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ const BIGNUM *pa = RSA_get0_n(rsa1);
+ const BIGNUM *pb = RSA_get0_n(rsa2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ const BIGNUM *pa = RSA_get0_d(rsa1);
+ const BIGNUM *pb = RSA_get0_d(rsa2);
+
+ if (pa != NULL && pb != NULL) {
+ ok = ok && BN_cmp(pa, pb) == 0;
+ key_checked = 1;
+ }
+ }
+ ok = ok && key_checked;
+ }
+ return ok;
+}
+
+static int rsa_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ RSA *rsa = keydata;
+ int rsa_type;
+ int ok = 1;
+ int pss_defaults_set = 0;
+
+ if (!ossl_prov_is_running() || rsa == NULL)
+ return 0;
+
+ if ((selection & RSA_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK);
+
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+ ok = ok && pss_params_fromdata(ossl_rsa_get0_pss_params_30(rsa),
+ &pss_defaults_set,
+ params, rsa_type,
+ ossl_rsa_get0_libctx(rsa));
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_rsa_fromdata(rsa, params, include_private);
+ }
+
+ return ok;
+}
+
+static int rsa_export(void *keydata, int selection,
+ OSSL_CALLBACK *param_callback, void *cbarg)
+{
+ RSA *rsa = keydata;
+ const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30(rsa);
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ok = 1;
+
+ if (!ossl_prov_is_running() || rsa == NULL)
+ return 0;
+
+ if ((selection & RSA_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_OTHER_PARAMETERS) != 0)
+ ok = ok && (ossl_rsa_pss_params_30_is_unrestricted(pss_params)
+ || ossl_rsa_pss_params_30_todata(pss_params, tmpl, NULL));
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private =
+ selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+
+ ok = ok && ossl_rsa_todata(rsa, tmpl, NULL, include_private);
+ }
+
+ if (!ok || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) {
+ ok = 0;
+ goto err;
+ }
+
+ ok = param_callback(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ok;
+}
+
+#ifdef FIPS_MODULE
+/* In fips mode there are no multi-primes. */
+# define RSA_KEY_MP_TYPES() \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0),
+#else
+/*
+ * We allow up to 10 prime factors (starting with p, q).
+ * NOTE: there is only 9 OSSL_PKEY_PARAM_RSA_COEFFICIENT
+ */
+# define RSA_KEY_MP_TYPES() \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR3, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR4, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR5, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR6, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR7, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR8, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR9, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_FACTOR10, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT2, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT3, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT4, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT5, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT6, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT7, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT8, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT9, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_EXPONENT10, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT2, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT3, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT4, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT5, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT6, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT7, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT8, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT9, NULL, 0),
+#endif
+
+#define RSA_KEY_TYPES() \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_N, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0), \
+OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_D, NULL, 0), \
+RSA_KEY_MP_TYPES()
+
+/*
+ * This provider can export everything in an RSA key, so we use the exact
+ * same type description for export as for import. Other providers might
+ * choose to import full keys, but only export the public parts, and will
+ * therefore have the importkey_types and importkey_types functions return
+ * different arrays.
+ */
+static const OSSL_PARAM rsa_key_types[] = {
+ RSA_KEY_TYPES()
+ OSSL_PARAM_END
+};
+/*
+ * We lied about the amount of factors, exponents and coefficients, the
+ * export and import functions can really deal with an infinite amount
+ * of these numbers. However, RSA keys with too many primes are futile,
+ * so we at least pretend to have some limits.
+ */
+
+static const OSSL_PARAM *rsa_imexport_types(int selection)
+{
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return rsa_key_types;
+ return NULL;
+}
+
+static const OSSL_PARAM *rsa_import_types(int selection)
+{
+ return rsa_imexport_types(selection);
+}
+
+static const OSSL_PARAM *rsa_export_types(int selection)
+{
+ return rsa_imexport_types(selection);
+}
+
+static int rsa_get_params(void *key, OSSL_PARAM params[])
+{
+ RSA *rsa = key;
+ const RSA_PSS_PARAMS_30 *pss_params = ossl_rsa_get0_pss_params_30(rsa);
+ int rsa_type = RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK);
+ OSSL_PARAM *p;
+ int empty = RSA_get0_n(rsa) == NULL;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && (empty || !OSSL_PARAM_set_int(p, RSA_bits(rsa))))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && (empty || !OSSL_PARAM_set_int(p, RSA_security_bits(rsa))))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && (empty || !OSSL_PARAM_set_int(p, RSA_size(rsa))))
+ return 0;
+
+ /*
+ * For restricted RSA-PSS keys, we ignore the default digest request.
+ * With RSA-OAEP keys, this may need to be amended.
+ */
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_DEFAULT_DIGEST)) != NULL
+ && (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
+ || ossl_rsa_pss_params_30_is_unrestricted(pss_params))) {
+ if (!OSSL_PARAM_set_utf8_string(p, RSA_DEFAULT_MD))
+ return 0;
+ }
+
+ /*
+ * For non-RSA-PSS keys, we ignore the mandatory digest request.
+ * With RSA-OAEP keys, this may need to be amended.
+ */
+ if ((p = OSSL_PARAM_locate(params,
+ OSSL_PKEY_PARAM_MANDATORY_DIGEST)) != NULL
+ && rsa_type == RSA_FLAG_TYPE_RSASSAPSS
+ && !ossl_rsa_pss_params_30_is_unrestricted(pss_params)) {
+ const char *mdname =
+ ossl_rsa_oaeppss_nid2name(ossl_rsa_pss_params_30_hashalg(pss_params));
+
+ if (mdname == NULL || !OSSL_PARAM_set_utf8_string(p, mdname))
+ return 0;
+ }
+ return (rsa_type != RSA_FLAG_TYPE_RSASSAPSS
+ || ossl_rsa_pss_params_30_todata(pss_params, NULL, params))
+ && ossl_rsa_todata(rsa, NULL, params, 1);
+}
+
+static const OSSL_PARAM rsa_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST, NULL, 0),
+ RSA_KEY_TYPES()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_gettable_params(void *provctx)
+{
+ return rsa_params;
+}
+
+static int rsa_validate(const void *keydata, int selection, int checktype)
+{
+ const RSA *rsa = keydata;
+ int ok = 1;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & RSA_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* nothing to validate */
+
+ /* If the whole key is selected, we do a pairwise validation */
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
+ == OSSL_KEYMGMT_SELECT_KEYPAIR) {
+ ok = ok && ossl_rsa_validate_pairwise(rsa);
+ } else {
+ if ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ ok = ok && ossl_rsa_validate_private(rsa);
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0)
+ ok = ok && ossl_rsa_validate_public(rsa);
+ }
+ return ok;
+}
+
+struct rsa_gen_ctx {
+ OSSL_LIB_CTX *libctx;
+ const char *propq;
+
+ int rsa_type;
+
+ size_t nbits;
+ BIGNUM *pub_exp;
+ size_t primes;
+
+ /* For PSS */
+ RSA_PSS_PARAMS_30 pss_params;
+ int pss_defaults_set;
+
+ /* For generation callback */
+ OSSL_CALLBACK *cb;
+ void *cbarg;
+
+#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)
+ /* ACVP test parameters */
+ OSSL_PARAM *acvp_test_params;
+#endif
+};
+
+static int rsa_gencb(int p, int n, BN_GENCB *cb)
+{
+ struct rsa_gen_ctx *gctx = BN_GENCB_get_arg(cb);
+ OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END };
+
+ params[0] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_POTENTIAL, &p);
+ params[1] = OSSL_PARAM_construct_int(OSSL_GEN_PARAM_ITERATION, &n);
+ return gctx->cb(params, gctx->cbarg);
+}
+
+static void *gen_init(void *provctx, int selection, int rsa_type,
+ const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct rsa_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ if ((gctx->pub_exp = BN_new()) == NULL
+ || !BN_set_word(gctx->pub_exp, RSA_F4)) {
+ goto err;
+ }
+ gctx->nbits = 2048;
+ gctx->primes = RSA_DEFAULT_PRIME_NUM;
+ gctx->rsa_type = rsa_type;
+ } else {
+ goto err;
+ }
+
+ if (!rsa_gen_set_params(gctx, params))
+ goto err;
+ return gctx;
+
+err:
+ if (gctx != NULL)
+ BN_free(gctx->pub_exp);
+ OPENSSL_free(gctx);
+ return NULL;
+}
+
+static void *rsa_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return gen_init(provctx, selection, RSA_FLAG_TYPE_RSA, params);
+}
+
+static void *rsapss_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ return gen_init(provctx, selection, RSA_FLAG_TYPE_RSASSAPSS, params);
+}
+
+/*
+ * This function is common for all RSA sub-types, to detect possible
+ * misuse, such as PSS parameters being passed when a plain RSA key
+ * is generated.
+ */
+static int rsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct rsa_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_BITS)) != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &gctx->nbits))
+ return 0;
+ if (gctx->nbits < RSA_MIN_MODULUS_BITS) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL);
+ return 0;
+ }
+ }
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_PRIMES)) != NULL
+ && !OSSL_PARAM_get_size_t(p, &gctx->primes))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_RSA_E)) != NULL
+ && !OSSL_PARAM_get_BN(p, &gctx->pub_exp))
+ return 0;
+ /* Only attempt to get PSS parameters when generating an RSA-PSS key */
+ if (gctx->rsa_type == RSA_FLAG_TYPE_RSASSAPSS
+ && !pss_params_fromdata(&gctx->pss_params, &gctx->pss_defaults_set, params,
+ gctx->rsa_type, gctx->libctx))
+ return 0;
+#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)
+ /* Any ACVP test related parameters are copied into a params[] */
+ if (!ossl_rsa_acvp_test_gen_params_new(&gctx->acvp_test_params, params))
+ return 0;
+#endif
+ return 1;
+}
+
+#define rsa_gen_basic \
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_BITS, NULL), \
+ OSSL_PARAM_size_t(OSSL_PKEY_PARAM_RSA_PRIMES, NULL), \
+ OSSL_PARAM_BN(OSSL_PKEY_PARAM_RSA_E, NULL, 0)
+
+/*
+ * The following must be kept in sync with ossl_rsa_pss_params_30_fromdata()
+ * in crypto/rsa/rsa_backend.c
+ */
+#define rsa_gen_pss \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_DIGEST_PROPS, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MASKGENFUNC, NULL, 0), \
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_RSA_MGF1_DIGEST, NULL, 0), \
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_RSA_PSS_SALTLEN, NULL)
+
+static const OSSL_PARAM *rsa_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ rsa_gen_basic,
+ OSSL_PARAM_END
+ };
+
+ return settable;
+}
+
+static const OSSL_PARAM *rsapss_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ rsa_gen_basic,
+ rsa_gen_pss,
+ OSSL_PARAM_END
+ };
+
+ return settable;
+}
+
+static void *rsa_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct rsa_gen_ctx *gctx = genctx;
+ RSA *rsa = NULL, *rsa_tmp = NULL;
+ BN_GENCB *gencb = NULL;
+
+ if (!ossl_prov_is_running() || gctx == NULL)
+ return NULL;
+
+ switch (gctx->rsa_type) {
+ case RSA_FLAG_TYPE_RSA:
+ /* For plain RSA keys, PSS parameters must not be set */
+ if (!ossl_rsa_pss_params_30_is_unrestricted(&gctx->pss_params))
+ goto err;
+ break;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ /*
+ * For plain RSA-PSS keys, PSS parameters may be set but don't have
+ * to, so not check.
+ */
+ break;
+ default:
+ /* Unsupported RSA key sub-type... */
+ return NULL;
+ }
+
+ if ((rsa_tmp = ossl_rsa_new_with_ctx(gctx->libctx)) == NULL)
+ return NULL;
+
+ gctx->cb = osslcb;
+ gctx->cbarg = cbarg;
+ gencb = BN_GENCB_new();
+ if (gencb != NULL)
+ BN_GENCB_set(gencb, rsa_gencb, genctx);
+
+#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)
+ if (gctx->acvp_test_params != NULL) {
+ if (!ossl_rsa_acvp_test_set_params(rsa_tmp, gctx->acvp_test_params))
+ goto err;
+ }
+#endif
+
+ if (!RSA_generate_multi_prime_key(rsa_tmp,
+ (int)gctx->nbits, (int)gctx->primes,
+ gctx->pub_exp, gencb))
+ goto err;
+
+ if (!ossl_rsa_pss_params_30_copy(ossl_rsa_get0_pss_params_30(rsa_tmp),
+ &gctx->pss_params))
+ goto err;
+
+ RSA_clear_flags(rsa_tmp, RSA_FLAG_TYPE_MASK);
+ RSA_set_flags(rsa_tmp, gctx->rsa_type);
+
+ rsa = rsa_tmp;
+ rsa_tmp = NULL;
+ err:
+ BN_GENCB_free(gencb);
+ RSA_free(rsa_tmp);
+ return rsa;
+}
+
+static void rsa_gen_cleanup(void *genctx)
+{
+ struct rsa_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+#if defined(FIPS_MODULE) && !defined(OPENSSL_NO_ACVP_TESTS)
+ ossl_rsa_acvp_test_gen_params_free(gctx->acvp_test_params);
+ gctx->acvp_test_params = NULL;
+#endif
+ BN_clear_free(gctx->pub_exp);
+ OPENSSL_free(gctx);
+}
+
+static void *common_load(const void *reference, size_t reference_sz,
+ int expected_rsa_type)
+{
+ RSA *rsa = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(rsa)) {
+ /* The contents of the reference is the address to our object */
+ rsa = *(RSA **)reference;
+
+ if (RSA_test_flags(rsa, RSA_FLAG_TYPE_MASK) != expected_rsa_type)
+ return NULL;
+
+ /* We grabbed, so we detach it */
+ *(RSA **)reference = NULL;
+ return rsa;
+ }
+ return NULL;
+}
+
+static void *rsa_load(const void *reference, size_t reference_sz)
+{
+ return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSA);
+}
+
+static void *rsapss_load(const void *reference, size_t reference_sz)
+{
+ return common_load(reference, reference_sz, RSA_FLAG_TYPE_RSASSAPSS);
+}
+
+static void *rsa_dup(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running()
+ /* do not allow creating empty keys by duplication */
+ && (selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return ossl_rsa_dup(keydata_from, selection);
+ return NULL;
+}
+
+/* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */
+static const char *rsa_query_operation_name(int operation_id)
+{
+ return "RSA";
+}
+
+const OSSL_DISPATCH ossl_rsa_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsa_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsa_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS,
+ (void (*)(void))rsa_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))rsa_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsa_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))rsa_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))rsa_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))rsa_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))rsa_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))rsa_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))rsa_dup },
+ OSSL_DISPATCH_END
+};
+
+const OSSL_DISPATCH ossl_rsapss_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))rsapss_newdata },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))rsapss_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))rsa_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))rsapss_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))rsa_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))rsa_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))rsapss_load },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))rsa_freedata },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))rsa_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))rsa_gettable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))rsa_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))rsa_match },
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))rsa_validate },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))rsa_import },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))rsa_import_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types },
+ { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME,
+ (void (*)(void))rsa_query_operation_name },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))rsa_dup },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c
new file mode 100644
index 000000000000..721617229467
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/slh_dsa_kmgmt.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/param_build.h>
+#include <openssl/self_test.h>
+#include <openssl/proverr.h>
+#include "crypto/slh_dsa.h"
+#include "internal/fips.h"
+#include "internal/param_build_set.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+
+#ifdef FIPS_MODULE
+static int slh_dsa_fips140_pairwise_test(const SLH_DSA_KEY *key,
+ SLH_DSA_HASH_CTX *ctx);
+#endif /* FIPS_MODULE */
+
+static OSSL_FUNC_keymgmt_free_fn slh_dsa_free_key;
+static OSSL_FUNC_keymgmt_has_fn slh_dsa_has;
+static OSSL_FUNC_keymgmt_match_fn slh_dsa_match;
+static OSSL_FUNC_keymgmt_import_fn slh_dsa_import;
+static OSSL_FUNC_keymgmt_export_fn slh_dsa_export;
+static OSSL_FUNC_keymgmt_import_types_fn slh_dsa_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn slh_dsa_imexport_types;
+static OSSL_FUNC_keymgmt_load_fn slh_dsa_load;
+static OSSL_FUNC_keymgmt_get_params_fn slh_dsa_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn slh_dsa_gettable_params;
+static OSSL_FUNC_keymgmt_validate_fn slh_dsa_validate;
+static OSSL_FUNC_keymgmt_gen_init_fn slh_dsa_gen_init;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn slh_dsa_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn slh_dsa_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn slh_dsa_gen_settable_params;
+static OSSL_FUNC_keymgmt_dup_fn slh_dsa_dup_key;
+
+#define SLH_DSA_POSSIBLE_SELECTIONS (OSSL_KEYMGMT_SELECT_KEYPAIR)
+
+struct slh_dsa_gen_ctx {
+ SLH_DSA_HASH_CTX *ctx;
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ uint8_t entropy[SLH_DSA_MAX_N * 3];
+ size_t entropy_len;
+};
+
+static void *slh_dsa_new_key(void *provctx, const char *alg)
+{
+ if (!ossl_prov_is_running())
+ return 0;
+
+ return ossl_slh_dsa_key_new(PROV_LIBCTX_OF(provctx), NULL, alg);
+}
+
+static void slh_dsa_free_key(void *keydata)
+{
+ ossl_slh_dsa_key_free((SLH_DSA_KEY *)keydata);
+}
+
+static void *slh_dsa_dup_key(const void *keydata_from, int selection)
+{
+ if (ossl_prov_is_running())
+ return ossl_slh_dsa_key_dup(keydata_from, selection);
+ return NULL;
+}
+
+static int slh_dsa_has(const void *keydata, int selection)
+{
+ const SLH_DSA_KEY *key = keydata;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+ if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+ return 1; /* the selection is not missing */
+
+ return ossl_slh_dsa_key_has(key, selection);
+}
+
+static int slh_dsa_match(const void *keydata1, const void *keydata2, int selection)
+{
+ const SLH_DSA_KEY *key1 = keydata1;
+ const SLH_DSA_KEY *key2 = keydata2;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (key1 == NULL || key2 == NULL)
+ return 0;
+ return ossl_slh_dsa_key_equal(key1, key2, selection);
+}
+
+static int slh_dsa_validate(const void *key_data, int selection, int check_type)
+{
+ const SLH_DSA_KEY *key = key_data;
+
+ if (!slh_dsa_has(key, selection))
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == OSSL_KEYMGMT_SELECT_KEYPAIR)
+ return ossl_slh_dsa_key_pairwise_check(key);
+ return 1;
+}
+
+static int slh_dsa_import(void *keydata, int selection, const OSSL_PARAM params[])
+{
+ SLH_DSA_KEY *key = keydata;
+ int include_priv;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+ return 0;
+
+ include_priv = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+ return ossl_slh_dsa_key_fromdata(key, params, include_priv);
+}
+
+#define SLH_DSA_IMEXPORTABLE_PARAMETERS \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+
+static const OSSL_PARAM slh_dsa_key_types[] = {
+ SLH_DSA_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *slh_dsa_imexport_types(int selection)
+{
+ if ((selection & SLH_DSA_POSSIBLE_SELECTIONS) == 0)
+ return NULL;
+ return slh_dsa_key_types;
+}
+
+static const OSSL_PARAM slh_dsa_params[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST, NULL, 0),
+ SLH_DSA_IMEXPORTABLE_PARAMETERS,
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *slh_dsa_gettable_params(void *provctx)
+{
+ return slh_dsa_params;
+}
+
+static int key_to_params(SLH_DSA_KEY *key, OSSL_PARAM_BLD *tmpl,
+ int selection)
+{
+ /* Error if there is no key or public key */
+ if (key == NULL || ossl_slh_dsa_key_get_pub(key) == NULL)
+ return 0;
+
+ if (((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0)
+ && ossl_slh_dsa_key_get_priv(key) != NULL)
+ if (ossl_param_build_set_octet_string(tmpl, NULL,
+ OSSL_PKEY_PARAM_PRIV_KEY,
+ ossl_slh_dsa_key_get_priv(key),
+ ossl_slh_dsa_key_get_priv_len(key)) != 1)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) == 0)
+ return 1;
+
+ return ossl_param_build_set_octet_string(tmpl, NULL,
+ OSSL_PKEY_PARAM_PUB_KEY,
+ ossl_slh_dsa_key_get_pub(key),
+ ossl_slh_dsa_key_get_pub_len(key));
+}
+
+static int slh_dsa_get_params(void *keydata, OSSL_PARAM params[])
+{
+ SLH_DSA_KEY *key = keydata;
+ OSSL_PARAM *p;
+ const uint8_t *pub, *priv;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, 8 * ossl_slh_dsa_key_get_pub_len(key)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, 8 * ossl_slh_dsa_key_get_n(key)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, ossl_slh_dsa_key_get_sig_len(key)))
+ return 0;
+
+ priv = ossl_slh_dsa_key_get_priv(key);
+ if (priv != NULL) {
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PRIV_KEY);
+ /* Note: ossl_slh_dsa_key_get_priv_len() includes the public key */
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p, priv,
+ ossl_slh_dsa_key_get_priv_len(key)))
+ return 0;
+ }
+ pub = ossl_slh_dsa_key_get_pub(key);
+ if (pub != NULL) {
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p, pub,
+ ossl_slh_dsa_key_get_pub_len(key)))
+ return 0;
+ }
+ /*
+ * This allows apps to use an empty digest, so that the old API
+ * for digest signing can be used.
+ */
+ p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MANDATORY_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, ""))
+ return 0;
+ return 1;
+}
+
+static int slh_dsa_export(void *keydata, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ SLH_DSA_KEY *key = keydata;
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ret = 0;
+
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if (!key_to_params(key, tmpl, selection))
+ goto err;
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ return ret;
+}
+
+static void *slh_dsa_load(const void *reference, size_t reference_sz)
+{
+ SLH_DSA_KEY *key = NULL;
+
+ if (ossl_prov_is_running() && reference_sz == sizeof(key)) {
+ /* The contents of the reference is the address to our object */
+ key = *(SLH_DSA_KEY **)reference;
+ /* We grabbed, so we detach it */
+ *(SLH_DSA_KEY **)reference = NULL;
+ return key;
+ }
+ return NULL;
+}
+
+static void *slh_dsa_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ struct slh_dsa_gen_ctx *gctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->libctx = libctx;
+ if (!slh_dsa_gen_set_params(gctx, params)) {
+ OPENSSL_free(gctx);
+ gctx = NULL;
+ }
+ }
+ return gctx;
+}
+
+#ifdef FIPS_MODULE
+/*
+ * Refer to FIPS 140-3 IG 10.3.A Additional Comment 1
+ * Perform a pairwise test for SLH_DSA by signing and verifying a signature.
+ */
+static int slh_dsa_fips140_pairwise_test(const SLH_DSA_KEY *key,
+ SLH_DSA_HASH_CTX *ctx)
+{
+ int ret = 0;
+ OSSL_SELF_TEST *st = NULL;
+ OSSL_CALLBACK *cb = NULL;
+ void *cb_arg = NULL;
+ uint8_t msg[16] = {0};
+ size_t msg_len = sizeof(msg);
+ uint8_t *sig = NULL;
+ size_t sig_len;
+ OSSL_LIB_CTX *lib_ctx;
+ int alloc_ctx = 0;
+
+ /* During self test, it is a waste to do this test */
+ if (ossl_fips_self_testing())
+ return 1;
+
+ if (ctx == NULL) {
+ ctx = ossl_slh_dsa_hash_ctx_new(key);
+ if (ctx == NULL)
+ return 0;
+ alloc_ctx = 1;
+ }
+ lib_ctx = ossl_slh_dsa_key_get0_libctx(key);
+
+ OSSL_SELF_TEST_get_callback(lib_ctx, &cb, &cb_arg);
+ st = OSSL_SELF_TEST_new(cb, cb_arg);
+ if (st == NULL)
+ goto err;
+
+ OSSL_SELF_TEST_onbegin(st, OSSL_SELF_TEST_TYPE_PCT,
+ OSSL_SELF_TEST_DESC_PCT_SLH_DSA);
+
+ sig_len = ossl_slh_dsa_key_get_sig_len(key);
+ sig = OPENSSL_malloc(sig_len);
+ if (sig == NULL)
+ goto err;
+
+ if (ossl_slh_dsa_sign(ctx, msg, msg_len, NULL, 0, NULL, 0,
+ sig, &sig_len, sig_len) != 1)
+ goto err;
+
+ OSSL_SELF_TEST_oncorrupt_byte(st, sig);
+
+ if (ossl_slh_dsa_verify(ctx, msg, msg_len, NULL, 0, 0, sig, sig_len) != 1)
+ goto err;
+
+ ret = 1;
+err:
+ if (alloc_ctx)
+ ossl_slh_dsa_hash_ctx_free(ctx);
+ OPENSSL_free(sig);
+ OSSL_SELF_TEST_onend(st, ret);
+ OSSL_SELF_TEST_free(st);
+ return ret;
+}
+#endif /* FIPS_MODULE */
+
+static void *slh_dsa_gen(void *genctx, const char *alg)
+{
+ struct slh_dsa_gen_ctx *gctx = genctx;
+ SLH_DSA_KEY *key = NULL;
+ SLH_DSA_HASH_CTX *ctx = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ key = ossl_slh_dsa_key_new(gctx->libctx, gctx->propq, alg);
+ if (key == NULL)
+ return NULL;
+ ctx = ossl_slh_dsa_hash_ctx_new(key);
+ if (ctx == NULL)
+ goto err;
+ if (!ossl_slh_dsa_generate_key(ctx, key, gctx->libctx,
+ gctx->entropy, gctx->entropy_len))
+ goto err;
+#ifdef FIPS_MODULE
+ if (!slh_dsa_fips140_pairwise_test(key, ctx)) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_PCT);
+ goto err;
+ }
+#endif /* FIPS_MODULE */
+ ossl_slh_dsa_hash_ctx_free(ctx);
+ return key;
+ err:
+ ossl_slh_dsa_hash_ctx_free(ctx);
+ ossl_slh_dsa_key_free(key);
+ return NULL;
+}
+
+static int slh_dsa_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct slh_dsa_gen_ctx *gctx = genctx;
+ const OSSL_PARAM *p;
+
+ if (gctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_SLH_DSA_SEED);
+ if (p != NULL) {
+ void *vp = gctx->entropy;
+ size_t len = sizeof(gctx->entropy);
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp, len, &(gctx->entropy_len))) {
+ gctx->entropy_len = 0;
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PROPERTIES);
+ if (p != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ OPENSSL_free(gctx->propq);
+ gctx->propq = OPENSSL_strdup(p->data);
+ if (gctx->propq == NULL)
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM *slh_dsa_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_utf8_string(OSSL_PKEY_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_SLH_DSA_SEED, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static void slh_dsa_gen_cleanup(void *genctx)
+{
+ struct slh_dsa_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ OPENSSL_cleanse(gctx->entropy, gctx->entropy_len);
+ OPENSSL_free(gctx->propq);
+ OPENSSL_free(gctx);
+}
+
+#define MAKE_KEYMGMT_FUNCTIONS(alg, fn) \
+ static OSSL_FUNC_keymgmt_new_fn slh_dsa_##fn##_new_key; \
+ static OSSL_FUNC_keymgmt_gen_fn slh_dsa_##fn##_gen; \
+ static void *slh_dsa_##fn##_new_key(void *provctx) \
+ { \
+ return slh_dsa_new_key(provctx, alg); \
+ } \
+ static void *slh_dsa_##fn##_gen(void *genctx, OSSL_CALLBACK *osslcb, void *cbarg)\
+ { \
+ return slh_dsa_gen(genctx, alg); \
+ } \
+ const OSSL_DISPATCH ossl_slh_dsa_##fn##_keymgmt_functions[] = { \
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))slh_dsa_##fn##_new_key }, \
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))slh_dsa_free_key }, \
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))slh_dsa_dup_key }, \
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))slh_dsa_has }, \
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))slh_dsa_match }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))slh_dsa_import }, \
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))slh_dsa_export }, \
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))slh_dsa_imexport_types },\
+ { OSSL_FUNC_KEYMGMT_LOAD, (void (*)(void))slh_dsa_load }, \
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))slh_dsa_get_params }, \
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))slh_dsa_gettable_params },\
+ { OSSL_FUNC_KEYMGMT_VALIDATE, (void (*)(void))slh_dsa_validate }, \
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))slh_dsa_gen_init }, \
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))slh_dsa_##fn##_gen }, \
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))slh_dsa_gen_cleanup },\
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, \
+ (void (*)(void))slh_dsa_gen_set_params }, \
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, \
+ (void (*)(void))slh_dsa_gen_settable_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-128f", sha2_128f);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-192s", sha2_192s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-192f", sha2_192f);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-256s", sha2_256s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHA2-256f", sha2_256f);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-128s", shake_128s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-128f", shake_128f);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-192s", shake_192s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-192f", shake_192f);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-256s", shake_256s);
+MAKE_KEYMGMT_FUNCTIONS("SLH-DSA-SHAKE-256f", shake_256f);
diff --git a/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c b/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c
new file mode 100644
index 000000000000..0d62f556ecd5
--- /dev/null
+++ b/crypto/openssl/providers/implementations/keymgmt/template_kmgmt.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/evp.h>
+#include <openssl/rand.h>
+#include <openssl/self_test.h>
+#include "internal/param_build_set.h"
+#include <openssl/param_build.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+
+extern const OSSL_DISPATCH ossl_template_keymgmt_functions[];
+
+#define BUFSIZE 1000
+#if defined(NDEBUG) || defined(OPENSSL_NO_STDIO)
+static void debug_print(char *fmt, ...)
+{
+}
+#else
+static void debug_print(char *fmt, ...)
+{
+ char out[BUFSIZE];
+ va_list argptr;
+
+ va_start(argptr, fmt);
+ vsnprintf(out, BUFSIZE, fmt, argptr);
+ va_end(argptr);
+ if (getenv("TEMPLATEKM"))
+ fprintf(stderr, "TEMPLATE_KM: %s", out);
+}
+#endif
+
+static OSSL_FUNC_keymgmt_new_fn template_new;
+static OSSL_FUNC_keymgmt_free_fn template_free;
+static OSSL_FUNC_keymgmt_gen_init_fn template_gen_init;
+static OSSL_FUNC_keymgmt_gen_fn template_gen;
+static OSSL_FUNC_keymgmt_gen_cleanup_fn template_gen_cleanup;
+static OSSL_FUNC_keymgmt_gen_set_params_fn template_gen_set_params;
+static OSSL_FUNC_keymgmt_gen_settable_params_fn template_gen_settable_params;
+static OSSL_FUNC_keymgmt_get_params_fn template_get_params;
+static OSSL_FUNC_keymgmt_gettable_params_fn template_gettable_params;
+static OSSL_FUNC_keymgmt_set_params_fn template_set_params;
+static OSSL_FUNC_keymgmt_settable_params_fn template_settable_params;
+static OSSL_FUNC_keymgmt_has_fn template_has;
+static OSSL_FUNC_keymgmt_match_fn template_match;
+static OSSL_FUNC_keymgmt_import_fn template_import;
+static OSSL_FUNC_keymgmt_export_fn template_export;
+static OSSL_FUNC_keymgmt_import_types_fn template_imexport_types;
+static OSSL_FUNC_keymgmt_export_types_fn template_imexport_types;
+
+static OSSL_FUNC_keymgmt_dup_fn template_dup;
+
+struct template_gen_ctx {
+ void *provctx;
+ int selection;
+};
+
+static void *template_new(void *provctx)
+{
+ void *key = NULL;
+
+ debug_print("new key req\n");
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* add logic to create new key */
+
+ debug_print("new key = %p\n", key);
+ return key;
+}
+
+static void template_free(void *vkey)
+{
+ debug_print("free key %p\n", vkey);
+ if (vkey == NULL)
+ return;
+
+ /* add logic to free all key components */
+
+ OPENSSL_free(vkey);
+}
+
+static int template_has(const void *keydata, int selection)
+{
+ int ok = 0;
+
+ debug_print("has %p\n", keydata);
+ if (ossl_prov_is_running() && keydata != NULL) {
+ /* add logic to check whether this key has the requested parameters */
+ }
+ debug_print("has result %d\n", ok);
+ return ok;
+}
+
+static int template_match(const void *keydata1, const void *keydata2, int selection)
+{
+ int ok = 1;
+
+ debug_print("matching %p and %p\n", keydata1, keydata2);
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS) != 0)
+ ok = ok && keydata1 != NULL && keydata2 != NULL;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int key_checked = 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) != 0) {
+ /* validate whether the public keys match */
+ }
+ if (!key_checked
+ && (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0) {
+ /* validate whether the private keys match */
+ }
+ ok = ok && key_checked;
+ }
+ debug_print("match result %d\n", ok);
+ return ok;
+}
+
+static int key_to_params(void *key, OSSL_PARAM_BLD *tmpl,
+ OSSL_PARAM params[], int include_private)
+{
+ if (key == NULL)
+ return 0;
+
+ /* add public and/or private key parts to templ as possible */
+
+ return 1;
+}
+
+static int template_export(void *key, int selection, OSSL_CALLBACK *param_cb,
+ void *cbarg)
+{
+ OSSL_PARAM_BLD *tmpl;
+ OSSL_PARAM *params = NULL;
+ int ret = 0;
+
+ debug_print("export %p\n", key);
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ tmpl = OSSL_PARAM_BLD_new();
+ if (tmpl == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0) {
+ int include_private = ((selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) != 0);
+
+ if (!key_to_params(key, tmpl, NULL, include_private))
+ goto err;
+ }
+
+ params = OSSL_PARAM_BLD_to_param(tmpl);
+ if (params == NULL)
+ goto err;
+
+ ret = param_cb(params, cbarg);
+ OSSL_PARAM_free(params);
+err:
+ OSSL_PARAM_BLD_free(tmpl);
+ debug_print("export result %d\n", ret);
+ return ret;
+}
+
+static int ossl_template_key_fromdata(void *key,
+ const OSSL_PARAM params[],
+ int include_private)
+{
+ const OSSL_PARAM *param_priv_key = NULL, *param_pub_key;
+
+ if (key == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 0;
+
+ /* validate integrity of key (algorithm type specific) */
+
+ param_pub_key = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PUB_KEY);
+ if (include_private)
+ param_priv_key =
+ OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_PRIV_KEY);
+
+ if (param_pub_key == NULL && param_priv_key == NULL)
+ return 0;
+
+ if (param_priv_key != NULL) {
+ /* retrieve private key and check integrity */
+ }
+
+ if (param_pub_key != NULL) {
+ /* retrieve public key and check integrity */
+ }
+
+ return 1;
+}
+
+static int template_import(void *key, int selection, const OSSL_PARAM params[])
+{
+ int ok = 1;
+ int include_private;
+
+ debug_print("import %p\n", key);
+ if (!ossl_prov_is_running() || key == NULL)
+ return 0;
+
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) == 0)
+ return 0;
+
+ include_private = selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY ? 1 : 0;
+ ok = ok && ossl_template_key_fromdata(key, params, include_private);
+
+ debug_print("import result %d\n", ok);
+ return ok;
+}
+
+#define TEMPLATE_KEY_TYPES() \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY, NULL, 0), \
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY, NULL, 0)
+
+static const OSSL_PARAM template_key_types[] = {
+ TEMPLATE_KEY_TYPES(),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *template_imexport_types(int selection)
+{
+ debug_print("getting imexport types\n");
+ if ((selection & OSSL_KEYMGMT_SELECT_KEYPAIR) != 0)
+ return template_key_types;
+ return NULL;
+}
+
+static int template_get_params(void *key, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ debug_print("get params %p\n", key);
+
+ if (ossl_param_is_empty(params))
+ return 0;
+
+ /* return sensible values for at least these parameters */
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, 0))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_SECURITY_BITS)) != NULL
+ && !OSSL_PARAM_set_int(p, 0))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_MAX_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, 0))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY)) != NULL) {
+ if (!OSSL_PARAM_set_octet_string(p, NULL, 0))
+ return 0;
+ }
+
+ debug_print("get params OK\n");
+ return 1;
+}
+
+static const OSSL_PARAM template_gettable_params_arr[] = {
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_SECURITY_BITS, NULL),
+ OSSL_PARAM_int(OSSL_PKEY_PARAM_MAX_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *template_gettable_params(void *provctx)
+{
+ debug_print("gettable params called\n");
+ return template_gettable_params_arr;
+}
+
+static int template_set_params(void *key, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+
+ debug_print("set params called for %p\n", key);
+ if (ossl_param_is_empty(params))
+ return 1; /* OK not to set anything */
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY);
+ if (p != NULL) {
+ /* load public key structure */
+ }
+
+ debug_print("set params OK\n");
+ return 1;
+}
+
+static const OSSL_PARAM template_settable_params_arr[] = {
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *template_settable_params(void *provctx)
+{
+ debug_print("settable params called\n");
+ return template_settable_params_arr;
+}
+
+static int template_gen_set_params(void *genctx, const OSSL_PARAM params[])
+{
+ struct template_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return 0;
+
+ debug_print("empty gen_set params called for %p\n", gctx);
+ return 1;
+}
+
+static void *template_gen_init(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ struct template_gen_ctx *gctx = NULL;
+
+ debug_print("gen init called for %p\n", provctx);
+
+ /* perform algorithm type specific sanity checks */
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((gctx = OPENSSL_zalloc(sizeof(*gctx))) != NULL) {
+ gctx->provctx = provctx;
+ gctx->selection = selection;
+ }
+ if (!template_gen_set_params(gctx, params)) {
+ OPENSSL_free(gctx);
+ gctx = NULL;
+ }
+ debug_print("gen init returns %p\n", gctx);
+ return gctx;
+}
+
+static const OSSL_PARAM *template_gen_settable_params(ossl_unused void *genctx,
+ ossl_unused void *provctx)
+{
+ static OSSL_PARAM settable[] = {
+ OSSL_PARAM_END
+ };
+ return settable;
+}
+
+static void *template_gen(void *vctx, OSSL_CALLBACK *osslcb, void *cbarg)
+{
+ struct template_gen_ctx *gctx = (struct template_gen_ctx *)vctx;
+ void *key = NULL;
+
+ debug_print("gen called for %p\n", gctx);
+
+ if (gctx == NULL)
+ goto err;
+
+ /* generate and return new key */
+
+ debug_print("gen returns set %p\n", key);
+ return key;
+err:
+ template_free(key);
+ debug_print("gen returns NULL\n");
+ return NULL;
+}
+
+static void template_gen_cleanup(void *genctx)
+{
+ struct template_gen_ctx *gctx = genctx;
+
+ if (gctx == NULL)
+ return;
+
+ debug_print("gen cleanup for %p\n", gctx);
+ OPENSSL_free(gctx);
+}
+
+static void *template_dup(const void *vsrckey, int selection)
+{
+ void *dstkey = NULL;
+
+ debug_print("dup called for %p\n", vsrckey);
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstkey = template_new(NULL);
+ if (dstkey == NULL)
+ goto err;
+
+ /* populate dstkey from vsrckey material */
+
+ debug_print("dup returns %p\n", dstkey);
+ return dstkey;
+ err:
+ template_free(dstkey);
+ debug_print("dup returns NULL\n");
+ return NULL;
+}
+
+const OSSL_DISPATCH ossl_template_keymgmt_functions[] = {
+ { OSSL_FUNC_KEYMGMT_NEW, (void (*)(void))template_new },
+ { OSSL_FUNC_KEYMGMT_FREE, (void (*)(void))template_free },
+ { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void (*) (void))template_get_params },
+ { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void (*) (void))template_gettable_params },
+ { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void (*) (void))template_set_params },
+ { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void (*) (void))template_settable_params },
+ { OSSL_FUNC_KEYMGMT_HAS, (void (*)(void))template_has },
+ { OSSL_FUNC_KEYMGMT_MATCH, (void (*)(void))template_match },
+ { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void (*)(void))template_imexport_types },
+ { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))template_imexport_types },
+ { OSSL_FUNC_KEYMGMT_IMPORT, (void (*)(void))template_import },
+ { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))template_export },
+ { OSSL_FUNC_KEYMGMT_GEN_INIT, (void (*)(void))template_gen_init },
+ { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void (*)(void))template_gen_set_params },
+ { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS,
+ (void (*)(void))template_gen_settable_params },
+ { OSSL_FUNC_KEYMGMT_GEN, (void (*)(void))template_gen },
+ { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void (*)(void))template_gen_cleanup },
+ { OSSL_FUNC_KEYMGMT_DUP, (void (*)(void))template_dup },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c b/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c
new file mode 100644
index 000000000000..2ca800fad9bc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/blake2_mac_impl.c
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/proverr.h>
+
+#include "prov/blake2.h"
+#include "internal/cryptlib.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn blake2_mac_new;
+static OSSL_FUNC_mac_dupctx_fn blake2_mac_dup;
+static OSSL_FUNC_mac_freectx_fn blake2_mac_free;
+static OSSL_FUNC_mac_gettable_ctx_params_fn blake2_gettable_ctx_params;
+static OSSL_FUNC_mac_get_ctx_params_fn blake2_get_ctx_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn blake2_mac_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn blake2_mac_set_ctx_params;
+static OSSL_FUNC_mac_init_fn blake2_mac_init;
+static OSSL_FUNC_mac_update_fn blake2_mac_update;
+static OSSL_FUNC_mac_final_fn blake2_mac_final;
+
+struct blake2_mac_data_st {
+ BLAKE2_CTX ctx;
+ BLAKE2_PARAM params;
+ unsigned char key[BLAKE2_KEYBYTES];
+};
+
+static void *blake2_mac_new(void *unused_provctx)
+{
+ struct blake2_mac_data_st *macctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ macctx = OPENSSL_zalloc(sizeof(*macctx));
+ if (macctx != NULL) {
+ BLAKE2_PARAM_INIT(&macctx->params);
+ /* ctx initialization is deferred to BLAKE2b_Init() */
+ }
+ return macctx;
+}
+
+static void *blake2_mac_dup(void *vsrc)
+{
+ struct blake2_mac_data_st *dst;
+ struct blake2_mac_data_st *src = vsrc;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dst = OPENSSL_zalloc(sizeof(*dst));
+ if (dst == NULL)
+ return NULL;
+
+ *dst = *src;
+ return dst;
+}
+
+static void blake2_mac_free(void *vmacctx)
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+
+ if (macctx != NULL) {
+ OPENSSL_cleanse(macctx->key, sizeof(macctx->key));
+ OPENSSL_free(macctx);
+ }
+}
+
+static size_t blake2_mac_size(void *vmacctx)
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+
+ return macctx->params.digest_length;
+}
+
+static int blake2_setkey(struct blake2_mac_data_st *macctx,
+ const unsigned char *key, size_t keylen)
+{
+ if (keylen > BLAKE2_KEYBYTES || keylen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ memcpy(macctx->key, key, keylen);
+ /* Pad with zeroes at the end if required */
+ if (keylen < BLAKE2_KEYBYTES)
+ memset(macctx->key + keylen, 0, BLAKE2_KEYBYTES - keylen);
+ BLAKE2_PARAM_SET_KEY_LENGTH(&macctx->params, (uint8_t)keylen);
+ return 1;
+}
+
+static int blake2_mac_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running() || !blake2_mac_set_ctx_params(macctx, params))
+ return 0;
+ if (key != NULL) {
+ if (!blake2_setkey(macctx, key, keylen))
+ return 0;
+ } else if (macctx->params.key_length == 0) {
+ /* Check key has been set */
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+ return BLAKE2_INIT_KEY(&macctx->ctx, &macctx->params, macctx->key);
+}
+
+static int blake2_mac_update(void *vmacctx,
+ const unsigned char *data, size_t datalen)
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+
+ if (datalen == 0)
+ return 1;
+
+ return BLAKE2_UPDATE(&macctx->ctx, data, datalen);
+}
+
+static int blake2_mac_final(void *vmacctx,
+ unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ *outl = blake2_mac_size(macctx);
+ return BLAKE2_FINAL(out, &macctx->ctx);
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *blake2_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int blake2_get_ctx_params(void *vmacctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, blake2_mac_size(vmacctx)))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, BLAKE2_BLOCKBYTES))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_SALT, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *blake2_mac_settable_ctx_params(
+ ossl_unused void *ctx, ossl_unused void *p_ctx)
+{
+ return known_settable_ctx_params;
+}
+
+/*
+ * ALL parameters should be set before init().
+ */
+static int blake2_mac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[])
+{
+ struct blake2_mac_data_st *macctx = vmacctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) {
+ size_t size;
+
+ if (!OSSL_PARAM_get_size_t(p, &size)
+ || size < 1
+ || size > BLAKE2_OUTBYTES) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_XOF_OR_INVALID_LENGTH);
+ return 0;
+ }
+ BLAKE2_PARAM_SET_DIGEST_LENGTH(&macctx->params, (uint8_t)size);
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL
+ && !blake2_setkey(macctx, p->data, p->data_size))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CUSTOM))
+ != NULL) {
+ /*
+ * The OSSL_PARAM API doesn't provide direct pointer use, so we
+ * must handle the OSSL_PARAM structure ourselves here
+ */
+ if (p->data_size > BLAKE2_PERSONALBYTES) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH);
+ return 0;
+ }
+ BLAKE2_PARAM_SET_PERSONAL(&macctx->params, p->data, p->data_size);
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SALT)) != NULL) {
+ /*
+ * The OSSL_PARAM API doesn't provide direct pointer use, so we
+ * must handle the OSSL_PARAM structure ourselves here as well
+ */
+ if (p->data_size > BLAKE2_SALTBYTES) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+ return 0;
+ }
+ BLAKE2_PARAM_SET_SALT(&macctx->params, p->data, p->data_size);
+ }
+ return 1;
+}
+
+const OSSL_DISPATCH BLAKE2_FUNCTIONS[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))blake2_mac_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))blake2_mac_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))blake2_mac_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))blake2_mac_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))blake2_mac_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))blake2_mac_final },
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,
+ (void (*)(void))blake2_gettable_ctx_params },
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))blake2_get_ctx_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))blake2_mac_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))blake2_mac_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/macs/blake2b_mac.c b/crypto/openssl/providers/implementations/macs/blake2b_mac.c
new file mode 100644
index 000000000000..b445cbd57875
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/blake2b_mac.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Constants */
+#define BLAKE2_CTX BLAKE2B_CTX
+#define BLAKE2_PARAM BLAKE2B_PARAM
+#define BLAKE2_KEYBYTES BLAKE2B_KEYBYTES
+#define BLAKE2_OUTBYTES BLAKE2B_OUTBYTES
+#define BLAKE2_PERSONALBYTES BLAKE2B_PERSONALBYTES
+#define BLAKE2_SALTBYTES BLAKE2B_SALTBYTES
+#define BLAKE2_BLOCKBYTES BLAKE2B_BLOCKBYTES
+
+/* Function names */
+#define BLAKE2_PARAM_INIT ossl_blake2b_param_init
+#define BLAKE2_INIT_KEY ossl_blake2b_init_key
+#define BLAKE2_UPDATE ossl_blake2b_update
+#define BLAKE2_FINAL ossl_blake2b_final
+#define BLAKE2_PARAM_SET_DIGEST_LENGTH ossl_blake2b_param_set_digest_length
+#define BLAKE2_PARAM_SET_KEY_LENGTH ossl_blake2b_param_set_key_length
+#define BLAKE2_PARAM_SET_PERSONAL ossl_blake2b_param_set_personal
+#define BLAKE2_PARAM_SET_SALT ossl_blake2b_param_set_salt
+
+/* OSSL_DISPATCH symbol */
+#define BLAKE2_FUNCTIONS ossl_blake2bmac_functions
+
+#include "blake2_mac_impl.c"
+
diff --git a/crypto/openssl/providers/implementations/macs/blake2s_mac.c b/crypto/openssl/providers/implementations/macs/blake2s_mac.c
new file mode 100644
index 000000000000..6b3fa28bd36b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/blake2s_mac.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* Constants */
+#define BLAKE2_CTX BLAKE2S_CTX
+#define BLAKE2_PARAM BLAKE2S_PARAM
+#define BLAKE2_KEYBYTES BLAKE2S_KEYBYTES
+#define BLAKE2_OUTBYTES BLAKE2S_OUTBYTES
+#define BLAKE2_PERSONALBYTES BLAKE2S_PERSONALBYTES
+#define BLAKE2_SALTBYTES BLAKE2S_SALTBYTES
+#define BLAKE2_BLOCKBYTES BLAKE2S_BLOCKBYTES
+
+/* Function names */
+#define BLAKE2_PARAM_INIT ossl_blake2s_param_init
+#define BLAKE2_INIT_KEY ossl_blake2s_init_key
+#define BLAKE2_UPDATE ossl_blake2s_update
+#define BLAKE2_FINAL ossl_blake2s_final
+#define BLAKE2_PARAM_SET_DIGEST_LENGTH ossl_blake2s_param_set_digest_length
+#define BLAKE2_PARAM_SET_KEY_LENGTH ossl_blake2s_param_set_key_length
+#define BLAKE2_PARAM_SET_PERSONAL ossl_blake2s_param_set_personal
+#define BLAKE2_PARAM_SET_SALT ossl_blake2s_param_set_salt
+
+/* OSSL_DISPATCH symbol */
+#define BLAKE2_FUNCTIONS ossl_blake2smac_functions
+
+#include "blake2_mac_impl.c"
diff --git a/crypto/openssl/providers/implementations/macs/build.info b/crypto/openssl/providers/implementations/macs/build.info
new file mode 100644
index 000000000000..35db66bf23bd
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/build.info
@@ -0,0 +1,30 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$GMAC_GOAL=../../libdefault.a ../../libfips.a
+$HMAC_GOAL=../../libdefault.a ../../libfips.a
+$KMAC_GOAL=../../libdefault.a ../../libfips.a
+$CMAC_GOAL=../../libdefault.a ../../libfips.a
+$BLAKE2_GOAL=../../libdefault.a
+$SIPHASH_GOAL=../../libdefault.a
+$POLY1305_GOAL=../../libdefault.a
+
+SOURCE[$GMAC_GOAL]=gmac_prov.c
+SOURCE[$HMAC_GOAL]=hmac_prov.c
+SOURCE[$KMAC_GOAL]=kmac_prov.c
+
+IF[{- !$disabled{cmac} -}]
+ SOURCE[$CMAC_GOAL]=cmac_prov.c
+ENDIF
+
+IF[{- !$disabled{blake2} -}]
+ SOURCE[$BLAKE2_GOAL]=blake2b_mac.c blake2s_mac.c
+ENDIF
+
+IF[{- !$disabled{siphash} -}]
+ SOURCE[$SIPHASH_GOAL]=siphash_prov.c
+ENDIF
+
+IF[{- !$disabled{poly1305} -}]
+ SOURCE[$POLY1305_GOAL]=poly1305_prov.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/macs/cmac_prov.c b/crypto/openssl/providers/implementations/macs/cmac_prov.c
new file mode 100644
index 000000000000..36acf4d81b8a
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/cmac_prov.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2018-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * CMAC low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/cmac.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+
+#include "prov/securitycheck.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+#include "crypto/cmac.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn cmac_new;
+static OSSL_FUNC_mac_dupctx_fn cmac_dup;
+static OSSL_FUNC_mac_freectx_fn cmac_free;
+static OSSL_FUNC_mac_gettable_ctx_params_fn cmac_gettable_ctx_params;
+static OSSL_FUNC_mac_get_ctx_params_fn cmac_get_ctx_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn cmac_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn cmac_set_ctx_params;
+static OSSL_FUNC_mac_init_fn cmac_init;
+static OSSL_FUNC_mac_update_fn cmac_update;
+static OSSL_FUNC_mac_final_fn cmac_final;
+
+/* local CMAC data */
+
+struct cmac_data_st {
+ void *provctx;
+ CMAC_CTX *ctx;
+ PROV_CIPHER cipher;
+ OSSL_FIPS_IND_DECLARE
+};
+
+static void *cmac_new(void *provctx)
+{
+ struct cmac_data_st *macctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL
+ || (macctx->ctx = CMAC_CTX_new()) == NULL) {
+ OPENSSL_free(macctx);
+ macctx = NULL;
+ } else {
+ macctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(macctx)
+ }
+
+ return macctx;
+}
+
+static void cmac_free(void *vmacctx)
+{
+ struct cmac_data_st *macctx = vmacctx;
+
+ if (macctx != NULL) {
+ CMAC_CTX_free(macctx->ctx);
+ ossl_prov_cipher_reset(&macctx->cipher);
+ OPENSSL_free(macctx);
+ }
+}
+
+static void *cmac_dup(void *vsrc)
+{
+ struct cmac_data_st *src = vsrc;
+ struct cmac_data_st *dst;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dst = cmac_new(src->provctx);
+ if (dst == NULL)
+ return NULL;
+ if (!CMAC_CTX_copy(dst->ctx, src->ctx)
+ || !ossl_prov_cipher_copy(&dst->cipher, &src->cipher)) {
+ cmac_free(dst);
+ return NULL;
+ }
+ OSSL_FIPS_IND_COPY(dst, src)
+ return dst;
+}
+
+static size_t cmac_size(void *vmacctx)
+{
+ struct cmac_data_st *macctx = vmacctx;
+ const EVP_CIPHER_CTX *cipherctx = CMAC_CTX_get0_cipher_ctx(macctx->ctx);
+
+ if (EVP_CIPHER_CTX_get0_cipher(cipherctx) == NULL)
+ return 0;
+
+ return EVP_CIPHER_CTX_get_block_size(cipherctx);
+}
+
+#ifdef FIPS_MODULE
+/*
+ * TDES Encryption is not approved in FIPS 140-3.
+ *
+ * In strict approved mode we just fail here (by returning 0).
+ * If we are going to bypass it using a FIPS indicator then we need to pass that
+ * information down to the cipher also.
+ * This function returns the param to pass down in 'p'.
+ * state will return OSSL_FIPS_IND_STATE_UNKNOWN if the param has not been set.
+ *
+ * The name 'OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK' used below matches the
+ * key name used by the Triple-DES.
+ */
+static int tdes_check_param(struct cmac_data_st *macctx, OSSL_PARAM *p,
+ int *state)
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(macctx->provctx);
+ const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher);
+
+ *state = OSSL_FIPS_IND_STATE_UNKNOWN;
+ if (EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "CMAC", "Triple-DES",
+ ossl_fips_config_tdes_encrypt_disallowed))
+ return 0;
+ OSSL_FIPS_IND_GET_PARAM(macctx, p, state, OSSL_FIPS_IND_SETTABLE0,
+ OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)
+ }
+ return 1;
+}
+#endif
+
+static int cmac_setkey(struct cmac_data_st *macctx,
+ const unsigned char *key, size_t keylen)
+{
+ int rv;
+ OSSL_PARAM *p = NULL;
+#ifdef FIPS_MODULE
+ int state = OSSL_FIPS_IND_STATE_UNKNOWN;
+ OSSL_PARAM prms[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+
+ if (!tdes_check_param(macctx, &prms[0], &state))
+ return 0;
+ if (state != OSSL_FIPS_IND_STATE_UNKNOWN)
+ p = prms;
+#endif
+ rv = ossl_cmac_init(macctx->ctx, key, keylen,
+ ossl_prov_cipher_cipher(&macctx->cipher),
+ ossl_prov_cipher_engine(&macctx->cipher), p);
+ ossl_prov_cipher_reset(&macctx->cipher);
+ return rv;
+}
+
+static int cmac_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct cmac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running() || !cmac_set_ctx_params(macctx, params))
+ return 0;
+ if (key != NULL)
+ return cmac_setkey(macctx, key, keylen);
+ /* Reinitialize the CMAC context */
+ return CMAC_Init(macctx->ctx, NULL, 0, NULL, NULL);
+}
+
+static int cmac_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct cmac_data_st *macctx = vmacctx;
+
+ return CMAC_Update(macctx->ctx, data, datalen);
+}
+
+static int cmac_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ struct cmac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ return CMAC_Final(macctx->ctx, out, outl);
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *cmac_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int cmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, cmac_size(vmacctx)))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, cmac_size(vmacctx)))
+ return 0;
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM((struct cmac_data_st *)vmacctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK)
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *cmac_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+/*
+ * ALL parameters should be set before init().
+ */
+static int cmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[])
+{
+ struct cmac_data_st *macctx = vmacctx;
+ OSSL_LIB_CTX *ctx = PROV_LIBCTX_OF(macctx->provctx);
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(macctx,
+ OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_CIPHER_PARAM_FIPS_ENCRYPT_CHECK))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CIPHER)) != NULL) {
+ if (!ossl_prov_cipher_load_from_params(&macctx->cipher, params, ctx))
+ return 0;
+
+ if (EVP_CIPHER_get_mode(ossl_prov_cipher_cipher(&macctx->cipher))
+ != EVP_CIPH_CBC_MODE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ {
+ const EVP_CIPHER *cipher = ossl_prov_cipher_cipher(&macctx->cipher);
+
+ if (!EVP_CIPHER_is_a(cipher, "AES-256-CBC")
+ && !EVP_CIPHER_is_a(cipher, "AES-192-CBC")
+ && !EVP_CIPHER_is_a(cipher, "AES-128-CBC")
+ && !EVP_CIPHER_is_a(cipher, "DES-EDE3-CBC")) {
+ ERR_raise(ERR_LIB_PROV, EVP_R_UNSUPPORTED_CIPHER);
+ return 0;
+ }
+ }
+#endif
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+ return cmac_setkey(macctx, p->data, p->data_size);
+ }
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_cmac_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))cmac_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))cmac_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))cmac_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))cmac_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))cmac_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))cmac_final },
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,
+ (void (*)(void))cmac_gettable_ctx_params },
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))cmac_get_ctx_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))cmac_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))cmac_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/macs/gmac_prov.c b/crypto/openssl/providers/implementations/macs/gmac_prov.c
new file mode 100644
index 000000000000..ca9382b0a82f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/gmac_prov.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn gmac_new;
+static OSSL_FUNC_mac_dupctx_fn gmac_dup;
+static OSSL_FUNC_mac_freectx_fn gmac_free;
+static OSSL_FUNC_mac_gettable_params_fn gmac_gettable_params;
+static OSSL_FUNC_mac_get_params_fn gmac_get_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn gmac_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn gmac_set_ctx_params;
+static OSSL_FUNC_mac_init_fn gmac_init;
+static OSSL_FUNC_mac_update_fn gmac_update;
+static OSSL_FUNC_mac_final_fn gmac_final;
+
+/* local GMAC pkey structure */
+
+struct gmac_data_st {
+ void *provctx;
+ EVP_CIPHER_CTX *ctx; /* Cipher context */
+ PROV_CIPHER cipher;
+};
+
+static void gmac_free(void *vmacctx)
+{
+ struct gmac_data_st *macctx = vmacctx;
+
+ if (macctx != NULL) {
+ EVP_CIPHER_CTX_free(macctx->ctx);
+ ossl_prov_cipher_reset(&macctx->cipher);
+ OPENSSL_free(macctx);
+ }
+}
+
+static void *gmac_new(void *provctx)
+{
+ struct gmac_data_st *macctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL
+ || (macctx->ctx = EVP_CIPHER_CTX_new()) == NULL) {
+ gmac_free(macctx);
+ return NULL;
+ }
+ macctx->provctx = provctx;
+
+ return macctx;
+}
+
+static void *gmac_dup(void *vsrc)
+{
+ struct gmac_data_st *src = vsrc;
+ struct gmac_data_st *dst;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dst = gmac_new(src->provctx);
+ if (dst == NULL)
+ return NULL;
+
+ if (!EVP_CIPHER_CTX_copy(dst->ctx, src->ctx)
+ || !ossl_prov_cipher_copy(&dst->cipher, &src->cipher)) {
+ gmac_free(dst);
+ return NULL;
+ }
+ return dst;
+}
+
+static size_t gmac_size(void)
+{
+ return EVP_GCM_TLS_TAG_LEN;
+}
+
+static int gmac_setkey(struct gmac_data_st *macctx,
+ const unsigned char *key, size_t keylen)
+{
+ EVP_CIPHER_CTX *ctx = macctx->ctx;
+
+ if (keylen != (size_t)EVP_CIPHER_CTX_get_key_length(ctx)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, NULL))
+ return 0;
+ return 1;
+}
+
+static int gmac_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct gmac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running() || !gmac_set_ctx_params(macctx, params))
+ return 0;
+ if (key != NULL)
+ return gmac_setkey(macctx, key, keylen);
+ return EVP_EncryptInit_ex(macctx->ctx, NULL, NULL, NULL, NULL);
+}
+
+static int gmac_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct gmac_data_st *macctx = vmacctx;
+ EVP_CIPHER_CTX *ctx = macctx->ctx;
+ int outlen;
+
+ if (datalen == 0)
+ return 1;
+
+ while (datalen > INT_MAX) {
+ if (!EVP_EncryptUpdate(ctx, NULL, &outlen, data, INT_MAX))
+ return 0;
+ data += INT_MAX;
+ datalen -= INT_MAX;
+ }
+ return EVP_EncryptUpdate(ctx, NULL, &outlen, data, datalen);
+}
+
+static int gmac_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ struct gmac_data_st *macctx = vmacctx;
+ int hlen = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!EVP_EncryptFinal_ex(macctx->ctx, out, &hlen))
+ return 0;
+
+ hlen = gmac_size();
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_CIPHER_PARAM_AEAD_TAG,
+ out, (size_t)hlen);
+ if (!EVP_CIPHER_CTX_get_params(macctx->ctx, params))
+ return 0;
+
+ *outl = hlen;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *gmac_gettable_params(void *provctx)
+{
+ return known_gettable_params;
+}
+
+static int gmac_get_params(OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, gmac_size());
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_IV, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *gmac_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+/*
+ * ALL parameters should be set before init().
+ */
+static int gmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[])
+{
+ struct gmac_data_st *macctx = vmacctx;
+ EVP_CIPHER_CTX *ctx = macctx->ctx;
+ OSSL_LIB_CTX *provctx = PROV_LIBCTX_OF(macctx->provctx);
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+ if (ctx == NULL)
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CIPHER)) != NULL) {
+ if (!ossl_prov_cipher_load_from_params(&macctx->cipher, params, provctx))
+ return 0;
+ if (EVP_CIPHER_get_mode(ossl_prov_cipher_cipher(&macctx->cipher))
+ != EVP_CIPH_GCM_MODE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MODE);
+ return 0;
+ }
+ if (!EVP_EncryptInit_ex(ctx, ossl_prov_cipher_cipher(&macctx->cipher),
+ ossl_prov_cipher_engine(&macctx->cipher), NULL,
+ NULL))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL)
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !gmac_setkey(macctx, p->data, p->data_size))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_IV)) != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
+ p->data_size, NULL) <= 0
+ || !EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, p->data))
+ return 0;
+ }
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_gmac_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))gmac_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))gmac_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))gmac_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))gmac_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))gmac_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))gmac_final },
+ { OSSL_FUNC_MAC_GETTABLE_PARAMS, (void (*)(void))gmac_gettable_params },
+ { OSSL_FUNC_MAC_GET_PARAMS, (void (*)(void))gmac_get_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))gmac_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))gmac_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/macs/hmac_prov.c b/crypto/openssl/providers/implementations/macs/hmac_prov.c
new file mode 100644
index 000000000000..eb5ecaa300ef
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/hmac_prov.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2018-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * HMAC low level APIs are deprecated for public use, but still ok for internal
+ * use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/proverr.h>
+#include <openssl/err.h>
+
+#include "internal/ssl3_cbc.h"
+
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+#include "prov/securitycheck.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn hmac_new;
+static OSSL_FUNC_mac_dupctx_fn hmac_dup;
+static OSSL_FUNC_mac_freectx_fn hmac_free;
+static OSSL_FUNC_mac_gettable_ctx_params_fn hmac_gettable_ctx_params;
+static OSSL_FUNC_mac_get_ctx_params_fn hmac_get_ctx_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn hmac_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn hmac_set_ctx_params;
+static OSSL_FUNC_mac_init_fn hmac_init;
+static OSSL_FUNC_mac_update_fn hmac_update;
+static OSSL_FUNC_mac_final_fn hmac_final;
+
+/* local HMAC context structure */
+
+/* typedef EVP_MAC_IMPL */
+struct hmac_data_st {
+ void *provctx;
+ HMAC_CTX *ctx; /* HMAC context */
+ PROV_DIGEST digest;
+ unsigned char *key;
+ size_t keylen;
+ /* Length of full TLS record including the MAC and any padding */
+ size_t tls_data_size;
+ unsigned char tls_header[13];
+ int tls_header_set;
+ unsigned char tls_mac_out[EVP_MAX_MD_SIZE];
+ size_t tls_mac_out_size;
+#ifdef FIPS_MODULE
+ /*
+ * 'internal' is set to 1 if HMAC is used inside another algorithm such as a
+ * KDF. In this case it is the parent algorithm that is responsible for
+ * performing any conditional FIPS indicator related checks for the HMAC.
+ */
+ int internal;
+#endif
+ OSSL_FIPS_IND_DECLARE
+};
+
+static void *hmac_new(void *provctx)
+{
+ struct hmac_data_st *macctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((macctx = OPENSSL_zalloc(sizeof(*macctx))) == NULL
+ || (macctx->ctx = HMAC_CTX_new()) == NULL) {
+ OPENSSL_free(macctx);
+ return NULL;
+ }
+ macctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(macctx)
+
+ return macctx;
+}
+
+static void hmac_free(void *vmacctx)
+{
+ struct hmac_data_st *macctx = vmacctx;
+
+ if (macctx != NULL) {
+ HMAC_CTX_free(macctx->ctx);
+ ossl_prov_digest_reset(&macctx->digest);
+ OPENSSL_clear_free(macctx->key, macctx->keylen);
+ OPENSSL_free(macctx);
+ }
+}
+
+static void *hmac_dup(void *vsrc)
+{
+ struct hmac_data_st *src = vsrc;
+ struct hmac_data_st *dst;
+ HMAC_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ dst = hmac_new(src->provctx);
+ if (dst == NULL)
+ return NULL;
+
+ ctx = dst->ctx;
+ *dst = *src;
+ dst->ctx = ctx;
+ dst->key = NULL;
+ memset(&dst->digest, 0, sizeof(dst->digest));
+
+ if (!HMAC_CTX_copy(dst->ctx, src->ctx)
+ || !ossl_prov_digest_copy(&dst->digest, &src->digest)) {
+ hmac_free(dst);
+ return NULL;
+ }
+ if (src->key != NULL) {
+ dst->key = OPENSSL_malloc(src->keylen > 0 ? src->keylen : 1);
+ if (dst->key == NULL) {
+ hmac_free(dst);
+ return 0;
+ }
+ if (src->keylen > 0)
+ memcpy(dst->key, src->key, src->keylen);
+ }
+ return dst;
+}
+
+static size_t hmac_size(struct hmac_data_st *macctx)
+{
+ return HMAC_size(macctx->ctx);
+}
+
+static int hmac_block_size(struct hmac_data_st *macctx)
+{
+ const EVP_MD *md = ossl_prov_digest_md(&macctx->digest);
+
+ if (md == NULL)
+ return 0;
+ return EVP_MD_block_size(md);
+}
+
+static int hmac_setkey(struct hmac_data_st *macctx,
+ const unsigned char *key, size_t keylen)
+{
+ const EVP_MD *digest;
+
+#ifdef FIPS_MODULE
+ /*
+ * KDF's pass a salt rather than a key,
+ * which is why it skips the key check unless "HMAC" is fetched directly.
+ */
+ if (!macctx->internal) {
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(macctx->provctx);
+ int approved = ossl_mac_check_key_size(keylen);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(macctx, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "HMAC", "keysize",
+ ossl_fips_config_hmac_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ }
+#endif
+
+ if (macctx->key != NULL)
+ OPENSSL_clear_free(macctx->key, macctx->keylen);
+ /* Keep a copy of the key in case we need it for TLS HMAC */
+ macctx->key = OPENSSL_malloc(keylen > 0 ? keylen : 1);
+ if (macctx->key == NULL)
+ return 0;
+
+ if (keylen > 0)
+ memcpy(macctx->key, key, keylen);
+ macctx->keylen = keylen;
+
+ digest = ossl_prov_digest_md(&macctx->digest);
+ /* HMAC_Init_ex doesn't tolerate all zero params, so we must be careful */
+ if (key != NULL || (macctx->tls_data_size == 0 && digest != NULL))
+ return HMAC_Init_ex(macctx->ctx, key, keylen, digest,
+ ossl_prov_digest_engine(&macctx->digest));
+ return 1;
+}
+
+static int hmac_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct hmac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running() || !hmac_set_ctx_params(macctx, params))
+ return 0;
+
+ if (key != NULL)
+ return hmac_setkey(macctx, key, keylen);
+
+ /* Just reinit the HMAC context */
+ return HMAC_Init_ex(macctx->ctx, NULL, 0, NULL, NULL);
+}
+
+static int hmac_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct hmac_data_st *macctx = vmacctx;
+
+ if (macctx->tls_data_size > 0) {
+ /* We're doing a TLS HMAC */
+ if (!macctx->tls_header_set) {
+ /* We expect the first update call to contain the TLS header */
+ if (datalen != sizeof(macctx->tls_header))
+ return 0;
+ memcpy(macctx->tls_header, data, datalen);
+ macctx->tls_header_set = 1;
+ return 1;
+ }
+ /* macctx->tls_data_size is datalen plus the padding length */
+ if (macctx->tls_data_size < datalen)
+ return 0;
+
+ return ssl3_cbc_digest_record(ossl_prov_digest_md(&macctx->digest),
+ macctx->tls_mac_out,
+ &macctx->tls_mac_out_size,
+ macctx->tls_header,
+ data,
+ datalen,
+ macctx->tls_data_size,
+ macctx->key,
+ macctx->keylen,
+ 0);
+ }
+
+ return HMAC_Update(macctx->ctx, data, datalen);
+}
+
+static int hmac_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ unsigned int hlen;
+ struct hmac_data_st *macctx = vmacctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (macctx->tls_data_size > 0) {
+ if (macctx->tls_mac_out_size == 0)
+ return 0;
+ if (outl != NULL)
+ *outl = macctx->tls_mac_out_size;
+ memcpy(out, macctx->tls_mac_out, macctx->tls_mac_out_size);
+ return 1;
+ }
+ if (!HMAC_Final(macctx->ctx, out, &hlen))
+ return 0;
+ *outl = hlen;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *hmac_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int hmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[])
+{
+ struct hmac_data_st *macctx = vmacctx;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, hmac_size(macctx)))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL
+ && !OSSL_PARAM_set_int(p, hmac_block_size(macctx)))
+ return 0;
+
+#ifdef FIPS_MODULE
+ p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_FIPS_APPROVED_INDICATOR);
+ if (p != NULL) {
+ int approved = 0;
+
+ if (!macctx->internal)
+ approved = OSSL_FIPS_IND_GET(macctx)->approved;
+ if (!OSSL_PARAM_set_int(p, approved))
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_int(OSSL_MAC_PARAM_DIGEST_NOINIT, NULL),
+ OSSL_PARAM_int(OSSL_MAC_PARAM_DIGEST_ONESHOT, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_TLS_DATA_SIZE, NULL),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *hmac_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+/*
+ * ALL parameters should be set before init().
+ */
+static int hmac_set_ctx_params(void *vmacctx, const OSSL_PARAM params[])
+{
+ struct hmac_data_st *macctx = vmacctx;
+ OSSL_LIB_CTX *ctx = PROV_LIBCTX_OF(macctx->provctx);
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(macctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_MAC_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!ossl_prov_digest_load_from_params(&macctx->digest, params, ctx))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL) {
+ if (p->data_type != OSSL_PARAM_OCTET_STRING)
+ return 0;
+
+ if (!hmac_setkey(macctx, p->data, p->data_size))
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params,
+ OSSL_MAC_PARAM_TLS_DATA_SIZE)) != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &macctx->tls_data_size))
+ return 0;
+ }
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_hmac_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))hmac_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))hmac_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))hmac_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))hmac_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))hmac_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))hmac_final },
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,
+ (void (*)(void))hmac_gettable_ctx_params },
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))hmac_get_ctx_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))hmac_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))hmac_set_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+#ifdef FIPS_MODULE
+static OSSL_FUNC_mac_newctx_fn hmac_internal_new;
+
+static void *hmac_internal_new(void *provctx)
+{
+ struct hmac_data_st *macctx = hmac_new(provctx);
+
+ if (macctx != NULL)
+ macctx->internal = 1;
+ return macctx;
+}
+
+const OSSL_DISPATCH ossl_hmac_internal_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))hmac_internal_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))hmac_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))hmac_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))hmac_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))hmac_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))hmac_final },
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,
+ (void (*)(void))hmac_gettable_ctx_params },
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))hmac_get_ctx_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))hmac_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))hmac_set_ctx_params },
+ OSSL_DISPATCH_END
+};
+
+#endif /* FIPS_MODULE */
diff --git a/crypto/openssl/providers/implementations/macs/kmac_prov.c b/crypto/openssl/providers/implementations/macs/kmac_prov.c
new file mode 100644
index 000000000000..8e583ed8f323
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/kmac_prov.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright 2018-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * See SP800-185 "Appendix A - KMAC, .... in Terms of Keccak[c]"
+ *
+ * Inputs are:
+ * K = Key (len(K) < 2^2040 bits)
+ * X = Input
+ * L = Output length (0 <= L < 2^2040 bits)
+ * S = Customization String Default="" (len(S) < 2^2040 bits)
+ *
+ * KMAC128(K, X, L, S)
+ * {
+ * newX = bytepad(encode_string(K), 168) || X || right_encode(L).
+ * T = bytepad(encode_string("KMAC") || encode_string(S), 168).
+ * return KECCAK[256](T || newX || 00, L).
+ * }
+ *
+ * KMAC256(K, X, L, S)
+ * {
+ * newX = bytepad(encode_string(K), 136) || X || right_encode(L).
+ * T = bytepad(encode_string("KMAC") || encode_string(S), 136).
+ * return KECCAK[512](T || newX || 00, L).
+ * }
+ *
+ * KMAC128XOF(K, X, L, S)
+ * {
+ * newX = bytepad(encode_string(K), 168) || X || right_encode(0).
+ * T = bytepad(encode_string("KMAC") || encode_string(S), 168).
+ * return KECCAK[256](T || newX || 00, L).
+ * }
+ *
+ * KMAC256XOF(K, X, L, S)
+ * {
+ * newX = bytepad(encode_string(K), 136) || X || right_encode(0).
+ * T = bytepad(encode_string("KMAC") || encode_string(S), 136).
+ * return KECCAK[512](T || newX || 00, L).
+ * }
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/fips_names.h>
+#include "prov/securitycheck.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/providercommon.h"
+#include "internal/cryptlib.h" /* ossl_assert */
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn kmac128_new;
+static OSSL_FUNC_mac_newctx_fn kmac256_new;
+static OSSL_FUNC_mac_dupctx_fn kmac_dup;
+static OSSL_FUNC_mac_freectx_fn kmac_free;
+static OSSL_FUNC_mac_gettable_ctx_params_fn kmac_gettable_ctx_params;
+static OSSL_FUNC_mac_get_ctx_params_fn kmac_get_ctx_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn kmac_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn kmac_set_ctx_params;
+static OSSL_FUNC_mac_init_fn kmac_init;
+static OSSL_FUNC_mac_update_fn kmac_update;
+static OSSL_FUNC_mac_final_fn kmac_final;
+
+#define KMAC_MAX_BLOCKSIZE ((1600 - 128 * 2) / 8) /* 168 */
+
+/*
+ * Length encoding will be a 1 byte size + length in bits (3 bytes max)
+ * This gives a range of 0..0XFFFFFF bits = 2097151 bytes).
+ */
+#define KMAC_MAX_OUTPUT_LEN (0xFFFFFF / 8)
+#define KMAC_MAX_ENCODED_HEADER_LEN (1 + 3)
+
+/*
+ * Restrict the maximum length of the customisation string. This must not
+ * exceed 64 bits = 8k bytes.
+ */
+#define KMAC_MAX_CUSTOM 512
+
+/* Maximum size of encoded custom string */
+#define KMAC_MAX_CUSTOM_ENCODED (KMAC_MAX_CUSTOM + KMAC_MAX_ENCODED_HEADER_LEN)
+
+/* Maximum key size in bytes = 512 (4096 bits) */
+#define KMAC_MAX_KEY 512
+#define KMAC_MIN_KEY 4
+
+/*
+ * Maximum Encoded Key size will be padded to a multiple of the blocksize
+ * i.e KMAC_MAX_KEY + KMAC_MAX_ENCODED_HEADER_LEN = 512 + 4
+ * Padded to a multiple of KMAC_MAX_BLOCKSIZE
+ */
+#define KMAC_MAX_KEY_ENCODED (KMAC_MAX_BLOCKSIZE * 4)
+
+/* Fixed value of encode_string("KMAC") */
+static const unsigned char kmac_string[] = {
+ 0x01, 0x20, 0x4B, 0x4D, 0x41, 0x43
+};
+
+#define KMAC_FLAG_XOF_MODE 1
+
+struct kmac_data_st {
+ void *provctx;
+ EVP_MD_CTX *ctx;
+ PROV_DIGEST digest;
+ size_t out_len;
+ size_t key_len;
+ size_t custom_len;
+ /* If xof_mode = 1 then we use right_encode(0) */
+ int xof_mode;
+ /* key and custom are stored in encoded form */
+ unsigned char key[KMAC_MAX_KEY_ENCODED];
+ unsigned char custom[KMAC_MAX_CUSTOM_ENCODED];
+#ifdef FIPS_MODULE
+ /*
+ * 'internal' is set to 1 if KMAC is used inside another algorithm such as a
+ * KDF. In this case it is the parent algorithm that is responsible for
+ * performing any conditional FIPS indicator related checks for KMAC.
+ */
+ int internal;
+#endif
+ OSSL_FIPS_IND_DECLARE
+};
+
+static int encode_string(unsigned char *out, size_t out_max_len, size_t *out_len,
+ const unsigned char *in, size_t in_len);
+static int right_encode(unsigned char *out, size_t out_max_len, size_t *out_len,
+ size_t bits);
+static int bytepad(unsigned char *out, size_t *out_len,
+ const unsigned char *in1, size_t in1_len,
+ const unsigned char *in2, size_t in2_len,
+ size_t w);
+static int kmac_bytepad_encode_key(unsigned char *out, size_t out_max_len,
+ size_t *out_len,
+ const unsigned char *in, size_t in_len,
+ size_t w);
+
+static void kmac_free(void *vmacctx)
+{
+ struct kmac_data_st *kctx = vmacctx;
+
+ if (kctx != NULL) {
+ EVP_MD_CTX_free(kctx->ctx);
+ ossl_prov_digest_reset(&kctx->digest);
+ OPENSSL_cleanse(kctx->key, kctx->key_len);
+ OPENSSL_cleanse(kctx->custom, kctx->custom_len);
+ OPENSSL_free(kctx);
+ }
+}
+
+/*
+ * We have KMAC implemented as a hash, which we can use instead of
+ * reimplementing the EVP functionality with direct use of
+ * keccak_mac_init() and friends.
+ */
+static struct kmac_data_st *kmac_new(void *provctx)
+{
+ struct kmac_data_st *kctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((kctx = OPENSSL_zalloc(sizeof(*kctx))) == NULL
+ || (kctx->ctx = EVP_MD_CTX_new()) == NULL) {
+ kmac_free(kctx);
+ return NULL;
+ }
+ kctx->provctx = provctx;
+ OSSL_FIPS_IND_INIT(kctx)
+ return kctx;
+}
+
+static void *kmac_fetch_new(void *provctx, const OSSL_PARAM *params)
+{
+ struct kmac_data_st *kctx = kmac_new(provctx);
+ int md_size;
+
+ if (kctx == NULL)
+ return 0;
+ if (!ossl_prov_digest_load_from_params(&kctx->digest, params,
+ PROV_LIBCTX_OF(provctx))) {
+ kmac_free(kctx);
+ return 0;
+ }
+
+ md_size = EVP_MD_get_size(ossl_prov_digest_md(&kctx->digest));
+ if (md_size <= 0) {
+ kmac_free(kctx);
+ return 0;
+ }
+ kctx->out_len = (size_t)md_size;
+ return kctx;
+}
+
+static void *kmac128_new(void *provctx)
+{
+ static const OSSL_PARAM kmac128_params[] = {
+ OSSL_PARAM_utf8_string("digest", OSSL_DIGEST_NAME_KECCAK_KMAC128,
+ sizeof(OSSL_DIGEST_NAME_KECCAK_KMAC128)),
+ OSSL_PARAM_END
+ };
+ return kmac_fetch_new(provctx, kmac128_params);
+}
+
+static void *kmac256_new(void *provctx)
+{
+ static const OSSL_PARAM kmac256_params[] = {
+ OSSL_PARAM_utf8_string("digest", OSSL_DIGEST_NAME_KECCAK_KMAC256,
+ sizeof(OSSL_DIGEST_NAME_KECCAK_KMAC256)),
+ OSSL_PARAM_END
+ };
+ return kmac_fetch_new(provctx, kmac256_params);
+}
+
+static void *kmac_dup(void *vsrc)
+{
+ struct kmac_data_st *src = vsrc;
+ struct kmac_data_st *dst;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dst = kmac_new(src->provctx);
+ if (dst == NULL)
+ return NULL;
+
+ if (!EVP_MD_CTX_copy(dst->ctx, src->ctx)
+ || !ossl_prov_digest_copy(&dst->digest, &src->digest)) {
+ kmac_free(dst);
+ return NULL;
+ }
+#ifdef FIPS_MODULE
+ dst->internal = src->internal;
+#endif
+ dst->out_len = src->out_len;
+ dst->key_len = src->key_len;
+ dst->custom_len = src->custom_len;
+ dst->xof_mode = src->xof_mode;
+ memcpy(dst->key, src->key, src->key_len);
+ memcpy(dst->custom, src->custom, dst->custom_len);
+ OSSL_FIPS_IND_COPY(dst, src)
+
+ return dst;
+}
+
+static int kmac_setkey(struct kmac_data_st *kctx, const unsigned char *key,
+ size_t keylen)
+{
+ const EVP_MD *digest = ossl_prov_digest_md(&kctx->digest);
+ int w = EVP_MD_get_block_size(digest);
+
+ if (keylen < KMAC_MIN_KEY || keylen > KMAC_MAX_KEY) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ /*
+ * Only do the key check if KMAC is fetched directly.
+ * Other algorithms that embed KMAC such as SSKDF will ignore this check.
+ */
+ if (!kctx->internal) {
+ int approved = ossl_mac_check_key_size(keylen);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(kctx, OSSL_FIPS_IND_SETTABLE1,
+ PROV_LIBCTX_OF(kctx->provctx),
+ "KMAC", "Key size",
+ ossl_fips_config_kmac_key_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ }
+#endif
+ if (w <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ if (!kmac_bytepad_encode_key(kctx->key, sizeof(kctx->key), &kctx->key_len,
+ key, keylen, (size_t)w))
+ return 0;
+ return 1;
+}
+
+/*
+ * The init() assumes that any ctrl methods are set beforehand for
+ * md, key and custom. Setting the fields afterwards will have no
+ * effect on the output mac.
+ */
+static int kmac_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct kmac_data_st *kctx = vmacctx;
+ EVP_MD_CTX *ctx = kctx->ctx;
+ unsigned char *out;
+ size_t out_len, block_len;
+ int res, t;
+
+ if (!ossl_prov_is_running() || !kmac_set_ctx_params(kctx, params))
+ return 0;
+
+ if (key != NULL) {
+ if (!kmac_setkey(kctx, key, keylen))
+ return 0;
+ } else if (kctx->key_len == 0) {
+ /* Check key has been set */
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+ if (!EVP_DigestInit_ex(kctx->ctx, ossl_prov_digest_md(&kctx->digest),
+ NULL))
+ return 0;
+
+ t = EVP_MD_get_block_size(ossl_prov_digest_md(&kctx->digest));
+ if (t <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ block_len = t;
+
+ /* Set default custom string if it is not already set */
+ if (kctx->custom_len == 0) {
+ const OSSL_PARAM cparams[] = {
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, "", 0),
+ OSSL_PARAM_END
+ };
+ (void)kmac_set_ctx_params(kctx, cparams);
+ }
+
+ if (!bytepad(NULL, &out_len, kmac_string, sizeof(kmac_string),
+ kctx->custom, kctx->custom_len, block_len)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+ out = OPENSSL_malloc(out_len);
+ if (out == NULL)
+ return 0;
+ res = bytepad(out, NULL, kmac_string, sizeof(kmac_string),
+ kctx->custom, kctx->custom_len, block_len)
+ && EVP_DigestUpdate(ctx, out, out_len)
+ && EVP_DigestUpdate(ctx, kctx->key, kctx->key_len);
+ OPENSSL_free(out);
+ return res;
+}
+
+static int kmac_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct kmac_data_st *kctx = vmacctx;
+
+ return EVP_DigestUpdate(kctx->ctx, data, datalen);
+}
+
+static int kmac_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ struct kmac_data_st *kctx = vmacctx;
+ EVP_MD_CTX *ctx = kctx->ctx;
+ size_t lbits, len;
+ unsigned char encoded_outlen[KMAC_MAX_ENCODED_HEADER_LEN];
+ int ok;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ /* KMAC XOF mode sets the encoded length to 0 */
+ lbits = (kctx->xof_mode ? 0 : (kctx->out_len * 8));
+
+ ok = right_encode(encoded_outlen, sizeof(encoded_outlen), &len, lbits)
+ && EVP_DigestUpdate(ctx, encoded_outlen, len)
+ && EVP_DigestFinalXOF(ctx, out, kctx->out_len);
+ *outl = kctx->out_len;
+ return ok;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_BLOCK_SIZE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *kmac_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int kmac_get_ctx_params(void *vmacctx, OSSL_PARAM params[])
+{
+ struct kmac_data_st *kctx = vmacctx;
+ OSSL_PARAM *p;
+ int sz;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, kctx->out_len))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_BLOCK_SIZE)) != NULL) {
+ sz = EVP_MD_block_size(ossl_prov_digest_md(&kctx->digest));
+ if (!OSSL_PARAM_set_int(p, sz))
+ return 0;
+ }
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(kctx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_MAC_PARAM_XOF, NULL),
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_CUSTOM, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_NO_SHORT_MAC)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_MAC_PARAM_FIPS_KEY_CHECK)
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *kmac_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+/*
+ * The following params can be set any time before final():
+ * - "outlen" or "size": The requested output length.
+ * - "xof": If set, this indicates that right_encoded(0)
+ * is part of the digested data, otherwise it
+ * uses right_encoded(requested output length).
+ *
+ * All other params should be set before init().
+ */
+static int kmac_set_ctx_params(void *vmacctx, const OSSL_PARAM *params)
+{
+ struct kmac_data_st *kctx = vmacctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(kctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_MAC_PARAM_FIPS_NO_SHORT_MAC))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(kctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_MAC_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_XOF)) != NULL
+ && !OSSL_PARAM_get_int(p, &kctx->xof_mode))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) {
+ size_t sz = 0;
+
+ if (!OSSL_PARAM_get_size_t(p, &sz))
+ return 0;
+ if (sz > KMAC_MAX_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ /* SP 800-185 8.4.2 mandates a minimum of 32 bits of output */
+ if (sz < 32 / 8) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(kctx, OSSL_FIPS_IND_SETTABLE0,
+ PROV_LIBCTX_OF(kctx->provctx),
+ "KMAC", "length",
+ ossl_fips_config_no_short_mac)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_OUTPUT_LENGTH);
+ return 0;
+ }
+ }
+#endif
+ kctx->out_len = sz;
+ }
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL
+ && !kmac_setkey(kctx, p->data, p->data_size))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_CUSTOM))
+ != NULL) {
+ if (p->data_size > KMAC_MAX_CUSTOM) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CUSTOM_LENGTH);
+ return 0;
+ }
+ if (!encode_string(kctx->custom, sizeof(kctx->custom), &kctx->custom_len,
+ p->data, p->data_size))
+ return 0;
+ }
+ return 1;
+}
+
+/* Encoding/Padding Methods. */
+
+/* Returns the number of bytes required to store 'bits' into a byte array */
+static unsigned int get_encode_size(size_t bits)
+{
+ unsigned int cnt = 0, sz = sizeof(size_t);
+
+ while (bits && (cnt < sz)) {
+ ++cnt;
+ bits >>= 8;
+ }
+ /* If bits is zero 1 byte is required */
+ if (cnt == 0)
+ cnt = 1;
+ return cnt;
+}
+
+/*
+ * Convert an integer into bytes . The number of bytes is appended
+ * to the end of the buffer. Returns an array of bytes 'out' of size
+ * *out_len.
+ *
+ * e.g if bits = 32, out[2] = { 0x20, 0x01 }
+ */
+static int right_encode(unsigned char *out, size_t out_max_len, size_t *out_len,
+ size_t bits)
+{
+ unsigned int len = get_encode_size(bits);
+ int i;
+
+ if (len >= out_max_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE);
+ return 0;
+ }
+
+ /* MSB's are at the start of the bytes array */
+ for (i = len - 1; i >= 0; --i) {
+ out[i] = (unsigned char)(bits & 0xFF);
+ bits >>= 8;
+ }
+ /* Tack the length onto the end */
+ out[len] = (unsigned char)len;
+
+ /* The Returned length includes the tacked on byte */
+ *out_len = len + 1;
+ return 1;
+}
+
+/*
+ * Encodes a string with a left encoded length added. Note that the
+ * in_len is converted to bits (*8).
+ *
+ * e.g- in="KMAC" gives out[6] = { 0x01, 0x20, 0x4B, 0x4D, 0x41, 0x43 }
+ * len bits K M A C
+ */
+static int encode_string(unsigned char *out, size_t out_max_len, size_t *out_len,
+ const unsigned char *in, size_t in_len)
+{
+ if (in == NULL) {
+ *out_len = 0;
+ } else {
+ size_t i, bits, len, sz;
+
+ bits = 8 * in_len;
+ len = get_encode_size(bits);
+ sz = 1 + len + in_len;
+
+ if (sz > out_max_len) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_LENGTH_TOO_LARGE);
+ return 0;
+ }
+
+ out[0] = (unsigned char)len;
+ for (i = len; i > 0; --i) {
+ out[i] = (bits & 0xFF);
+ bits >>= 8;
+ }
+ memcpy(out + len + 1, in, in_len);
+ *out_len = sz;
+ }
+ return 1;
+}
+
+/*
+ * Returns a zero padded encoding of the inputs in1 and an optional
+ * in2 (can be NULL). The padded output must be a multiple of the blocksize 'w'.
+ * The value of w is in bytes (< 256).
+ *
+ * The returned output is:
+ * zero_padded(multiple of w, (left_encode(w) || in1 [|| in2])
+ */
+static int bytepad(unsigned char *out, size_t *out_len,
+ const unsigned char *in1, size_t in1_len,
+ const unsigned char *in2, size_t in2_len, size_t w)
+{
+ int len;
+ unsigned char *p = out;
+ int sz = w;
+
+ if (out == NULL) {
+ if (out_len == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PASSED_NULL_PARAMETER);
+ return 0;
+ }
+ sz = 2 + in1_len + (in2 != NULL ? in2_len : 0);
+ *out_len = (sz + w - 1) / w * w;
+ return 1;
+ }
+
+ if (!ossl_assert(w <= 255))
+ return 0;
+
+ /* Left encoded w */
+ *p++ = 1;
+ *p++ = (unsigned char)w;
+ /* || in1 */
+ memcpy(p, in1, in1_len);
+ p += in1_len;
+ /* [ || in2 ] */
+ if (in2 != NULL && in2_len > 0) {
+ memcpy(p, in2, in2_len);
+ p += in2_len;
+ }
+ /* Figure out the pad size (divisible by w) */
+ len = p - out;
+ sz = (len + w - 1) / w * w;
+ /* zero pad the end of the buffer */
+ if (sz != len)
+ memset(p, 0, sz - len);
+ if (out_len != NULL)
+ *out_len = sz;
+ return 1;
+}
+
+/* Returns out = bytepad(encode_string(in), w) */
+static int kmac_bytepad_encode_key(unsigned char *out, size_t out_max_len,
+ size_t *out_len,
+ const unsigned char *in, size_t in_len,
+ size_t w)
+{
+ unsigned char tmp[KMAC_MAX_KEY + KMAC_MAX_ENCODED_HEADER_LEN];
+ size_t tmp_len;
+
+ if (!encode_string(tmp, sizeof(tmp), &tmp_len, in, in_len))
+ return 0;
+ if (!bytepad(NULL, out_len, tmp, tmp_len, NULL, 0, w))
+ return 0;
+ if (!ossl_assert(*out_len <= out_max_len))
+ return 0;
+ return bytepad(out, NULL, tmp, tmp_len, NULL, 0, w);
+}
+
+#define IMPLEMENT_KMAC_TABLE(size, funcname, newname) \
+const OSSL_DISPATCH ossl_kmac##size##_##funcname[] = \
+{ \
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))kmac##size##_##newname }, \
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))kmac_dup }, \
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))kmac_free }, \
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))kmac_init }, \
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))kmac_update }, \
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))kmac_final }, \
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))kmac_gettable_ctx_params }, \
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))kmac_get_ctx_params }, \
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))kmac_settable_ctx_params }, \
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))kmac_set_ctx_params }, \
+ OSSL_DISPATCH_END \
+}
+
+#define KMAC_TABLE(size) IMPLEMENT_KMAC_TABLE(size, functions, new)
+
+KMAC_TABLE(128);
+KMAC_TABLE(256);
+
+#ifdef FIPS_MODULE
+# define KMAC_INTERNAL_TABLE(size) \
+static OSSL_FUNC_mac_newctx_fn kmac##size##_internal_new; \
+static void *kmac##size##_internal_new(void *provctx) \
+{ \
+ struct kmac_data_st *macctx = kmac##size##_new(provctx); \
+ \
+ if (macctx != NULL) \
+ macctx->internal = 1; \
+ return macctx; \
+} \
+IMPLEMENT_KMAC_TABLE(size, internal_functions, internal_new)
+
+KMAC_INTERNAL_TABLE(128);
+KMAC_INTERNAL_TABLE(256);
+#endif /* FIPS_MODULE */
diff --git a/crypto/openssl/providers/implementations/macs/poly1305_prov.c b/crypto/openssl/providers/implementations/macs/poly1305_prov.c
new file mode 100644
index 000000000000..19974f9289b1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/poly1305_prov.c
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+
+#include "crypto/poly1305.h"
+
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn poly1305_new;
+static OSSL_FUNC_mac_dupctx_fn poly1305_dup;
+static OSSL_FUNC_mac_freectx_fn poly1305_free;
+static OSSL_FUNC_mac_gettable_params_fn poly1305_gettable_params;
+static OSSL_FUNC_mac_get_params_fn poly1305_get_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn poly1305_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn poly1305_set_ctx_params;
+static OSSL_FUNC_mac_init_fn poly1305_init;
+static OSSL_FUNC_mac_update_fn poly1305_update;
+static OSSL_FUNC_mac_final_fn poly1305_final;
+
+struct poly1305_data_st {
+ void *provctx;
+ int updated;
+ POLY1305 poly1305; /* Poly1305 data */
+};
+
+static void *poly1305_new(void *provctx)
+{
+ struct poly1305_data_st *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void poly1305_free(void *vmacctx)
+{
+ OPENSSL_free(vmacctx);
+}
+
+static void *poly1305_dup(void *vsrc)
+{
+ struct poly1305_data_st *src = vsrc;
+ struct poly1305_data_st *dst;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ dst = OPENSSL_malloc(sizeof(*dst));
+ if (dst == NULL)
+ return NULL;
+
+ *dst = *src;
+ return dst;
+}
+
+static size_t poly1305_size(void)
+{
+ return POLY1305_DIGEST_SIZE;
+}
+
+static int poly1305_setkey(struct poly1305_data_st *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ if (keylen != POLY1305_KEY_SIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ Poly1305_Init(&ctx->poly1305, key);
+ ctx->updated = 0;
+ return 1;
+}
+
+static int poly1305_init(void *vmacctx, const unsigned char *key,
+ size_t keylen, const OSSL_PARAM params[])
+{
+ struct poly1305_data_st *ctx = vmacctx;
+
+ /* initialize the context in MAC_ctrl function */
+ if (!ossl_prov_is_running() || !poly1305_set_ctx_params(ctx, params))
+ return 0;
+ if (key != NULL)
+ return poly1305_setkey(ctx, key, keylen);
+ /* no reinitialization of context with the same key is allowed */
+ return ctx->updated == 0;
+}
+
+static int poly1305_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct poly1305_data_st *ctx = vmacctx;
+
+ ctx->updated = 1;
+ if (datalen == 0)
+ return 1;
+
+ /* poly1305 has nothing to return in its update function */
+ Poly1305_Update(&ctx->poly1305, data, datalen);
+ return 1;
+}
+
+static int poly1305_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ struct poly1305_data_st *ctx = vmacctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ ctx->updated = 1;
+ Poly1305_Final(&ctx->poly1305, out);
+ *outl = poly1305_size();
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *poly1305_gettable_params(void *provctx)
+{
+ return known_gettable_params;
+}
+
+static int poly1305_get_params(OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL)
+ return OSSL_PARAM_set_size_t(p, poly1305_size());
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_END
+};
+static const OSSL_PARAM *poly1305_settable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+static int poly1305_set_ctx_params(void *vmacctx, const OSSL_PARAM *params)
+{
+ struct poly1305_data_st *ctx = vmacctx;
+ const OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL
+ && !poly1305_setkey(ctx, p->data, p->data_size))
+ return 0;
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_poly1305_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))poly1305_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))poly1305_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))poly1305_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))poly1305_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))poly1305_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))poly1305_final },
+ { OSSL_FUNC_MAC_GETTABLE_PARAMS, (void (*)(void))poly1305_gettable_params },
+ { OSSL_FUNC_MAC_GET_PARAMS, (void (*)(void))poly1305_get_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))poly1305_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))poly1305_set_ctx_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/macs/siphash_prov.c b/crypto/openssl/providers/implementations/macs/siphash_prov.c
new file mode 100644
index 000000000000..08a393c39c69
--- /dev/null
+++ b/crypto/openssl/providers/implementations/macs/siphash_prov.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2018-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+
+#include "crypto/siphash.h"
+
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+
+/*
+ * Forward declaration of everything implemented here. This is not strictly
+ * necessary for the compiler, but provides an assurance that the signatures
+ * of the functions in the dispatch table are correct.
+ */
+static OSSL_FUNC_mac_newctx_fn siphash_new;
+static OSSL_FUNC_mac_dupctx_fn siphash_dup;
+static OSSL_FUNC_mac_freectx_fn siphash_free;
+static OSSL_FUNC_mac_gettable_ctx_params_fn siphash_gettable_ctx_params;
+static OSSL_FUNC_mac_get_ctx_params_fn siphash_get_ctx_params;
+static OSSL_FUNC_mac_settable_ctx_params_fn siphash_settable_ctx_params;
+static OSSL_FUNC_mac_set_ctx_params_fn siphash_set_params;
+static OSSL_FUNC_mac_init_fn siphash_init;
+static OSSL_FUNC_mac_update_fn siphash_update;
+static OSSL_FUNC_mac_final_fn siphash_final;
+
+struct siphash_data_st {
+ void *provctx;
+ SIPHASH siphash; /* Siphash data */
+ SIPHASH sipcopy; /* Siphash data copy for reinitialization */
+ unsigned int crounds, drounds;
+};
+
+static unsigned int crounds(struct siphash_data_st *ctx)
+{
+ return ctx->crounds != 0 ? ctx->crounds : SIPHASH_C_ROUNDS;
+}
+
+static unsigned int drounds(struct siphash_data_st *ctx)
+{
+ return ctx->drounds != 0 ? ctx->drounds : SIPHASH_D_ROUNDS;
+}
+
+static void *siphash_new(void *provctx)
+{
+ struct siphash_data_st *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void siphash_free(void *vmacctx)
+{
+ OPENSSL_free(vmacctx);
+}
+
+static void *siphash_dup(void *vsrc)
+{
+ struct siphash_data_st *ssrc = vsrc;
+ struct siphash_data_st *sdst;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+ sdst = OPENSSL_malloc(sizeof(*sdst));
+ if (sdst == NULL)
+ return NULL;
+
+ *sdst = *ssrc;
+ return sdst;
+}
+
+static size_t siphash_size(void *vmacctx)
+{
+ struct siphash_data_st *ctx = vmacctx;
+
+ return SipHash_hash_size(&ctx->siphash);
+}
+
+static int siphash_setkey(struct siphash_data_st *ctx,
+ const unsigned char *key, size_t keylen)
+{
+ int ret;
+
+ if (keylen != SIPHASH_KEY_SIZE)
+ return 0;
+ ret = SipHash_Init(&ctx->siphash, key, crounds(ctx), drounds(ctx));
+ if (ret)
+ ctx->sipcopy = ctx->siphash;
+ return ret;
+}
+
+static int siphash_init(void *vmacctx, const unsigned char *key, size_t keylen,
+ const OSSL_PARAM params[])
+{
+ struct siphash_data_st *ctx = vmacctx;
+
+ if (!ossl_prov_is_running() || !siphash_set_params(ctx, params))
+ return 0;
+ /*
+ * Without a key, there is not much to do here,
+ * The actual initialization happens through controls.
+ */
+ if (key == NULL) {
+ ctx->siphash = ctx->sipcopy;
+ return 1;
+ }
+ return siphash_setkey(ctx, key, keylen);
+}
+
+static int siphash_update(void *vmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ struct siphash_data_st *ctx = vmacctx;
+
+ if (datalen == 0)
+ return 1;
+
+ SipHash_Update(&ctx->siphash, data, datalen);
+ return 1;
+}
+
+static int siphash_final(void *vmacctx, unsigned char *out, size_t *outl,
+ size_t outsize)
+{
+ struct siphash_data_st *ctx = vmacctx;
+ size_t hlen = siphash_size(ctx);
+
+ if (!ossl_prov_is_running() || outsize < hlen)
+ return 0;
+
+ *outl = hlen;
+ return SipHash_Final(&ctx->siphash, out, hlen);
+}
+
+static const OSSL_PARAM *siphash_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_uint(OSSL_MAC_PARAM_C_ROUNDS, NULL),
+ OSSL_PARAM_uint(OSSL_MAC_PARAM_D_ROUNDS, NULL),
+ OSSL_PARAM_END
+ };
+
+ return known_gettable_ctx_params;
+}
+
+static int siphash_get_ctx_params(void *vmacctx, OSSL_PARAM params[])
+{
+ struct siphash_data_st *ctx = vmacctx;
+ OSSL_PARAM *p;
+
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_SIZE)) != NULL
+ && !OSSL_PARAM_set_size_t(p, siphash_size(vmacctx)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_C_ROUNDS)) != NULL
+ && !OSSL_PARAM_set_uint(p, crounds(ctx)))
+ return 0;
+ if ((p = OSSL_PARAM_locate(params, OSSL_MAC_PARAM_D_ROUNDS)) != NULL
+ && !OSSL_PARAM_set_uint(p, drounds(ctx)))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *siphash_settable_ctx_params(ossl_unused void *ctx,
+ void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_MAC_PARAM_SIZE, NULL),
+ OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, NULL, 0),
+ OSSL_PARAM_uint(OSSL_MAC_PARAM_C_ROUNDS, NULL),
+ OSSL_PARAM_uint(OSSL_MAC_PARAM_D_ROUNDS, NULL),
+ OSSL_PARAM_END
+ };
+
+ return known_settable_ctx_params;
+}
+
+static int siphash_set_params(void *vmacctx, const OSSL_PARAM *params)
+{
+ struct siphash_data_st *ctx = vmacctx;
+ const OSSL_PARAM *p = NULL;
+ size_t size;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_SIZE)) != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &size)
+ || !SipHash_set_hash_size(&ctx->siphash, size)
+ || !SipHash_set_hash_size(&ctx->sipcopy, size))
+ return 0;
+ }
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_C_ROUNDS)) != NULL
+ && !OSSL_PARAM_get_uint(p, &ctx->crounds))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_D_ROUNDS)) != NULL
+ && !OSSL_PARAM_get_uint(p, &ctx->drounds))
+ return 0;
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_MAC_PARAM_KEY)) != NULL)
+ if (p->data_type != OSSL_PARAM_OCTET_STRING
+ || !siphash_setkey(ctx, p->data, p->data_size))
+ return 0;
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_siphash_functions[] = {
+ { OSSL_FUNC_MAC_NEWCTX, (void (*)(void))siphash_new },
+ { OSSL_FUNC_MAC_DUPCTX, (void (*)(void))siphash_dup },
+ { OSSL_FUNC_MAC_FREECTX, (void (*)(void))siphash_free },
+ { OSSL_FUNC_MAC_INIT, (void (*)(void))siphash_init },
+ { OSSL_FUNC_MAC_UPDATE, (void (*)(void))siphash_update },
+ { OSSL_FUNC_MAC_FINAL, (void (*)(void))siphash_final },
+ { OSSL_FUNC_MAC_GETTABLE_CTX_PARAMS,
+ (void (*)(void))siphash_gettable_ctx_params },
+ { OSSL_FUNC_MAC_GET_CTX_PARAMS, (void (*)(void))siphash_get_ctx_params },
+ { OSSL_FUNC_MAC_SETTABLE_CTX_PARAMS,
+ (void (*)(void))siphash_settable_ctx_params },
+ { OSSL_FUNC_MAC_SET_CTX_PARAMS, (void (*)(void))siphash_set_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/build.info b/crypto/openssl/providers/implementations/rands/build.info
new file mode 100644
index 000000000000..98230648e354
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/build.info
@@ -0,0 +1,11 @@
+SUBDIRS=seeding
+
+$RANDS_GOAL=../../libdefault.a ../../libfips.a
+
+SOURCE[$RANDS_GOAL]=drbg.c test_rng.c drbg_ctr.c drbg_hash.c drbg_hmac.c
+SOURCE[../../libdefault.a]=seed_src.c seed_src_jitter.c
+SOURCE[../../libfips.a]=fips_crng_test.c
+
+IF[{- !$disabled{'fips-jitter'} -}]
+ SOURCE[../../libfips.a]=seed_src_jitter.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/rands/drbg.c b/crypto/openssl/providers/implementations/rands/drbg.c
new file mode 100644
index 000000000000..4925a3b400ed
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/drbg.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright 2011-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/evp.h>
+#include "crypto/rand.h"
+#include <openssl/proverr.h>
+#include "drbg_local.h"
+#include "internal/thread_once.h"
+#include "crypto/cryptlib.h"
+#include "prov/seeding.h"
+#include "crypto/rand_pool.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "crypto/context.h"
+
+/*
+ * Support framework for NIST SP 800-90A DRBG
+ *
+ * See manual page PROV_DRBG(7) for a general overview.
+ *
+ * The OpenSSL model is to have new and free functions, and that new
+ * does all initialization. That is not the NIST model, which has
+ * instantiation and un-instantiate, and reuse within a new/free
+ * lifecycle. (No doubt this comes from the desire to support hardware
+ * DRBG, where allocation of resources on something like an HSM is
+ * a much bigger deal than just re-setting an allocated resource.)
+ */
+
+/* NIST SP 800-90A DRBG recommends the use of a personalization string. */
+static const char ossl_pers_string[] = DRBG_DEFAULT_PERS_STRING;
+
+static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch,
+ int function);
+
+static int rand_drbg_restart(PROV_DRBG *drbg);
+
+/*
+ * We interpret a call to this function as a hint only and ignore it. This
+ * occurs when the EVP layer thinks we should do some locking. In practice
+ * however we manage for ourselves when we take a lock or not on the basis
+ * of whether drbg->lock is present or not.
+ */
+int ossl_drbg_lock(void *vctx)
+{
+ return 1;
+}
+
+/* Interpreted as a hint only and ignored as for ossl_drbg_lock() */
+void ossl_drbg_unlock(void *vctx)
+{
+}
+
+static int ossl_drbg_lock_parent(PROV_DRBG *drbg)
+{
+ void *parent = drbg->parent;
+
+ if (parent != NULL
+ && drbg->parent_lock != NULL
+ && !drbg->parent_lock(parent)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED);
+ return 0;
+ }
+ return 1;
+}
+
+static void ossl_drbg_unlock_parent(PROV_DRBG *drbg)
+{
+ void *parent = drbg->parent;
+
+ if (parent != NULL && drbg->parent_unlock != NULL)
+ drbg->parent_unlock(parent);
+}
+
+static int get_parent_strength(PROV_DRBG *drbg, unsigned int *str)
+{
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ void *parent = drbg->parent;
+ int res;
+
+ if (drbg->parent_get_ctx_params == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH);
+ return 0;
+ }
+
+ *params = OSSL_PARAM_construct_uint(OSSL_RAND_PARAM_STRENGTH, str);
+ if (!ossl_drbg_lock_parent(drbg)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT);
+ return 0;
+ }
+ res = drbg->parent_get_ctx_params(parent, params);
+ ossl_drbg_unlock_parent(drbg);
+ if (!res) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_GET_PARENT_STRENGTH);
+ return 0;
+ }
+ return 1;
+}
+
+static unsigned int get_parent_reseed_count(PROV_DRBG *drbg)
+{
+ OSSL_PARAM params[2] = { OSSL_PARAM_END, OSSL_PARAM_END };
+ void *parent = drbg->parent;
+ unsigned int r = 0;
+
+ *params = OSSL_PARAM_construct_uint(OSSL_DRBG_PARAM_RESEED_COUNTER, &r);
+ if (!ossl_drbg_lock_parent(drbg)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_LOCK_PARENT);
+ goto err;
+ }
+ if (!drbg->parent_get_ctx_params(parent, params))
+ r = 0;
+ ossl_drbg_unlock_parent(drbg);
+ return r;
+
+ err:
+ r = tsan_load(&drbg->reseed_counter) - 2;
+ if (r == 0)
+ r = UINT_MAX;
+ return r;
+}
+
+/*
+ * Implements the get_entropy() callback
+ *
+ * If the DRBG has a parent, then the required amount of entropy input
+ * is fetched using the parent's ossl_prov_drbg_generate().
+ *
+ * Otherwise, the entropy is polled from the system entropy sources
+ * using ossl_pool_acquire_entropy().
+ *
+ * If a random pool has been added to the DRBG using RAND_add(), then
+ * its entropy will be used up first.
+ */
+size_t ossl_drbg_get_seed(void *vdrbg, unsigned char **pout,
+ int entropy, size_t min_len,
+ size_t max_len, int prediction_resistance,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ size_t bytes_needed;
+ unsigned char *buffer;
+
+ /* Figure out how many bytes we need */
+ bytes_needed = entropy >= 0 ? (entropy + 7) / 8 : 0;
+ if (bytes_needed < min_len)
+ bytes_needed = min_len;
+ if (bytes_needed > max_len)
+ bytes_needed = max_len;
+
+ /* Allocate storage */
+ buffer = OPENSSL_secure_malloc(bytes_needed);
+ if (buffer == NULL)
+ return 0;
+
+ /*
+ * Get random data. Include our DRBG address as
+ * additional input, in order to provide a distinction between
+ * different DRBG child instances.
+ *
+ * Note: using the sizeof() operator on a pointer triggers
+ * a warning in some static code analyzers, but it's
+ * intentional and correct here.
+ */
+ if (!ossl_prov_drbg_generate(drbg, buffer, bytes_needed,
+ drbg->strength, prediction_resistance,
+ (unsigned char *)&drbg, sizeof(drbg))) {
+ OPENSSL_secure_clear_free(buffer, bytes_needed);
+ ERR_raise(ERR_LIB_PROV, PROV_R_GENERATE_ERROR);
+ return 0;
+ }
+ *pout = buffer;
+ return bytes_needed;
+}
+
+/* Implements the cleanup_entropy() callback */
+void ossl_drbg_clear_seed(ossl_unused void *vdrbg,
+ unsigned char *out, size_t outlen)
+{
+ OPENSSL_secure_clear_free(out, outlen);
+}
+
+static size_t get_entropy(PROV_DRBG *drbg, unsigned char **pout, int entropy,
+ size_t min_len, size_t max_len,
+ int prediction_resistance)
+{
+ size_t bytes;
+ unsigned int p_str;
+
+ if (drbg->parent == NULL)
+ /*
+ * In normal use (i.e. OpenSSL's own uses), this is never called.
+ * This remains purely for legacy reasons.
+ */
+ return ossl_prov_get_entropy(drbg->provctx, pout, entropy, min_len,
+ max_len);
+
+ if (drbg->parent_get_seed == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_CANNOT_SUPPLY_ENTROPY_SEED);
+ return 0;
+ }
+ if (!get_parent_strength(drbg, &p_str))
+ return 0;
+ if (drbg->strength > p_str) {
+ /*
+ * We currently don't support the algorithm from NIST SP 800-90C
+ * 10.1.2 to use a weaker DRBG as source
+ */
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK);
+ return 0;
+ }
+
+ /*
+ * Our lock is already held, but we need to lock our parent before
+ * generating bits from it. Note: taking the lock will be a no-op
+ * if locking is not required (while drbg->parent->lock == NULL).
+ */
+ if (!ossl_drbg_lock_parent(drbg))
+ return 0;
+ /*
+ * Get random data from parent. Include our DRBG address as
+ * additional input, in order to provide a distinction between
+ * different DRBG child instances.
+ *
+ * Note: using the sizeof() operator on a pointer triggers
+ * a warning in some static code analyzers, but it's
+ * intentional and correct here.
+ */
+ bytes = drbg->parent_get_seed(drbg->parent, pout,
+ entropy > 0 ? entropy : (int) drbg->strength,
+ min_len, max_len, prediction_resistance,
+ (unsigned char *)&drbg, sizeof(drbg));
+ ossl_drbg_unlock_parent(drbg);
+ return bytes;
+}
+
+static void cleanup_entropy(PROV_DRBG *drbg, unsigned char *out, size_t outlen)
+{
+ if (drbg->parent == NULL) {
+ ossl_prov_cleanup_entropy(drbg->provctx, out, outlen);
+ } else if (drbg->parent_clear_seed != NULL) {
+ if (!ossl_drbg_lock_parent(drbg))
+ return;
+ drbg->parent_clear_seed(drbg->parent, out, outlen);
+ ossl_drbg_unlock_parent(drbg);
+ }
+}
+
+#ifndef PROV_RAND_GET_RANDOM_NONCE
+typedef struct prov_drbg_nonce_global_st {
+ CRYPTO_RWLOCK *rand_nonce_lock;
+ int rand_nonce_count;
+} PROV_DRBG_NONCE_GLOBAL;
+
+/*
+ * drbg_ossl_ctx_new() calls drgb_setup() which calls rand_drbg_get_nonce()
+ * which needs to get the rand_nonce_lock out of the OSSL_LIB_CTX...but since
+ * drbg_ossl_ctx_new() hasn't finished running yet we need the rand_nonce_lock
+ * to be in a different global data object. Otherwise we will go into an
+ * infinite recursion loop.
+ */
+void *ossl_prov_drbg_nonce_ctx_new(OSSL_LIB_CTX *libctx)
+{
+ PROV_DRBG_NONCE_GLOBAL *dngbl = OPENSSL_zalloc(sizeof(*dngbl));
+
+ if (dngbl == NULL)
+ return NULL;
+
+ dngbl->rand_nonce_lock = CRYPTO_THREAD_lock_new();
+ if (dngbl->rand_nonce_lock == NULL) {
+ OPENSSL_free(dngbl);
+ return NULL;
+ }
+
+ return dngbl;
+}
+
+void ossl_prov_drbg_nonce_ctx_free(void *vdngbl)
+{
+ PROV_DRBG_NONCE_GLOBAL *dngbl = vdngbl;
+
+ if (dngbl == NULL)
+ return;
+
+ CRYPTO_THREAD_lock_free(dngbl->rand_nonce_lock);
+
+ OPENSSL_free(dngbl);
+}
+
+/* Get a nonce from the operating system */
+static size_t prov_drbg_get_nonce(PROV_DRBG *drbg, unsigned char **pout,
+ size_t min_len, size_t max_len)
+{
+ size_t ret = 0, n;
+ unsigned char *buf = NULL;
+ OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(drbg->provctx);
+ PROV_DRBG_NONCE_GLOBAL *dngbl
+ = ossl_lib_ctx_get_data(libctx, OSSL_LIB_CTX_DRBG_NONCE_INDEX);
+ struct {
+ void *drbg;
+ int count;
+ } data;
+
+ if (dngbl == NULL)
+ return 0;
+
+ if (drbg->parent != NULL && drbg->parent_nonce != NULL) {
+ n = drbg->parent_nonce(drbg->parent, NULL, 0, drbg->min_noncelen,
+ drbg->max_noncelen);
+ if (n > 0 && (buf = OPENSSL_malloc(n)) != NULL) {
+ ret = drbg->parent_nonce(drbg->parent, buf, 0,
+ drbg->min_noncelen, drbg->max_noncelen);
+ if (ret == n) {
+ *pout = buf;
+ return ret;
+ }
+ OPENSSL_free(buf);
+ }
+ }
+
+ /* Use the built in nonce source plus some of our specifics */
+ memset(&data, 0, sizeof(data));
+ data.drbg = drbg;
+ if (!CRYPTO_atomic_add(&dngbl->rand_nonce_count, 1, &data.count,
+ dngbl->rand_nonce_lock))
+ return 0;
+ return ossl_prov_get_nonce(drbg->provctx, pout, min_len, max_len,
+ &data, sizeof(data));
+}
+#endif /* PROV_RAND_GET_RANDOM_NONCE */
+
+/*
+ * Instantiate |drbg|, after it has been initialized. Use |pers| and
+ * |perslen| as prediction-resistance input.
+ *
+ * Requires that drbg->lock is already locked for write, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int ossl_prov_drbg_instantiate(PROV_DRBG *drbg, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pers, size_t perslen)
+{
+ unsigned char *nonce = NULL, *entropy = NULL;
+ size_t noncelen = 0, entropylen = 0;
+ size_t min_entropy, min_entropylen, max_entropylen;
+
+ if (strength > drbg->strength) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH);
+ goto end;
+ }
+ min_entropy = drbg->strength;
+ min_entropylen = drbg->min_entropylen;
+ max_entropylen = drbg->max_entropylen;
+
+ if (pers == NULL) {
+ pers = (const unsigned char *)ossl_pers_string;
+ perslen = sizeof(ossl_pers_string);
+ }
+ if (perslen > drbg->max_perslen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PERSONALISATION_STRING_TOO_LONG);
+ goto end;
+ }
+
+ if (drbg->state != EVP_RAND_STATE_UNINITIALISED) {
+ if (drbg->state == EVP_RAND_STATE_ERROR)
+ ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE);
+ else
+ ERR_raise(ERR_LIB_PROV, PROV_R_ALREADY_INSTANTIATED);
+ goto end;
+ }
+
+ drbg->state = EVP_RAND_STATE_ERROR;
+
+ if (drbg->min_noncelen > 0) {
+ if (drbg->parent_nonce != NULL) {
+ noncelen = drbg->parent_nonce(drbg->parent, NULL, drbg->strength,
+ drbg->min_noncelen,
+ drbg->max_noncelen);
+ if (noncelen == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE);
+ goto end;
+ }
+ nonce = OPENSSL_malloc(noncelen);
+ if (nonce == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE);
+ goto end;
+ }
+ if (noncelen != drbg->parent_nonce(drbg->parent, nonce,
+ drbg->strength,
+ drbg->min_noncelen,
+ drbg->max_noncelen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE);
+ goto end;
+ }
+#ifndef PROV_RAND_GET_RANDOM_NONCE
+ } else if (drbg->parent != NULL) {
+#endif
+ /*
+ * NIST SP800-90Ar1 section 9.1 says you can combine getting
+ * the entropy and nonce in 1 call by increasing the entropy
+ * with 50% and increasing the minimum length to accommodate
+ * the length of the nonce. We do this in case a nonce is
+ * required and there is no parental nonce capability.
+ */
+ min_entropy += drbg->strength / 2;
+ min_entropylen += drbg->min_noncelen;
+ max_entropylen += drbg->max_noncelen;
+ }
+#ifndef PROV_RAND_GET_RANDOM_NONCE
+ else { /* parent == NULL */
+ noncelen = prov_drbg_get_nonce(drbg, &nonce, drbg->min_noncelen,
+ drbg->max_noncelen);
+ if (noncelen < drbg->min_noncelen
+ || noncelen > drbg->max_noncelen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_NONCE);
+ goto end;
+ }
+ }
+#endif
+ }
+
+ drbg->reseed_next_counter = tsan_load(&drbg->reseed_counter);
+ if (drbg->reseed_next_counter) {
+ drbg->reseed_next_counter++;
+ if (!drbg->reseed_next_counter)
+ drbg->reseed_next_counter = 1;
+ }
+
+ entropylen = get_entropy(drbg, &entropy, min_entropy,
+ min_entropylen, max_entropylen,
+ prediction_resistance);
+ if (entropylen < min_entropylen
+ || entropylen > max_entropylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY);
+ goto end;
+ }
+
+ if (!drbg->instantiate(drbg, entropy, entropylen, nonce, noncelen,
+ pers, perslen)) {
+ cleanup_entropy(drbg, entropy, entropylen);
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_INSTANTIATING_DRBG);
+ goto end;
+ }
+ cleanup_entropy(drbg, entropy, entropylen);
+
+ drbg->state = EVP_RAND_STATE_READY;
+ drbg->generate_counter = 1;
+ drbg->reseed_time = time(NULL);
+ tsan_store(&drbg->reseed_counter, drbg->reseed_next_counter);
+
+ end:
+ if (nonce != NULL)
+ ossl_prov_cleanup_nonce(drbg->provctx, nonce, noncelen);
+ if (drbg->state == EVP_RAND_STATE_READY)
+ return 1;
+ return 0;
+}
+
+/*
+ * Uninstantiate |drbg|. Must be instantiated before it can be used.
+ *
+ * Requires that drbg->lock is already locked for write, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int ossl_prov_drbg_uninstantiate(PROV_DRBG *drbg)
+{
+ drbg->state = EVP_RAND_STATE_UNINITIALISED;
+ return 1;
+}
+
+static int ossl_prov_drbg_reseed_unlocked(PROV_DRBG *drbg,
+ int prediction_resistance,
+ const unsigned char *ent,
+ size_t ent_len,
+ const unsigned char *adin,
+ size_t adinlen)
+{
+ unsigned char *entropy = NULL;
+ size_t entropylen = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (drbg->state != EVP_RAND_STATE_READY) {
+ /* try to recover from previous errors */
+ rand_drbg_restart(drbg);
+
+ if (drbg->state == EVP_RAND_STATE_ERROR) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE);
+ return 0;
+ }
+ if (drbg->state == EVP_RAND_STATE_UNINITIALISED) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED);
+ return 0;
+ }
+ }
+
+ if (ent != NULL) {
+ if (ent_len < drbg->min_entropylen) {
+ ERR_raise(ERR_LIB_RAND, RAND_R_ENTROPY_OUT_OF_RANGE);
+ drbg->state = EVP_RAND_STATE_ERROR;
+ return 0;
+ }
+ if (ent_len > drbg->max_entropylen) {
+ ERR_raise(ERR_LIB_RAND, RAND_R_ENTROPY_INPUT_TOO_LONG);
+ drbg->state = EVP_RAND_STATE_ERROR;
+ return 0;
+ }
+ }
+
+ if (adin == NULL) {
+ adinlen = 0;
+ } else if (adinlen > drbg->max_adinlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG);
+ return 0;
+ }
+
+ drbg->state = EVP_RAND_STATE_ERROR;
+
+ drbg->reseed_next_counter = tsan_load(&drbg->reseed_counter);
+ if (drbg->reseed_next_counter) {
+ drbg->reseed_next_counter++;
+ if (!drbg->reseed_next_counter)
+ drbg->reseed_next_counter = 1;
+ }
+
+ if (ent != NULL) {
+#ifdef FIPS_MODULE
+ /*
+ * NIST SP-800-90A mandates that entropy *shall not* be provided
+ * by the consuming application. Instead the data is added as additional
+ * input.
+ *
+ * (NIST SP-800-90Ar1, Sections 9.1 and 9.2)
+ */
+ if (!drbg->reseed(drbg, NULL, 0, ent, ent_len)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED);
+ return 0;
+ }
+#else
+ if (!drbg->reseed(drbg, ent, ent_len, adin, adinlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_RESEED);
+ return 0;
+ }
+ /* There isn't much point adding the same additional input twice */
+ adin = NULL;
+ adinlen = 0;
+#endif
+ }
+
+ /* Reseed using our sources in addition */
+ entropylen = get_entropy(drbg, &entropy, drbg->strength,
+ drbg->min_entropylen, drbg->max_entropylen,
+ prediction_resistance);
+ if (entropylen < drbg->min_entropylen
+ || entropylen > drbg->max_entropylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ERROR_RETRIEVING_ENTROPY);
+ goto end;
+ }
+
+ if (!drbg->reseed(drbg, entropy, entropylen, adin, adinlen))
+ goto end;
+
+ drbg->state = EVP_RAND_STATE_READY;
+ drbg->generate_counter = 1;
+ drbg->reseed_time = time(NULL);
+ tsan_store(&drbg->reseed_counter, drbg->reseed_next_counter);
+ if (drbg->parent != NULL)
+ drbg->parent_reseed_counter = get_parent_reseed_count(drbg);
+
+ end:
+ cleanup_entropy(drbg, entropy, entropylen);
+ if (drbg->state == EVP_RAND_STATE_READY)
+ return 1;
+ return 0;
+}
+
+/*
+ * Reseed |drbg|, mixing in the specified data
+ *
+ * Acquires the drbg->lock for writing, if non-null.
+ *
+ * Returns 1 on success, 0 on failure.
+ */
+int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adinlen)
+{
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, ent,
+ ent_len, adin, adinlen);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+/*
+ * Generate |outlen| bytes into the buffer at |out|. Reseed if we need
+ * to or if |prediction_resistance| is set. Additional input can be
+ * sent in |adin| and |adinlen|.
+ *
+ * Acquires the drbg->lock for writing if available
+ *
+ * Returns 1 on success, 0 on failure.
+ *
+ */
+int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen,
+ unsigned int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adinlen)
+{
+ int fork_id;
+ int reseed_required = 0;
+ int ret = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ if (drbg->state != EVP_RAND_STATE_READY) {
+ /* try to recover from previous errors */
+ rand_drbg_restart(drbg);
+
+ if (drbg->state == EVP_RAND_STATE_ERROR) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_IN_ERROR_STATE);
+ goto err;
+ }
+ if (drbg->state == EVP_RAND_STATE_UNINITIALISED) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_INSTANTIATED);
+ goto err;
+ }
+ }
+ if (strength > drbg->strength) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INSUFFICIENT_DRBG_STRENGTH);
+ goto err;
+ }
+
+ if (outlen > drbg->max_request) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_REQUEST_TOO_LARGE_FOR_DRBG);
+ goto err;
+ }
+ if (adinlen > drbg->max_adinlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ADDITIONAL_INPUT_TOO_LONG);
+ goto err;
+ }
+
+ fork_id = openssl_get_fork_id();
+
+ if (drbg->fork_id != fork_id) {
+ drbg->fork_id = fork_id;
+ reseed_required = 1;
+ }
+
+ if (drbg->reseed_interval > 0) {
+ if (drbg->generate_counter >= drbg->reseed_interval)
+ reseed_required = 1;
+ }
+ if (drbg->reseed_time_interval > 0) {
+ time_t now = time(NULL);
+ if (now < drbg->reseed_time
+ || now - drbg->reseed_time >= drbg->reseed_time_interval)
+ reseed_required = 1;
+ }
+ if (drbg->parent != NULL
+ && get_parent_reseed_count(drbg) != drbg->parent_reseed_counter)
+ reseed_required = 1;
+
+ if (reseed_required || prediction_resistance) {
+ if (!ossl_prov_drbg_reseed_unlocked(drbg, prediction_resistance, NULL,
+ 0, adin, adinlen)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_RESEED_ERROR);
+ goto err;
+ }
+ adin = NULL;
+ adinlen = 0;
+ }
+
+ if (!drbg->generate(drbg, out, outlen, adin, adinlen)) {
+ drbg->state = EVP_RAND_STATE_ERROR;
+ ERR_raise(ERR_LIB_PROV, PROV_R_GENERATE_ERROR);
+ goto err;
+ }
+
+ drbg->generate_counter++;
+
+ ret = 1;
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+/*
+ * Restart |drbg|, using the specified entropy or additional input
+ *
+ * Tries its best to get the drbg instantiated by all means,
+ * regardless of its current state.
+ *
+ * Optionally, a |buffer| of |len| random bytes can be passed,
+ * which is assumed to contain at least |entropy| bits of entropy.
+ *
+ * If |entropy| > 0, the buffer content is used as entropy input.
+ *
+ * If |entropy| == 0, the buffer content is used as additional input
+ *
+ * Returns 1 on success, 0 on failure.
+ *
+ * This function is used internally only.
+ */
+static int rand_drbg_restart(PROV_DRBG *drbg)
+{
+ /* repair error state */
+ if (drbg->state == EVP_RAND_STATE_ERROR)
+ drbg->uninstantiate(drbg);
+
+ /* repair uninitialized state */
+ if (drbg->state == EVP_RAND_STATE_UNINITIALISED)
+ /* reinstantiate drbg */
+ ossl_prov_drbg_instantiate(drbg, drbg->strength, 0, NULL, 0);
+
+ return drbg->state == EVP_RAND_STATE_READY;
+}
+
+/* Provider support from here down */
+static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch,
+ int function)
+{
+ if (dispatch != NULL)
+ while (dispatch->function_id != 0) {
+ if (dispatch->function_id == function)
+ return dispatch;
+ dispatch++;
+ }
+ return NULL;
+}
+
+int ossl_drbg_enable_locking(void *vctx)
+{
+ PROV_DRBG *drbg = vctx;
+
+ if (drbg != NULL && drbg->lock == NULL) {
+ if (drbg->parent_enable_locking != NULL)
+ if (!drbg->parent_enable_locking(drbg->parent)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED);
+ return 0;
+ }
+ drbg->lock = CRYPTO_THREAD_lock_new();
+ if (drbg->lock == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * Allocate memory and initialize a new DRBG. The DRBG is allocated on
+ * the secure heap if |secure| is nonzero and the secure heap is enabled.
+ * The |parent|, if not NULL, will be used as random source for reseeding.
+ * This also requires the parent's provider context and the parent's lock.
+ *
+ * Returns a pointer to the new DRBG instance on success, NULL on failure.
+ */
+PROV_DRBG *ossl_rand_drbg_new
+ (void *provctx, void *parent, const OSSL_DISPATCH *p_dispatch,
+ int (*dnew)(PROV_DRBG *ctx),
+ void (*dfree)(void *vctx),
+ int (*instantiate)(PROV_DRBG *drbg,
+ const unsigned char *entropy, size_t entropylen,
+ const unsigned char *nonce, size_t noncelen,
+ const unsigned char *pers, size_t perslen),
+ int (*uninstantiate)(PROV_DRBG *ctx),
+ int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len),
+ int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len))
+{
+ PROV_DRBG *drbg;
+ unsigned int p_str;
+ const OSSL_DISPATCH *pfunc;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ drbg = OPENSSL_zalloc(sizeof(*drbg));
+ if (drbg == NULL)
+ return NULL;
+
+ drbg->provctx = provctx;
+ drbg->instantiate = instantiate;
+ drbg->uninstantiate = uninstantiate;
+ drbg->reseed = reseed;
+ drbg->generate = generate;
+ drbg->fork_id = openssl_get_fork_id();
+
+ /* Extract parent's functions */
+ drbg->parent = parent;
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_ENABLE_LOCKING)) != NULL)
+ drbg->parent_enable_locking = OSSL_FUNC_rand_enable_locking(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_LOCK)) != NULL)
+ drbg->parent_lock = OSSL_FUNC_rand_lock(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_UNLOCK)) != NULL)
+ drbg->parent_unlock = OSSL_FUNC_rand_unlock(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_CTX_PARAMS)) != NULL)
+ drbg->parent_get_ctx_params = OSSL_FUNC_rand_get_ctx_params(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_NONCE)) != NULL)
+ drbg->parent_nonce = OSSL_FUNC_rand_nonce(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_SEED)) != NULL)
+ drbg->parent_get_seed = OSSL_FUNC_rand_get_seed(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_CLEAR_SEED)) != NULL)
+ drbg->parent_clear_seed = OSSL_FUNC_rand_clear_seed(pfunc);
+
+ /* Set some default maximums up */
+ drbg->max_entropylen = DRBG_MAX_LENGTH;
+ drbg->max_noncelen = DRBG_MAX_LENGTH;
+ drbg->max_perslen = DRBG_MAX_LENGTH;
+ drbg->max_adinlen = DRBG_MAX_LENGTH;
+ drbg->generate_counter = 1;
+ drbg->reseed_counter = 1;
+ drbg->reseed_interval = RESEED_INTERVAL;
+ drbg->reseed_time_interval = TIME_INTERVAL;
+
+ if (!dnew(drbg))
+ goto err;
+
+ if (parent != NULL) {
+ if (!get_parent_strength(drbg, &p_str))
+ goto err;
+ if (drbg->strength > p_str) {
+ /*
+ * We currently don't support the algorithm from NIST SP 800-90C
+ * 10.1.2 to use a weaker DRBG as source
+ */
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_STRENGTH_TOO_WEAK);
+ goto err;
+ }
+ }
+#ifdef TSAN_REQUIRES_LOCKING
+ if (!ossl_drbg_enable_locking(drbg))
+ goto err;
+#endif
+ return drbg;
+
+ err:
+ dfree(drbg);
+ return NULL;
+}
+
+void ossl_rand_drbg_free(PROV_DRBG *drbg)
+{
+ if (drbg == NULL)
+ return;
+
+ CRYPTO_THREAD_lock_free(drbg->lock);
+ OPENSSL_free(drbg);
+}
+
+/*
+ * Helper function called by internal DRBG implementations. Assumes that at
+ * least a read lock has been taken on drbg->lock
+ */
+int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[])
+{
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE);
+ if (p != NULL && !OSSL_PARAM_set_int(p, drbg->state))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_set_int(p, drbg->strength))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MIN_ENTROPYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->min_entropylen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_ENTROPYLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_entropylen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MIN_NONCELEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->min_noncelen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_NONCELEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_noncelen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_PERSLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_perslen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAX_ADINLEN);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, drbg->max_adinlen))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_REQUESTS);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, drbg->reseed_interval))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_TIME);
+ if (p != NULL && !OSSL_PARAM_set_time_t(p, drbg->reseed_time))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL);
+ if (p != NULL && !OSSL_PARAM_set_time_t(p, drbg->reseed_time_interval))
+ return 0;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(drbg, params))
+ return 0;
+ return 1;
+}
+
+/*
+ * Helper function to get certain params that require no lock to obtain. Sets
+ * *complete to 1 if all the params were processed, or 0 otherwise
+ */
+int ossl_drbg_get_ctx_params_no_lock(PROV_DRBG *drbg, OSSL_PARAM params[],
+ int *complete)
+{
+ size_t cnt = 0;
+ OSSL_PARAM *p;
+
+ /* This value never changes once set */
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_size_t(p, drbg->max_request))
+ return 0;
+ cnt++;
+ }
+
+ /*
+ * Can be changed by multiple threads, but we tolerate inaccuracies in this
+ * value.
+ */
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_RESEED_COUNTER);
+ if (p != NULL) {
+ if (!OSSL_PARAM_set_uint(p, tsan_load(&drbg->reseed_counter)))
+ return 0;
+ cnt++;
+ }
+
+ if (params[cnt].key == NULL)
+ *complete = 1;
+ else
+ *complete = 0;
+
+ return 1;
+}
+
+int ossl_drbg_set_ctx_params(PROV_DRBG *drbg, const OSSL_PARAM params[])
+{
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_RESEED_REQUESTS);
+ if (p != NULL && !OSSL_PARAM_get_uint(p, &drbg->reseed_interval))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL);
+ if (p != NULL && !OSSL_PARAM_get_time_t(p, &drbg->reseed_time_interval))
+ return 0;
+
+ return 1;
+}
+
+#ifdef FIPS_MODULE
+static int digest_allowed(const EVP_MD *md)
+{
+ /* FIPS 140-3 IG D.R limited DRBG digests to a specific set */
+ static const char *const allowed_digests[] = {
+ "SHA1", /* SHA 1 allowed */
+ "SHA2-256", "SHA2-512", /* non-truncated SHA2 allowed */
+ "SHA3-256", "SHA3-512", /* non-truncated SHA3 allowed */
+ };
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(allowed_digests); i++) {
+ if (EVP_MD_is_a(md, allowed_digests[i]))
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/* Confirm digest is allowed to be used with a DRBG */
+int ossl_drbg_verify_digest(PROV_DRBG *drbg, OSSL_LIB_CTX *libctx,
+ const EVP_MD *md)
+{
+#ifdef FIPS_MODULE
+ int approved = digest_allowed(md);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(drbg, OSSL_FIPS_IND_SETTABLE0,
+ libctx, "DRBG", "Digest",
+ ossl_fips_config_restricted_drbg_digests)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ }
+#else /* FIPS_MODULE */
+ /* Outside of FIPS, any digests that are not XOF are allowed */
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+#endif /* FIPS_MODULE */
+ return 1;
+}
diff --git a/crypto/openssl/providers/implementations/rands/drbg_ctr.c b/crypto/openssl/providers/implementations/rands/drbg_ctr.c
new file mode 100644
index 000000000000..a5c929a2cadc
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/drbg_ctr.c
@@ -0,0 +1,857 @@
+/*
+ * Copyright 2011-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/aes.h>
+#include <openssl/proverr.h>
+#include "crypto/modes.h"
+#include "internal/thread_once.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "drbg_local.h"
+#include "crypto/evp.h"
+#include "crypto/evp/evp_local.h"
+#include "internal/provider.h"
+#include "internal/common.h"
+
+static OSSL_FUNC_rand_newctx_fn drbg_ctr_new_wrapper;
+static OSSL_FUNC_rand_freectx_fn drbg_ctr_free;
+static OSSL_FUNC_rand_instantiate_fn drbg_ctr_instantiate_wrapper;
+static OSSL_FUNC_rand_uninstantiate_fn drbg_ctr_uninstantiate_wrapper;
+static OSSL_FUNC_rand_generate_fn drbg_ctr_generate_wrapper;
+static OSSL_FUNC_rand_reseed_fn drbg_ctr_reseed_wrapper;
+static OSSL_FUNC_rand_settable_ctx_params_fn drbg_ctr_settable_ctx_params;
+static OSSL_FUNC_rand_set_ctx_params_fn drbg_ctr_set_ctx_params;
+static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_ctr_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn drbg_ctr_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn drbg_ctr_verify_zeroization;
+
+static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]);
+
+/*
+ * The state of a DRBG AES-CTR.
+ */
+typedef struct rand_drbg_ctr_st {
+ EVP_CIPHER_CTX *ctx_ecb;
+ EVP_CIPHER_CTX *ctx_ctr;
+ EVP_CIPHER_CTX *ctx_df;
+ EVP_CIPHER *cipher_ecb;
+ EVP_CIPHER *cipher_ctr;
+ size_t keylen;
+ int use_df;
+ unsigned char K[32];
+ unsigned char V[16];
+ /* Temporary block storage used by ctr_df */
+ unsigned char bltmp[16];
+ size_t bltmp_pos;
+ unsigned char KX[48];
+} PROV_DRBG_CTR;
+
+/*
+ * Implementation of NIST SP 800-90A CTR DRBG.
+ */
+static void inc_128(PROV_DRBG_CTR *ctr)
+{
+ unsigned char *p = &ctr->V[0];
+ u32 n = 16, c = 1;
+
+ do {
+ --n;
+ c += p[n];
+ p[n] = (u8)c;
+ c >>= 8;
+ } while (n);
+}
+
+static void ctr_XOR(PROV_DRBG_CTR *ctr, const unsigned char *in, size_t inlen)
+{
+ size_t i, n;
+
+ if (in == NULL || inlen == 0)
+ return;
+
+ /*
+ * Any zero padding will have no effect on the result as we
+ * are XORing. So just process however much input we have.
+ */
+ n = inlen < ctr->keylen ? inlen : ctr->keylen;
+ if (!ossl_assert(n <= sizeof(ctr->K)))
+ return;
+ for (i = 0; i < n; i++)
+ ctr->K[i] ^= in[i];
+ if (inlen <= ctr->keylen)
+ return;
+
+ n = inlen - ctr->keylen;
+ if (n > 16) {
+ /* Should never happen */
+ n = 16;
+ }
+ for (i = 0; i < n; i++)
+ ctr->V[i] ^= in[i + ctr->keylen];
+}
+
+/*
+ * Process a complete block using BCC algorithm of SP 800-90A 10.3.3
+ */
+__owur static int ctr_BCC_block(PROV_DRBG_CTR *ctr, unsigned char *out,
+ const unsigned char *in, int len)
+{
+ int i, outlen = AES_BLOCK_SIZE;
+
+ for (i = 0; i < len; i++)
+ out[i] ^= in[i];
+
+ if (!EVP_CipherUpdate(ctr->ctx_df, out, &outlen, out, len)
+ || outlen != len)
+ return 0;
+ return 1;
+}
+
+
+/*
+ * Handle several BCC operations for as much data as we need for K and X
+ */
+__owur static int ctr_BCC_blocks(PROV_DRBG_CTR *ctr, const unsigned char *in)
+{
+ unsigned char in_tmp[48];
+ unsigned char num_of_blk = 2;
+
+ memcpy(in_tmp, in, 16);
+ memcpy(in_tmp + 16, in, 16);
+ if (ctr->keylen != 16) {
+ memcpy(in_tmp + 32, in, 16);
+ num_of_blk = 3;
+ }
+ return ctr_BCC_block(ctr, ctr->KX, in_tmp, AES_BLOCK_SIZE * num_of_blk);
+}
+
+/*
+ * Initialise BCC blocks: these have the value 0,1,2 in leftmost positions:
+ * see 10.3.1 stage 7.
+ */
+__owur static int ctr_BCC_init(PROV_DRBG_CTR *ctr)
+{
+ unsigned char bltmp[48] = {0};
+ unsigned char num_of_blk;
+
+ memset(ctr->KX, 0, 48);
+ num_of_blk = ctr->keylen == 16 ? 2 : 3;
+ bltmp[(AES_BLOCK_SIZE * 1) + 3] = 1;
+ bltmp[(AES_BLOCK_SIZE * 2) + 3] = 2;
+ return ctr_BCC_block(ctr, ctr->KX, bltmp, num_of_blk * AES_BLOCK_SIZE);
+}
+
+/*
+ * Process several blocks into BCC algorithm, some possibly partial
+ */
+__owur static int ctr_BCC_update(PROV_DRBG_CTR *ctr,
+ const unsigned char *in, size_t inlen)
+{
+ if (in == NULL || inlen == 0)
+ return 1;
+
+ /* If we have partial block handle it first */
+ if (ctr->bltmp_pos) {
+ size_t left = 16 - ctr->bltmp_pos;
+
+ /* If we now have a complete block process it */
+ if (inlen >= left) {
+ memcpy(ctr->bltmp + ctr->bltmp_pos, in, left);
+ if (!ctr_BCC_blocks(ctr, ctr->bltmp))
+ return 0;
+ ctr->bltmp_pos = 0;
+ inlen -= left;
+ in += left;
+ }
+ }
+
+ /* Process zero or more complete blocks */
+ for (; inlen >= 16; in += 16, inlen -= 16) {
+ if (!ctr_BCC_blocks(ctr, in))
+ return 0;
+ }
+
+ /* Copy any remaining partial block to the temporary buffer */
+ if (inlen > 0) {
+ memcpy(ctr->bltmp + ctr->bltmp_pos, in, inlen);
+ ctr->bltmp_pos += inlen;
+ }
+ return 1;
+}
+
+__owur static int ctr_BCC_final(PROV_DRBG_CTR *ctr)
+{
+ if (ctr->bltmp_pos) {
+ memset(ctr->bltmp + ctr->bltmp_pos, 0, 16 - ctr->bltmp_pos);
+ if (!ctr_BCC_blocks(ctr, ctr->bltmp))
+ return 0;
+ }
+ return 1;
+}
+
+__owur static int ctr_df(PROV_DRBG_CTR *ctr,
+ const unsigned char *in1, size_t in1len,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *in3, size_t in3len)
+{
+ static unsigned char c80 = 0x80;
+ size_t inlen;
+ unsigned char *p = ctr->bltmp;
+ int outlen = AES_BLOCK_SIZE;
+
+ if (!ctr_BCC_init(ctr))
+ return 0;
+ if (in1 == NULL)
+ in1len = 0;
+ if (in2 == NULL)
+ in2len = 0;
+ if (in3 == NULL)
+ in3len = 0;
+ inlen = in1len + in2len + in3len;
+ /* Initialise L||N in temporary block */
+ *p++ = (inlen >> 24) & 0xff;
+ *p++ = (inlen >> 16) & 0xff;
+ *p++ = (inlen >> 8) & 0xff;
+ *p++ = inlen & 0xff;
+
+ /* NB keylen is at most 32 bytes */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0;
+ *p = (unsigned char)((ctr->keylen + 16) & 0xff);
+ ctr->bltmp_pos = 8;
+ if (!ctr_BCC_update(ctr, in1, in1len)
+ || !ctr_BCC_update(ctr, in2, in2len)
+ || !ctr_BCC_update(ctr, in3, in3len)
+ || !ctr_BCC_update(ctr, &c80, 1)
+ || !ctr_BCC_final(ctr))
+ return 0;
+ /* Set up key K */
+ if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->KX, NULL, -1))
+ return 0;
+ /* X follows key K */
+ if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX, &outlen, ctr->KX + ctr->keylen,
+ AES_BLOCK_SIZE)
+ || outlen != AES_BLOCK_SIZE)
+ return 0;
+ if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX + 16, &outlen, ctr->KX,
+ AES_BLOCK_SIZE)
+ || outlen != AES_BLOCK_SIZE)
+ return 0;
+ if (ctr->keylen != 16)
+ if (!EVP_CipherUpdate(ctr->ctx_ecb, ctr->KX + 32, &outlen,
+ ctr->KX + 16, AES_BLOCK_SIZE)
+ || outlen != AES_BLOCK_SIZE)
+ return 0;
+ return 1;
+}
+
+/*
+ * NB the no-df Update in SP800-90A specifies a constant input length
+ * of seedlen, however other uses of this algorithm pad the input with
+ * zeroes if necessary and have up to two parameters XORed together,
+ * so we handle both cases in this function instead.
+ */
+__owur static int ctr_update(PROV_DRBG *drbg,
+ const unsigned char *in1, size_t in1len,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *nonce, size_t noncelen)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ int outlen = AES_BLOCK_SIZE;
+ unsigned char V_tmp[48], out[48];
+ unsigned char len;
+
+ /* correct key is already set up. */
+ memcpy(V_tmp, ctr->V, 16);
+ inc_128(ctr);
+ memcpy(V_tmp + 16, ctr->V, 16);
+ if (ctr->keylen == 16) {
+ len = 32;
+ } else {
+ inc_128(ctr);
+ memcpy(V_tmp + 32, ctr->V, 16);
+ len = 48;
+ }
+ if (!EVP_CipherUpdate(ctr->ctx_ecb, out, &outlen, V_tmp, len)
+ || outlen != len)
+ return 0;
+ memcpy(ctr->K, out, ctr->keylen);
+ memcpy(ctr->V, out + ctr->keylen, 16);
+
+ if (ctr->use_df) {
+ /* If no input reuse existing derived value */
+ if (in1 != NULL || nonce != NULL || in2 != NULL)
+ if (!ctr_df(ctr, in1, in1len, nonce, noncelen, in2, in2len))
+ return 0;
+ /* If this a reuse input in1len != 0 */
+ if (in1len)
+ ctr_XOR(ctr, ctr->KX, drbg->seedlen);
+ } else {
+ ctr_XOR(ctr, in1, in1len);
+ ctr_XOR(ctr, in2, in2len);
+ }
+
+ if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->K, NULL, -1)
+ || !EVP_CipherInit_ex(ctr->ctx_ctr, NULL, NULL, ctr->K, NULL, -1))
+ return 0;
+ return 1;
+}
+
+static int drbg_ctr_instantiate(PROV_DRBG *drbg,
+ const unsigned char *entropy, size_t entropylen,
+ const unsigned char *nonce, size_t noncelen,
+ const unsigned char *pers, size_t perslen)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+
+ if (entropy == NULL)
+ return 0;
+
+ memset(ctr->K, 0, sizeof(ctr->K));
+ memset(ctr->V, 0, sizeof(ctr->V));
+ if (!EVP_CipherInit_ex(ctr->ctx_ecb, NULL, NULL, ctr->K, NULL, -1))
+ return 0;
+
+ inc_128(ctr);
+ if (!ctr_update(drbg, entropy, entropylen, pers, perslen, nonce, noncelen))
+ return 0;
+ return 1;
+}
+
+static int drbg_ctr_instantiate_wrapper(void *vdrbg, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr,
+ size_t pstr_len,
+ const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ if (!ossl_prov_is_running()
+ || !drbg_ctr_set_ctx_params_locked(drbg, params))
+ goto err;
+ ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance,
+ pstr, pstr_len);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+static int drbg_ctr_reseed(PROV_DRBG *drbg,
+ const unsigned char *entropy, size_t entropylen,
+ const unsigned char *adin, size_t adinlen)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+
+ if (entropy == NULL)
+ return 0;
+
+ inc_128(ctr);
+ if (!ctr_update(drbg, entropy, entropylen, adin, adinlen, NULL, 0))
+ return 0;
+ return 1;
+}
+
+static int drbg_ctr_reseed_wrapper(void *vdrbg, int prediction_resistance,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len,
+ adin, adin_len);
+}
+
+static void ctr96_inc(unsigned char *counter)
+{
+ u32 n = 12, c = 1;
+
+ do {
+ --n;
+ c += counter[n];
+ counter[n] = (u8)c;
+ c >>= 8;
+ } while (n);
+}
+
+static int drbg_ctr_generate(PROV_DRBG *drbg,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adinlen)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ unsigned int ctr32, blocks;
+ int outl, buflen;
+
+ if (adin != NULL && adinlen != 0) {
+ inc_128(ctr);
+
+ if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0))
+ return 0;
+ /* This means we reuse derived value */
+ if (ctr->use_df) {
+ adin = NULL;
+ adinlen = 1;
+ }
+ } else {
+ adinlen = 0;
+ }
+
+ inc_128(ctr);
+
+ if (outlen == 0) {
+ inc_128(ctr);
+
+ if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0))
+ return 0;
+ return 1;
+ }
+
+ memset(out, 0, outlen);
+
+ do {
+ if (!EVP_CipherInit_ex(ctr->ctx_ctr,
+ NULL, NULL, NULL, ctr->V, -1))
+ return 0;
+
+ /*-
+ * outlen has type size_t while EVP_CipherUpdate takes an
+ * int argument and thus cannot be guaranteed to process more
+ * than 2^31-1 bytes at a time. We process such huge generate
+ * requests in 2^30 byte chunks, which is the greatest multiple
+ * of AES block size lower than or equal to 2^31-1.
+ */
+ buflen = outlen > (1U << 30) ? (1U << 30) : outlen;
+ blocks = (buflen + 15) / 16;
+
+ ctr32 = GETU32(ctr->V + 12) + blocks;
+ if (ctr32 < blocks) {
+ /* 32-bit counter overflow into V. */
+ if (ctr32 != 0) {
+ blocks -= ctr32;
+ buflen = blocks * 16;
+ ctr32 = 0;
+ }
+ ctr96_inc(ctr->V);
+ }
+ PUTU32(ctr->V + 12, ctr32);
+
+ if (!EVP_CipherUpdate(ctr->ctx_ctr, out, &outl, out, buflen)
+ || outl != buflen)
+ return 0;
+
+ out += buflen;
+ outlen -= buflen;
+ } while (outlen);
+
+ if (!ctr_update(drbg, adin, adinlen, NULL, 0, NULL, 0))
+ return 0;
+ return 1;
+}
+
+static int drbg_ctr_generate_wrapper
+ (void *vdrbg, unsigned char *out, size_t outlen,
+ unsigned int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_generate(drbg, out, outlen, strength,
+ prediction_resistance, adin, adin_len);
+}
+
+static int drbg_ctr_uninstantiate(PROV_DRBG *drbg)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+
+ OPENSSL_cleanse(ctr->K, sizeof(ctr->K));
+ OPENSSL_cleanse(ctr->V, sizeof(ctr->V));
+ OPENSSL_cleanse(ctr->bltmp, sizeof(ctr->bltmp));
+ OPENSSL_cleanse(ctr->KX, sizeof(ctr->KX));
+ ctr->bltmp_pos = 0;
+ return ossl_prov_drbg_uninstantiate(drbg);
+}
+
+static int drbg_ctr_uninstantiate_wrapper(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_ctr_uninstantiate(drbg);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static int drbg_ctr_verify_zeroization(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ PROV_DRBG_VERIFY_ZEROIZATION(ctr->K);
+ PROV_DRBG_VERIFY_ZEROIZATION(ctr->V);
+ PROV_DRBG_VERIFY_ZEROIZATION(ctr->bltmp);
+ PROV_DRBG_VERIFY_ZEROIZATION(ctr->KX);
+ if (ctr->bltmp_pos != 0)
+ goto err;
+
+ ret = 1;
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+static int drbg_ctr_init_lengths(PROV_DRBG *drbg)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ int res = 1;
+
+ /* Maximum number of bits per request = 2^19 = 2^16 bytes */
+ drbg->max_request = 1 << 16;
+ if (ctr->use_df) {
+ drbg->min_entropylen = 0;
+ drbg->max_entropylen = DRBG_MAX_LENGTH;
+ drbg->min_noncelen = 0;
+ drbg->max_noncelen = DRBG_MAX_LENGTH;
+ drbg->max_perslen = DRBG_MAX_LENGTH;
+ drbg->max_adinlen = DRBG_MAX_LENGTH;
+
+ if (ctr->keylen > 0) {
+ drbg->min_entropylen = ctr->keylen;
+ drbg->min_noncelen = drbg->min_entropylen / 2;
+ }
+ } else {
+ const size_t len = ctr->keylen > 0 ? drbg->seedlen : DRBG_MAX_LENGTH;
+
+ drbg->min_entropylen = len;
+ drbg->max_entropylen = len;
+ /* Nonce not used */
+ drbg->min_noncelen = 0;
+ drbg->max_noncelen = 0;
+ drbg->max_perslen = len;
+ drbg->max_adinlen = len;
+ }
+ return res;
+}
+
+static int drbg_ctr_init(PROV_DRBG *drbg)
+{
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ size_t keylen;
+
+ if (ctr->cipher_ctr == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_CIPHER);
+ return 0;
+ }
+ ctr->keylen = keylen = EVP_CIPHER_get_key_length(ctr->cipher_ctr);
+ if (ctr->ctx_ecb == NULL)
+ ctr->ctx_ecb = EVP_CIPHER_CTX_new();
+ if (ctr->ctx_ctr == NULL)
+ ctr->ctx_ctr = EVP_CIPHER_CTX_new();
+ if (ctr->ctx_ecb == NULL || ctr->ctx_ctr == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB);
+ goto err;
+ }
+
+ if (!EVP_CipherInit_ex(ctr->ctx_ecb,
+ ctr->cipher_ecb, NULL, NULL, NULL, 1)
+ || !EVP_CipherInit_ex(ctr->ctx_ctr,
+ ctr->cipher_ctr, NULL, NULL, NULL, 1)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_INITIALISE_CIPHERS);
+ goto err;
+ }
+
+ drbg->strength = keylen * 8;
+ drbg->seedlen = keylen + 16;
+
+ if (ctr->use_df) {
+ /* df initialisation */
+ static const unsigned char df_key[32] = {
+ 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
+ };
+
+ if (ctr->ctx_df == NULL)
+ ctr->ctx_df = EVP_CIPHER_CTX_new();
+ if (ctr->ctx_df == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_EVP_LIB);
+ goto err;
+ }
+ /* Set key schedule for df_key */
+ if (!EVP_CipherInit_ex(ctr->ctx_df,
+ ctr->cipher_ecb, NULL, df_key, NULL, 1)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DERIVATION_FUNCTION_INIT_FAILED);
+ goto err;
+ }
+ }
+ return drbg_ctr_init_lengths(drbg);
+
+err:
+ EVP_CIPHER_CTX_free(ctr->ctx_ecb);
+ EVP_CIPHER_CTX_free(ctr->ctx_ctr);
+ ctr->ctx_ecb = ctr->ctx_ctr = NULL;
+ return 0;
+}
+
+static int drbg_ctr_new(PROV_DRBG *drbg)
+{
+ PROV_DRBG_CTR *ctr;
+
+ ctr = OPENSSL_secure_zalloc(sizeof(*ctr));
+ if (ctr == NULL)
+ return 0;
+
+ ctr->use_df = 1;
+ drbg->data = ctr;
+ OSSL_FIPS_IND_INIT(drbg)
+ return drbg_ctr_init_lengths(drbg);
+}
+
+static void *drbg_ctr_new_wrapper(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ return ossl_rand_drbg_new(provctx, parent, parent_dispatch,
+ &drbg_ctr_new, &drbg_ctr_free,
+ &drbg_ctr_instantiate, &drbg_ctr_uninstantiate,
+ &drbg_ctr_reseed, &drbg_ctr_generate);
+}
+
+static void drbg_ctr_free(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_CTR *ctr;
+
+ if (drbg != NULL && (ctr = (PROV_DRBG_CTR *)drbg->data) != NULL) {
+ EVP_CIPHER_CTX_free(ctr->ctx_ecb);
+ EVP_CIPHER_CTX_free(ctr->ctx_ctr);
+ EVP_CIPHER_CTX_free(ctr->ctx_df);
+ EVP_CIPHER_free(ctr->cipher_ecb);
+ EVP_CIPHER_free(ctr->cipher_ctr);
+
+ OPENSSL_secure_clear_free(ctr, sizeof(*ctr));
+ }
+ ossl_rand_drbg_free(drbg);
+}
+
+static int drbg_ctr_get_ctx_params(void *vdrbg, OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)drbg->data;
+ OSSL_PARAM *p;
+ int ret = 0, complete = 0;
+
+ if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete))
+ return 0;
+
+ if (complete)
+ return 1;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_USE_DF);
+ if (p != NULL && !OSSL_PARAM_set_int(p, ctr->use_df))
+ goto err;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_CIPHER);
+ if (p != NULL) {
+ if (ctr->cipher_ctr == NULL
+ || !OSSL_PARAM_set_utf8_string(p,
+ EVP_CIPHER_get0_name(ctr->cipher_ctr)))
+ goto err;
+ }
+
+ ret = ossl_drbg_get_ctx_params(drbg, params);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_ctr_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_int(OSSL_DRBG_PARAM_USE_DF, NULL),
+ OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int drbg_ctr_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *ctx = (PROV_DRBG *)vctx;
+ PROV_DRBG_CTR *ctr = (PROV_DRBG_CTR *)ctx->data;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ OSSL_PROVIDER *prov = NULL;
+ const OSSL_PARAM *p;
+ char *ecb;
+ const char *propquery = NULL;
+ int i, cipher_init = 0;
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_USE_DF)) != NULL
+ && OSSL_PARAM_get_int(p, &i)) {
+ /* FIPS errors out in the drbg_ctr_init() call later */
+ ctr->use_df = i != 0;
+ cipher_init = 1;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params,
+ OSSL_DRBG_PARAM_PROPERTIES)) != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ propquery = (const char *)p->data;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params,
+ OSSL_PROV_PARAM_CORE_PROV_NAME)) != NULL) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ if ((prov = ossl_provider_find(libctx,
+ (const char *)p->data, 1)) == NULL)
+ return 0;
+ }
+
+ if ((p = OSSL_PARAM_locate_const(params, OSSL_DRBG_PARAM_CIPHER)) != NULL) {
+ const char *base = (const char *)p->data;
+ size_t ctr_str_len = sizeof("CTR") - 1;
+ size_t ecb_str_len = sizeof("ECB") - 1;
+
+ if (p->data_type != OSSL_PARAM_UTF8_STRING
+ || p->data_size < ctr_str_len) {
+ ossl_provider_free(prov);
+ return 0;
+ }
+ if (OPENSSL_strcasecmp("CTR", base + p->data_size - ctr_str_len) != 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_REQUIRE_CTR_MODE_CIPHER);
+ ossl_provider_free(prov);
+ return 0;
+ }
+ if ((ecb = OPENSSL_strndup(base, p->data_size)) == NULL) {
+ ossl_provider_free(prov);
+ return 0;
+ }
+ strcpy(ecb + p->data_size - ecb_str_len, "ECB");
+ EVP_CIPHER_free(ctr->cipher_ecb);
+ EVP_CIPHER_free(ctr->cipher_ctr);
+ /*
+ * Try to fetch algorithms from our own provider code, fallback
+ * to generic fetch only if that fails
+ */
+ (void)ERR_set_mark();
+ ctr->cipher_ctr = evp_cipher_fetch_from_prov(prov, base, NULL);
+ if (ctr->cipher_ctr == NULL) {
+ (void)ERR_pop_to_mark();
+ ctr->cipher_ctr = EVP_CIPHER_fetch(libctx, base, propquery);
+ } else {
+ (void)ERR_clear_last_mark();
+ }
+ (void)ERR_set_mark();
+ ctr->cipher_ecb = evp_cipher_fetch_from_prov(prov, ecb, NULL);
+ if (ctr->cipher_ecb == NULL) {
+ (void)ERR_pop_to_mark();
+ ctr->cipher_ecb = EVP_CIPHER_fetch(libctx, ecb, propquery);
+ } else {
+ (void)ERR_clear_last_mark();
+ }
+ OPENSSL_free(ecb);
+ if (ctr->cipher_ctr == NULL || ctr->cipher_ecb == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UNABLE_TO_FIND_CIPHERS);
+ ossl_provider_free(prov);
+ return 0;
+ }
+ cipher_init = 1;
+ }
+ ossl_provider_free(prov);
+
+ if (cipher_init && !drbg_ctr_init(ctx))
+ return 0;
+
+ return ossl_drbg_set_ctx_params(ctx, params);
+}
+
+static int drbg_ctr_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vctx;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_ctr_set_ctx_params_locked(vctx, params);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_ctr_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_CIPHER, NULL, 0),
+ OSSL_PARAM_int(OSSL_DRBG_PARAM_USE_DF, NULL),
+ OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON,
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_drbg_ctr_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_ctr_new_wrapper },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_ctr_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))drbg_ctr_instantiate_wrapper },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))drbg_ctr_uninstantiate_wrapper },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_ctr_generate_wrapper },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_ctr_reseed_wrapper },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock },
+ { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_ctr_settable_ctx_params },
+ { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_ctr_set_ctx_params },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_ctr_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_ctr_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))drbg_ctr_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/drbg_hash.c b/crypto/openssl/providers/implementations/rands/drbg_hash.c
new file mode 100644
index 000000000000..8bb831ae3572
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/drbg_hash.c
@@ -0,0 +1,655 @@
+/*
+ * Copyright 2011-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/sha.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/proverr.h>
+#include "internal/thread_once.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/implementations.h"
+#include "drbg_local.h"
+#include "crypto/evp.h"
+#include "crypto/evp/evp_local.h"
+#include "internal/provider.h"
+
+static OSSL_FUNC_rand_newctx_fn drbg_hash_new_wrapper;
+static OSSL_FUNC_rand_freectx_fn drbg_hash_free;
+static OSSL_FUNC_rand_instantiate_fn drbg_hash_instantiate_wrapper;
+static OSSL_FUNC_rand_uninstantiate_fn drbg_hash_uninstantiate_wrapper;
+static OSSL_FUNC_rand_generate_fn drbg_hash_generate_wrapper;
+static OSSL_FUNC_rand_reseed_fn drbg_hash_reseed_wrapper;
+static OSSL_FUNC_rand_settable_ctx_params_fn drbg_hash_settable_ctx_params;
+static OSSL_FUNC_rand_set_ctx_params_fn drbg_hash_set_ctx_params;
+static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_hash_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn drbg_hash_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn drbg_hash_verify_zeroization;
+
+static int drbg_hash_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]);
+
+/* 888 bits from SP800-90Ar1 10.1 table 2 */
+#define HASH_PRNG_MAX_SEEDLEN (888/8)
+
+/* 440 bits from SP800-90Ar1 10.1 table 2 */
+#define HASH_PRNG_SMALL_SEEDLEN (440/8)
+
+/* Determine what seedlen to use based on the block length */
+#define MAX_BLOCKLEN_USING_SMALL_SEEDLEN (256/8)
+#define INBYTE_IGNORE ((unsigned char)0xFF)
+
+typedef struct rand_drbg_hash_st {
+ PROV_DIGEST digest;
+ EVP_MD_CTX *ctx;
+ size_t blocklen;
+ unsigned char V[HASH_PRNG_MAX_SEEDLEN];
+ unsigned char C[HASH_PRNG_MAX_SEEDLEN];
+ /* Temporary value storage: should always exceed max digest length */
+ unsigned char vtmp[HASH_PRNG_MAX_SEEDLEN];
+} PROV_DRBG_HASH;
+
+/*
+ * SP800-90Ar1 10.3.1 Derivation function using a Hash Function (Hash_df).
+ * The input string used is composed of:
+ * inbyte - An optional leading byte (ignore if equal to INBYTE_IGNORE)
+ * in - input string 1 (A Non NULL value).
+ * in2 - optional input string (Can be NULL).
+ * in3 - optional input string (Can be NULL).
+ * These are concatenated as part of the DigestUpdate process.
+ */
+static int hash_df(PROV_DRBG *drbg, unsigned char *out,
+ const unsigned char inbyte,
+ const unsigned char *in, size_t inlen,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *in3, size_t in3len)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ EVP_MD_CTX *ctx = hash->ctx;
+ unsigned char *vtmp = hash->vtmp;
+ /* tmp = counter || num_bits_returned || [inbyte] */
+ unsigned char tmp[1 + 4 + 1];
+ int tmp_sz = 0;
+ size_t outlen = drbg->seedlen;
+ size_t num_bits_returned = outlen * 8;
+ /*
+ * No need to check outlen size here, as the standard only ever needs
+ * seedlen bytes which is always less than the maximum permitted.
+ */
+
+ /* (Step 3) counter = 1 (tmp[0] is the 8 bit counter) */
+ tmp[tmp_sz++] = 1;
+ /* tmp[1..4] is the fixed 32 bit no_of_bits_to_return */
+ tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 24) & 0xff);
+ tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 16) & 0xff);
+ tmp[tmp_sz++] = (unsigned char)((num_bits_returned >> 8) & 0xff);
+ tmp[tmp_sz++] = (unsigned char)(num_bits_returned & 0xff);
+ /* Tack the additional input byte onto the end of tmp if it exists */
+ if (inbyte != INBYTE_IGNORE)
+ tmp[tmp_sz++] = inbyte;
+
+ /* (Step 4) */
+ for (;;) {
+ /*
+ * (Step 4.1) out = out || Hash(tmp || in || [in2] || [in3])
+ * (where tmp = counter || num_bits_returned || [inbyte])
+ */
+ if (!(EVP_DigestInit_ex(ctx, ossl_prov_digest_md(&hash->digest), NULL)
+ && EVP_DigestUpdate(ctx, tmp, tmp_sz)
+ && EVP_DigestUpdate(ctx, in, inlen)
+ && (in2 == NULL || EVP_DigestUpdate(ctx, in2, in2len))
+ && (in3 == NULL || EVP_DigestUpdate(ctx, in3, in3len))))
+ return 0;
+
+ if (outlen < hash->blocklen) {
+ if (!EVP_DigestFinal(ctx, vtmp, NULL))
+ return 0;
+ memcpy(out, vtmp, outlen);
+ OPENSSL_cleanse(vtmp, hash->blocklen);
+ break;
+ } else if (!EVP_DigestFinal(ctx, out, NULL)) {
+ return 0;
+ }
+
+ outlen -= hash->blocklen;
+ if (outlen == 0)
+ break;
+ /* (Step 4.2) counter++ */
+ tmp[0]++;
+ out += hash->blocklen;
+ }
+ return 1;
+}
+
+/* Helper function that just passes 2 input parameters to hash_df() */
+static int hash_df1(PROV_DRBG *drbg, unsigned char *out,
+ const unsigned char in_byte,
+ const unsigned char *in1, size_t in1len)
+{
+ return hash_df(drbg, out, in_byte, in1, in1len, NULL, 0, NULL, 0);
+}
+
+/*
+ * Add 2 byte buffers together. The first elements in each buffer are the top
+ * most bytes. The result is stored in the dst buffer.
+ * The final carry is ignored i.e: dst = (dst + in) mod (2^seedlen_bits).
+ * where dst size is drbg->seedlen, and inlen <= drbg->seedlen.
+ */
+static int add_bytes(PROV_DRBG *drbg, unsigned char *dst,
+ unsigned char *in, size_t inlen)
+{
+ size_t i;
+ int result;
+ const unsigned char *add;
+ unsigned char carry = 0, *d;
+
+ assert(drbg->seedlen >= 1 && inlen >= 1 && inlen <= drbg->seedlen);
+
+ d = &dst[drbg->seedlen - 1];
+ add = &in[inlen - 1];
+
+ for (i = inlen; i > 0; i--, d--, add--) {
+ result = *d + *add + carry;
+ carry = (unsigned char)(result >> 8);
+ *d = (unsigned char)(result & 0xff);
+ }
+
+ if (carry != 0) {
+ /* Add the carry to the top of the dst if inlen is not the same size */
+ for (i = drbg->seedlen - inlen; i > 0; --i, d--) {
+ *d += 1; /* Carry can only be 1 */
+ if (*d != 0) /* exit if carry doesn't propagate to the next byte */
+ break;
+ }
+ }
+ return 1;
+}
+
+/* V = (V + Hash(inbyte || V || [additional_input]) mod (2^seedlen) */
+static int add_hash_to_v(PROV_DRBG *drbg, unsigned char inbyte,
+ const unsigned char *adin, size_t adinlen)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ EVP_MD_CTX *ctx = hash->ctx;
+
+ return EVP_DigestInit_ex(ctx, ossl_prov_digest_md(&hash->digest), NULL)
+ && EVP_DigestUpdate(ctx, &inbyte, 1)
+ && EVP_DigestUpdate(ctx, hash->V, drbg->seedlen)
+ && (adin == NULL || EVP_DigestUpdate(ctx, adin, adinlen))
+ && EVP_DigestFinal(ctx, hash->vtmp, NULL)
+ && add_bytes(drbg, hash->V, hash->vtmp, hash->blocklen);
+}
+
+/*
+ * The Hashgen() as listed in SP800-90Ar1 10.1.1.4 Hash_DRBG_Generate_Process.
+ *
+ * drbg contains the current value of V.
+ * outlen is the requested number of bytes.
+ * out is a buffer to return the generated bits.
+ *
+ * The algorithm to generate the bits is:
+ * data = V
+ * w = NULL
+ * for (i = 1 to m) {
+ * W = W || Hash(data)
+ * data = (data + 1) mod (2^seedlen)
+ * }
+ * out = Leftmost(W, outlen)
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int hash_gen(PROV_DRBG *drbg, unsigned char *out, size_t outlen)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ unsigned char one = 1;
+
+ if (outlen == 0)
+ return 1;
+ memcpy(hash->vtmp, hash->V, drbg->seedlen);
+ for (;;) {
+ if (!EVP_DigestInit_ex(hash->ctx, ossl_prov_digest_md(&hash->digest),
+ NULL)
+ || !EVP_DigestUpdate(hash->ctx, hash->vtmp, drbg->seedlen))
+ return 0;
+
+ if (outlen < hash->blocklen) {
+ if (!EVP_DigestFinal(hash->ctx, hash->vtmp, NULL))
+ return 0;
+ memcpy(out, hash->vtmp, outlen);
+ return 1;
+ } else {
+ if (!EVP_DigestFinal(hash->ctx, out, NULL))
+ return 0;
+ outlen -= hash->blocklen;
+ if (outlen == 0)
+ break;
+ out += hash->blocklen;
+ }
+ add_bytes(drbg, hash->vtmp, &one, 1);
+ }
+ return 1;
+}
+
+/*
+ * SP800-90Ar1 10.1.1.2 Hash_DRBG_Instantiate_Process:
+ *
+ * ent is entropy input obtained from a randomness source of length ent_len.
+ * nonce is a string of bytes of length nonce_len.
+ * pstr is a personalization string received from an application. May be NULL.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int drbg_hash_instantiate(PROV_DRBG *drbg,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *nonce, size_t nonce_len,
+ const unsigned char *pstr, size_t pstr_len)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+
+ EVP_MD_CTX_free(hash->ctx);
+ hash->ctx = EVP_MD_CTX_new();
+
+ /* (Step 1-3) V = Hash_df(entropy||nonce||pers, seedlen) */
+ return hash->ctx != NULL
+ && hash_df(drbg, hash->V, INBYTE_IGNORE,
+ ent, ent_len, nonce, nonce_len, pstr, pstr_len)
+ /* (Step 4) C = Hash_df(0x00||V, seedlen) */
+ && hash_df1(drbg, hash->C, 0x00, hash->V, drbg->seedlen);
+}
+
+static int drbg_hash_instantiate_wrapper(void *vdrbg, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr,
+ size_t pstr_len,
+ const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ if (!ossl_prov_is_running()
+ || !drbg_hash_set_ctx_params_locked(drbg, params))
+ goto err;
+ ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance,
+ pstr, pstr_len);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+/*
+ * SP800-90Ar1 10.1.1.3 Hash_DRBG_Reseed_Process:
+ *
+ * ent is entropy input bytes obtained from a randomness source.
+ * addin is additional input received from an application. May be NULL.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int drbg_hash_reseed(PROV_DRBG *drbg,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+
+ /* (Step 1-2) V = Hash_df(0x01 || V || entropy_input || additional_input) */
+ /* V about to be updated so use C as output instead */
+ if (!hash_df(drbg, hash->C, 0x01, hash->V, drbg->seedlen, ent, ent_len,
+ adin, adin_len))
+ return 0;
+ memcpy(hash->V, hash->C, drbg->seedlen);
+ /* (Step 4) C = Hash_df(0x00||V, seedlen) */
+ return hash_df1(drbg, hash->C, 0x00, hash->V, drbg->seedlen);
+}
+
+static int drbg_hash_reseed_wrapper(void *vdrbg, int prediction_resistance,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len,
+ adin, adin_len);
+}
+
+/*
+ * SP800-90Ar1 10.1.1.4 Hash_DRBG_Generate_Process:
+ *
+ * Generates pseudo random bytes using the drbg.
+ * out is a buffer to fill with outlen bytes of pseudo random data.
+ * addin is additional input received from an application. May be NULL.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int drbg_hash_generate(PROV_DRBG *drbg,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ unsigned char counter[4];
+ int reseed_counter = drbg->generate_counter;
+
+ counter[0] = (unsigned char)((reseed_counter >> 24) & 0xff);
+ counter[1] = (unsigned char)((reseed_counter >> 16) & 0xff);
+ counter[2] = (unsigned char)((reseed_counter >> 8) & 0xff);
+ counter[3] = (unsigned char)(reseed_counter & 0xff);
+
+ return hash->ctx != NULL
+ && (adin == NULL
+ /* (Step 2) if adin != NULL then V = V + Hash(0x02||V||adin) */
+ || adin_len == 0
+ || add_hash_to_v(drbg, 0x02, adin, adin_len))
+ /* (Step 3) Hashgen(outlen, V) */
+ && hash_gen(drbg, out, outlen)
+ /* (Step 4/5) H = V = (V + Hash(0x03||V) mod (2^seedlen_bits) */
+ && add_hash_to_v(drbg, 0x03, NULL, 0)
+ /* (Step 5) V = (V + H + C + reseed_counter) mod (2^seedlen_bits) */
+ /* V = (V + C) mod (2^seedlen_bits) */
+ && add_bytes(drbg, hash->V, hash->C, drbg->seedlen)
+ /* V = (V + reseed_counter) mod (2^seedlen_bits) */
+ && add_bytes(drbg, hash->V, counter, 4);
+}
+
+static int drbg_hash_generate_wrapper
+ (void *vdrbg, unsigned char *out, size_t outlen, unsigned int strength,
+ int prediction_resistance, const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_generate(drbg, out, outlen, strength,
+ prediction_resistance, adin, adin_len);
+}
+
+static int drbg_hash_uninstantiate(PROV_DRBG *drbg)
+{
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+
+ OPENSSL_cleanse(hash->V, sizeof(hash->V));
+ OPENSSL_cleanse(hash->C, sizeof(hash->C));
+ OPENSSL_cleanse(hash->vtmp, sizeof(hash->vtmp));
+ return ossl_prov_drbg_uninstantiate(drbg);
+}
+
+static int drbg_hash_uninstantiate_wrapper(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_hash_uninstantiate(drbg);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static int drbg_hash_verify_zeroization(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ PROV_DRBG_VERIFY_ZEROIZATION(hash->V);
+ PROV_DRBG_VERIFY_ZEROIZATION(hash->C);
+ PROV_DRBG_VERIFY_ZEROIZATION(hash->vtmp);
+
+ ret = 1;
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+static int drbg_hash_new(PROV_DRBG *ctx)
+{
+ PROV_DRBG_HASH *hash;
+
+ hash = OPENSSL_secure_zalloc(sizeof(*hash));
+ if (hash == NULL)
+ return 0;
+
+ OSSL_FIPS_IND_INIT(ctx)
+
+ ctx->data = hash;
+ ctx->seedlen = HASH_PRNG_MAX_SEEDLEN;
+ ctx->max_entropylen = DRBG_MAX_LENGTH;
+ ctx->max_noncelen = DRBG_MAX_LENGTH;
+ ctx->max_perslen = DRBG_MAX_LENGTH;
+ ctx->max_adinlen = DRBG_MAX_LENGTH;
+
+ /* Maximum number of bits per request = 2^19 = 2^16 bytes */
+ ctx->max_request = 1 << 16;
+ return 1;
+}
+
+static void *drbg_hash_new_wrapper(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ return ossl_rand_drbg_new(provctx, parent, parent_dispatch,
+ &drbg_hash_new, &drbg_hash_free,
+ &drbg_hash_instantiate, &drbg_hash_uninstantiate,
+ &drbg_hash_reseed, &drbg_hash_generate);
+}
+
+static void drbg_hash_free(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HASH *hash;
+
+ if (drbg != NULL && (hash = (PROV_DRBG_HASH *)drbg->data) != NULL) {
+ EVP_MD_CTX_free(hash->ctx);
+ ossl_prov_digest_reset(&hash->digest);
+ OPENSSL_secure_clear_free(hash, sizeof(*hash));
+ }
+ ossl_rand_drbg_free(drbg);
+}
+
+static int drbg_hash_get_ctx_params(void *vdrbg, OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)drbg->data;
+ const EVP_MD *md;
+ OSSL_PARAM *p;
+ int ret = 0, complete = 0;
+
+ if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete))
+ return 0;
+
+ if (complete)
+ return 1;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_DIGEST);
+ if (p != NULL) {
+ md = ossl_prov_digest_md(&hash->digest);
+ if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md)))
+ goto err;
+ }
+
+ ret = ossl_drbg_get_ctx_params(drbg, params);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_hash_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int drbg_fetch_digest_from_prov(const OSSL_PARAM params[],
+ OSSL_LIB_CTX *libctx,
+ EVP_MD **digest)
+{
+ OSSL_PROVIDER *prov = NULL;
+ const OSSL_PARAM *p;
+ EVP_MD *md = NULL;
+ int ret = 0;
+
+ if (digest == NULL)
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params,
+ OSSL_PROV_PARAM_CORE_PROV_NAME)) == NULL)
+ return 0;
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ if ((prov = ossl_provider_find(libctx, (const char *)p->data, 1)) == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST);
+ if (p == NULL) {
+ ret = 1;
+ goto done;
+ }
+
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ goto done;
+
+ md = evp_digest_fetch_from_prov(prov, (const char *)p->data, NULL);
+ if (md) {
+ EVP_MD_free(*digest);
+ *digest = md;
+ ret = 1;
+ }
+
+done:
+ ossl_provider_free(prov);
+ return ret;
+}
+
+static int drbg_hash_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *ctx = (PROV_DRBG *)vctx;
+ PROV_DRBG_HASH *hash = (PROV_DRBG_HASH *)ctx->data;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ EVP_MD *prov_md = NULL;
+ const EVP_MD *md;
+ int md_size;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+
+ /* try to fetch digest from provider */
+ (void)ERR_set_mark();
+ if (!drbg_fetch_digest_from_prov(params, libctx, &prov_md)) {
+ (void)ERR_pop_to_mark();
+ /* fall back to full implementation search */
+ if (!ossl_prov_digest_load_from_params(&hash->digest, params, libctx))
+ return 0;
+ } else {
+ (void)ERR_clear_last_mark();
+ if (prov_md)
+ ossl_prov_digest_set_md(&hash->digest, prov_md);
+ }
+
+ md = ossl_prov_digest_md(&hash->digest);
+ if (md != NULL) {
+ if (!ossl_drbg_verify_digest(ctx, libctx, md))
+ return 0; /* Error already raised for us */
+
+ /* These are taken from SP 800-90 10.1 Table 2 */
+ md_size = EVP_MD_get_size(md);
+ if (md_size <= 0)
+ return 0;
+ hash->blocklen = md_size;
+ /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */
+ ctx->strength = 64 * (hash->blocklen >> 3);
+ if (ctx->strength > 256)
+ ctx->strength = 256;
+ if (hash->blocklen > MAX_BLOCKLEN_USING_SMALL_SEEDLEN)
+ ctx->seedlen = HASH_PRNG_MAX_SEEDLEN;
+ else
+ ctx->seedlen = HASH_PRNG_SMALL_SEEDLEN;
+
+ ctx->min_entropylen = ctx->strength / 8;
+ ctx->min_noncelen = ctx->min_entropylen / 2;
+ }
+
+ return ossl_drbg_set_ctx_params(ctx, params);
+}
+
+static int drbg_hash_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vctx;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_hash_set_ctx_params_locked(vctx, params);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_hash_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON,
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_drbg_hash_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_hash_new_wrapper },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_hash_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))drbg_hash_instantiate_wrapper },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))drbg_hash_uninstantiate_wrapper },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_hash_generate_wrapper },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_hash_reseed_wrapper },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock },
+ { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_hash_settable_ctx_params },
+ { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_hash_set_ctx_params },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_hash_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_hash_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))drbg_hash_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/drbg_hmac.c b/crypto/openssl/providers/implementations/rands/drbg_hmac.c
new file mode 100644
index 000000000000..43b3f8766e3d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/drbg_hmac.c
@@ -0,0 +1,579 @@
+/*
+ * Copyright 2011-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "internal/thread_once.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/hmac_drbg.h"
+#include "drbg_local.h"
+#include "crypto/evp.h"
+#include "crypto/evp/evp_local.h"
+#include "internal/provider.h"
+
+static OSSL_FUNC_rand_newctx_fn drbg_hmac_new_wrapper;
+static OSSL_FUNC_rand_freectx_fn drbg_hmac_free;
+static OSSL_FUNC_rand_instantiate_fn drbg_hmac_instantiate_wrapper;
+static OSSL_FUNC_rand_uninstantiate_fn drbg_hmac_uninstantiate_wrapper;
+static OSSL_FUNC_rand_generate_fn drbg_hmac_generate_wrapper;
+static OSSL_FUNC_rand_reseed_fn drbg_hmac_reseed_wrapper;
+static OSSL_FUNC_rand_settable_ctx_params_fn drbg_hmac_settable_ctx_params;
+static OSSL_FUNC_rand_set_ctx_params_fn drbg_hmac_set_ctx_params;
+static OSSL_FUNC_rand_gettable_ctx_params_fn drbg_hmac_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn drbg_hmac_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn drbg_hmac_verify_zeroization;
+
+static int drbg_hmac_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[]);
+
+/*
+ * Called twice by SP800-90Ar1 10.1.2.2 HMAC_DRBG_Update_Process.
+ *
+ * hmac is an object that holds the input/output Key and Value (K and V).
+ * inbyte is 0x00 on the first call and 0x01 on the second call.
+ * in1, in2, in3 are optional inputs that can be NULL.
+ * in1len, in2len, in3len are the lengths of the input buffers.
+ *
+ * The returned K,V is:
+ * hmac->K = HMAC(hmac->K, hmac->V || inbyte || [in1] || [in2] || [in3])
+ * hmac->V = HMAC(hmac->K, hmac->V)
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int do_hmac(PROV_DRBG_HMAC *hmac, unsigned char inbyte,
+ const unsigned char *in1, size_t in1len,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *in3, size_t in3len)
+{
+ EVP_MAC_CTX *ctx = hmac->ctx;
+
+ if (!EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL)
+ /* K = HMAC(K, V || inbyte || [in1] || [in2] || [in3]) */
+ || !EVP_MAC_update(ctx, hmac->V, hmac->blocklen)
+ || !EVP_MAC_update(ctx, &inbyte, 1)
+ || !(in1 == NULL || in1len == 0 || EVP_MAC_update(ctx, in1, in1len))
+ || !(in2 == NULL || in2len == 0 || EVP_MAC_update(ctx, in2, in2len))
+ || !(in3 == NULL || in3len == 0 || EVP_MAC_update(ctx, in3, in3len))
+ || !EVP_MAC_final(ctx, hmac->K, NULL, sizeof(hmac->K)))
+ return 0;
+
+ /* V = HMAC(K, V) */
+ return EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL)
+ && EVP_MAC_update(ctx, hmac->V, hmac->blocklen)
+ && EVP_MAC_final(ctx, hmac->V, NULL, sizeof(hmac->V));
+}
+
+/*
+ * SP800-90Ar1 10.1.2.2 HMAC_DRBG_Update_Process
+ *
+ *
+ * Updates the drbg objects Key(K) and Value(V) using the following algorithm:
+ * K,V = do_hmac(hmac, 0, in1, in2, in3)
+ * if (any input is not NULL)
+ * K,V = do_hmac(hmac, 1, in1, in2, in3)
+ *
+ * where in1, in2, in3 are optional input buffers that can be NULL.
+ * in1len, in2len, in3len are the lengths of the input buffers.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int drbg_hmac_update(PROV_DRBG_HMAC *hmac,
+ const unsigned char *in1, size_t in1len,
+ const unsigned char *in2, size_t in2len,
+ const unsigned char *in3, size_t in3len)
+{
+ /* (Steps 1-2) K = HMAC(K, V||0x00||provided_data). V = HMAC(K,V) */
+ if (!do_hmac(hmac, 0x00, in1, in1len, in2, in2len, in3, in3len))
+ return 0;
+ /* (Step 3) If provided_data == NULL then return (K,V) */
+ if (in1len == 0 && in2len == 0 && in3len == 0)
+ return 1;
+ /* (Steps 4-5) K = HMAC(K, V||0x01||provided_data). V = HMAC(K,V) */
+ return do_hmac(hmac, 0x01, in1, in1len, in2, in2len, in3, in3len);
+}
+
+/*
+ * SP800-90Ar1 10.1.2.3 HMAC_DRBG_Instantiate_Process:
+ *
+ * This sets the drbg Key (K) to all zeros, and Value (V) to all 1's.
+ * and then calls (K,V) = drbg_hmac_update() with input parameters:
+ * ent = entropy data (Can be NULL) of length ent_len.
+ * nonce = nonce data (Can be NULL) of length nonce_len.
+ * pstr = personalization data (Can be NULL) of length pstr_len.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+int ossl_drbg_hmac_init(PROV_DRBG_HMAC *hmac,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *nonce, size_t nonce_len,
+ const unsigned char *pstr, size_t pstr_len)
+{
+ if (hmac->ctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_MAC);
+ return 0;
+ }
+
+ /* (Step 2) Key = 0x00 00...00 */
+ memset(hmac->K, 0x00, hmac->blocklen);
+ /* (Step 3) V = 0x01 01...01 */
+ memset(hmac->V, 0x01, hmac->blocklen);
+ /* (Step 4) (K,V) = HMAC_DRBG_Update(entropy||nonce||pers string, K, V) */
+ return drbg_hmac_update(hmac, ent, ent_len, nonce, nonce_len, pstr,
+ pstr_len);
+}
+static int drbg_hmac_instantiate(PROV_DRBG *drbg,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *nonce, size_t nonce_len,
+ const unsigned char *pstr, size_t pstr_len)
+{
+ return ossl_drbg_hmac_init((PROV_DRBG_HMAC *)drbg->data, ent, ent_len,
+ nonce, nonce_len, pstr, pstr_len);
+}
+
+static int drbg_hmac_instantiate_wrapper(void *vdrbg, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr,
+ size_t pstr_len,
+ const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ if (!ossl_prov_is_running()
+ || !drbg_hmac_set_ctx_params_locked(drbg, params))
+ goto err;
+ ret = ossl_prov_drbg_instantiate(drbg, strength, prediction_resistance,
+ pstr, pstr_len);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+
+/*
+ * SP800-90Ar1 10.1.2.4 HMAC_DRBG_Reseed_Process:
+ *
+ * Reseeds the drbg's Key (K) and Value (V) by calling
+ * (K,V) = drbg_hmac_update() with the following input parameters:
+ * ent = entropy input data (Can be NULL) of length ent_len.
+ * adin = additional input data (Can be NULL) of length adin_len.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+static int drbg_hmac_reseed(PROV_DRBG *drbg,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data;
+
+ /* (Step 2) (K,V) = HMAC_DRBG_Update(entropy||additional_input, K, V) */
+ return drbg_hmac_update(hmac, ent, ent_len, adin, adin_len, NULL, 0);
+}
+
+static int drbg_hmac_reseed_wrapper(void *vdrbg, int prediction_resistance,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_reseed(drbg, prediction_resistance, ent, ent_len,
+ adin, adin_len);
+}
+
+/*
+ * SP800-90Ar1 10.1.2.5 HMAC_DRBG_Generate_Process:
+ *
+ * Generates pseudo random bytes and updates the internal K,V for the drbg.
+ * out is a buffer to fill with outlen bytes of pseudo random data.
+ * adin is an additional_input string of size adin_len that may be NULL.
+ *
+ * Returns zero if an error occurs otherwise it returns 1.
+ */
+int ossl_drbg_hmac_generate(PROV_DRBG_HMAC *hmac,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len)
+{
+ EVP_MAC_CTX *ctx = hmac->ctx;
+ const unsigned char *temp = hmac->V;
+
+ /* (Step 2) if adin != NULL then (K,V) = HMAC_DRBG_Update(adin, K, V) */
+ if (adin != NULL
+ && adin_len > 0
+ && !drbg_hmac_update(hmac, adin, adin_len, NULL, 0, NULL, 0))
+ return 0;
+
+ /*
+ * (Steps 3-5) temp = NULL
+ * while (len(temp) < outlen) {
+ * V = HMAC(K, V)
+ * temp = temp || V
+ * }
+ */
+ for (;;) {
+ if (!EVP_MAC_init(ctx, hmac->K, hmac->blocklen, NULL)
+ || !EVP_MAC_update(ctx, temp, hmac->blocklen))
+ return 0;
+
+ if (outlen > hmac->blocklen) {
+ if (!EVP_MAC_final(ctx, out, NULL, outlen))
+ return 0;
+ temp = out;
+ } else {
+ if (!EVP_MAC_final(ctx, hmac->V, NULL, sizeof(hmac->V)))
+ return 0;
+ memcpy(out, hmac->V, outlen);
+ break;
+ }
+ out += hmac->blocklen;
+ outlen -= hmac->blocklen;
+ }
+ /* (Step 6) (K,V) = HMAC_DRBG_Update(adin, K, V) */
+ if (!drbg_hmac_update(hmac, adin, adin_len, NULL, 0, NULL, 0))
+ return 0;
+
+ return 1;
+}
+
+static int drbg_hmac_generate(PROV_DRBG *drbg,
+ unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len)
+{
+ return ossl_drbg_hmac_generate((PROV_DRBG_HMAC *)drbg->data, out, outlen,
+ adin, adin_len);
+}
+
+static int drbg_hmac_generate_wrapper(void *vdrbg,
+ unsigned char *out, size_t outlen, unsigned int strength,
+ int prediction_resistance, const unsigned char *adin, size_t adin_len)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+
+ return ossl_prov_drbg_generate(drbg, out, outlen, strength,
+ prediction_resistance, adin, adin_len);
+}
+
+static int drbg_hmac_uninstantiate(PROV_DRBG *drbg)
+{
+ PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data;
+
+ OPENSSL_cleanse(hmac->K, sizeof(hmac->K));
+ OPENSSL_cleanse(hmac->V, sizeof(hmac->V));
+ return ossl_prov_drbg_uninstantiate(drbg);
+}
+
+static int drbg_hmac_uninstantiate_wrapper(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_hmac_uninstantiate(drbg);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static int drbg_hmac_verify_zeroization(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data;
+ int ret = 0;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ PROV_DRBG_VERIFY_ZEROIZATION(hmac->K);
+ PROV_DRBG_VERIFY_ZEROIZATION(hmac->V);
+
+ ret = 1;
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+ return ret;
+}
+
+static int drbg_hmac_new(PROV_DRBG *drbg)
+{
+ PROV_DRBG_HMAC *hmac;
+
+ hmac = OPENSSL_secure_zalloc(sizeof(*hmac));
+ if (hmac == NULL)
+ return 0;
+
+ OSSL_FIPS_IND_INIT(drbg)
+
+ drbg->data = hmac;
+ /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */
+ drbg->max_entropylen = DRBG_MAX_LENGTH;
+ drbg->max_noncelen = DRBG_MAX_LENGTH;
+ drbg->max_perslen = DRBG_MAX_LENGTH;
+ drbg->max_adinlen = DRBG_MAX_LENGTH;
+
+ /* Maximum number of bits per request = 2^19 = 2^16 bytes */
+ drbg->max_request = 1 << 16;
+ return 1;
+}
+
+static void *drbg_hmac_new_wrapper(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ return ossl_rand_drbg_new(provctx, parent, parent_dispatch,
+ &drbg_hmac_new, &drbg_hmac_free,
+ &drbg_hmac_instantiate, &drbg_hmac_uninstantiate,
+ &drbg_hmac_reseed, &drbg_hmac_generate);
+}
+
+static void drbg_hmac_free(void *vdrbg)
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HMAC *hmac;
+
+ if (drbg != NULL && (hmac = (PROV_DRBG_HMAC *)drbg->data) != NULL) {
+ EVP_MAC_CTX_free(hmac->ctx);
+ ossl_prov_digest_reset(&hmac->digest);
+ OPENSSL_secure_clear_free(hmac, sizeof(*hmac));
+ }
+ ossl_rand_drbg_free(drbg);
+}
+
+static int drbg_hmac_get_ctx_params(void *vdrbg, OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vdrbg;
+ PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)drbg->data;
+ const char *name;
+ const EVP_MD *md;
+ OSSL_PARAM *p;
+ int ret = 0, complete = 0;
+
+ if (!ossl_drbg_get_ctx_params_no_lock(drbg, params, &complete))
+ return 0;
+
+ if (complete)
+ return 1;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_read_lock(drbg->lock))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_MAC);
+ if (p != NULL) {
+ if (hmac->ctx == NULL)
+ goto err;
+ name = EVP_MAC_get0_name(EVP_MAC_CTX_get0_mac(hmac->ctx));
+ if (!OSSL_PARAM_set_utf8_string(p, name))
+ goto err;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_DRBG_PARAM_DIGEST);
+ if (p != NULL) {
+ md = ossl_prov_digest_md(&hmac->digest);
+ if (md == NULL || !OSSL_PARAM_set_utf8_string(p, EVP_MD_get0_name(md)))
+ goto err;
+ }
+
+ ret = ossl_drbg_get_ctx_params(drbg, params);
+ err:
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_hmac_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_MAC, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON,
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int drbg_fetch_algs_from_prov(const OSSL_PARAM params[],
+ OSSL_LIB_CTX *libctx,
+ EVP_MAC_CTX **macctx,
+ EVP_MD **digest)
+{
+ OSSL_PROVIDER *prov = NULL;
+ const OSSL_PARAM *p;
+ EVP_MD *md = NULL;
+ EVP_MAC *mac = NULL;
+ int ret = 0;
+
+ if (macctx == NULL || digest == NULL)
+ return 0;
+
+ if ((p = OSSL_PARAM_locate_const(params,
+ OSSL_PROV_PARAM_CORE_PROV_NAME)) == NULL)
+ return 0;
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ return 0;
+ if ((prov = ossl_provider_find(libctx, (const char *)p->data, 1)) == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_DIGEST);
+ if (p) {
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ goto done;
+
+ md = evp_digest_fetch_from_prov(prov, (const char *)p->data, NULL);
+ if (md) {
+ EVP_MD_free(*digest);
+ *digest = md;
+ } else {
+ goto done;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_ALG_PARAM_MAC);
+ if (p == NULL) {
+ ret = 1;
+ goto done;
+ }
+
+ if (p->data_type != OSSL_PARAM_UTF8_STRING)
+ goto done;
+
+ EVP_MAC_CTX_free(*macctx);
+ *macctx = NULL;
+
+ mac = evp_mac_fetch_from_prov(prov, (const char *)p->data, NULL);
+ if (mac) {
+ *macctx = EVP_MAC_CTX_new(mac);
+ /* The context holds on to the MAC */
+ EVP_MAC_free(mac);
+ ret = 1;
+ }
+
+done:
+ ossl_provider_free(prov);
+ return ret;
+}
+
+static int drbg_hmac_set_ctx_params_locked(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *ctx = (PROV_DRBG *)vctx;
+ PROV_DRBG_HMAC *hmac = (PROV_DRBG_HMAC *)ctx->data;
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(ctx->provctx);
+ EVP_MD *prov_md = NULL;
+ const EVP_MD *md;
+ int md_size;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+
+ /* try to fetch mac and digest from provider */
+ (void)ERR_set_mark();
+ if (!drbg_fetch_algs_from_prov(params, libctx, &hmac->ctx, &prov_md)) {
+ (void)ERR_pop_to_mark();
+ /* fall back to full implementation search */
+ if (!ossl_prov_digest_load_from_params(&hmac->digest, params, libctx))
+ return 0;
+
+ if (!ossl_prov_macctx_load_from_params(&hmac->ctx, params,
+ NULL, NULL, NULL, libctx))
+ return 0;
+ } else {
+ (void)ERR_clear_last_mark();
+ if (prov_md)
+ ossl_prov_digest_set_md(&hmac->digest, prov_md);
+ }
+
+ md = ossl_prov_digest_md(&hmac->digest);
+ if (md != NULL && !ossl_drbg_verify_digest(ctx, libctx, md))
+ return 0; /* Error already raised for us */
+
+ if (md != NULL && hmac->ctx != NULL) {
+ /* These are taken from SP 800-90 10.1 Table 2 */
+ md_size = EVP_MD_get_size(md);
+ if (md_size <= 0)
+ return 0;
+ hmac->blocklen = (size_t)md_size;
+ /* See SP800-57 Part1 Rev4 5.6.1 Table 3 */
+ ctx->strength = 64 * (int)(hmac->blocklen >> 3);
+ if (ctx->strength > 256)
+ ctx->strength = 256;
+ ctx->seedlen = hmac->blocklen;
+ ctx->min_entropylen = ctx->strength / 8;
+ ctx->min_noncelen = ctx->min_entropylen / 2;
+ }
+
+ return ossl_drbg_set_ctx_params(ctx, params);
+}
+
+static int drbg_hmac_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_DRBG *drbg = (PROV_DRBG *)vctx;
+ int ret;
+
+ if (drbg->lock != NULL && !CRYPTO_THREAD_write_lock(drbg->lock))
+ return 0;
+
+ ret = drbg_hmac_set_ctx_params_locked(vctx, params);
+
+ if (drbg->lock != NULL)
+ CRYPTO_THREAD_unlock(drbg->lock);
+
+ return ret;
+}
+
+static const OSSL_PARAM *drbg_hmac_settable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *p_ctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_DRBG_PARAM_MAC, NULL, 0),
+ OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON,
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_DRBG_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_drbg_ossl_hmac_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))drbg_hmac_new_wrapper },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))drbg_hmac_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))drbg_hmac_instantiate_wrapper },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))drbg_hmac_uninstantiate_wrapper },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))drbg_hmac_generate_wrapper },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))drbg_hmac_reseed_wrapper },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))ossl_drbg_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))ossl_drbg_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))ossl_drbg_unlock },
+ { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_hmac_settable_ctx_params },
+ { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))drbg_hmac_set_ctx_params },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))drbg_hmac_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))drbg_hmac_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))drbg_hmac_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))ossl_drbg_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))ossl_drbg_clear_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/drbg_local.h b/crypto/openssl/providers/implementations/rands/drbg_local.h
new file mode 100644
index 000000000000..6cfd43db2d1d
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/drbg_local.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_CRYPTO_PROV_LOCAL_H
+# define OSSL_CRYPTO_PROV_LOCAL_H
+
+# include <openssl/evp.h>
+# include <openssl/core_dispatch.h>
+# include <openssl/core_names.h>
+# include <openssl/params.h>
+# include "internal/tsan_assist.h"
+# include "internal/nelem.h"
+# include "internal/numbers.h"
+# include "prov/provider_ctx.h"
+# include "prov/securitycheck.h"
+
+/* How many times to read the TSC as a randomness source. */
+# define TSC_READ_COUNT 4
+
+/* Maximum reseed intervals */
+# define MAX_RESEED_INTERVAL (1 << 24)
+# define MAX_RESEED_TIME_INTERVAL (1 << 20) /* approx. 12 days */
+
+/* Default reseed intervals */
+# define RESEED_INTERVAL (1 << 8)
+# define TIME_INTERVAL (60*60) /* 1 hour */
+
+/*
+ * Maximum input size for the DRBG (entropy, nonce, personalization string)
+ *
+ * NIST SP800 90Ar1 allows a maximum of (1 << 35) bits i.e., (1 << 32) bytes.
+ *
+ * We lower it to 'only' INT32_MAX bytes, which is equivalent to 2 gigabytes.
+ */
+# define DRBG_MAX_LENGTH INT32_MAX
+
+/* The default nonce */
+/* ASCII: "OpenSSL NIST SP 800-90A DRBG", in hex for EBCDIC compatibility */
+#define DRBG_DEFAULT_PERS_STRING "\x4f\x70\x65\x6e\x53\x53\x4c\x20\x4e\x49\x53\x54\x20\x53\x50\x20\x38\x30\x30\x2d\x39\x30\x41\x20\x44\x52\x42\x47"
+
+typedef struct prov_drbg_st PROV_DRBG;
+
+/* DRBG status values */
+typedef enum drbg_status_e {
+ DRBG_UNINITIALISED,
+ DRBG_READY,
+ DRBG_ERROR
+} DRBG_STATUS;
+
+/*
+ * The state of all types of DRBGs.
+ */
+struct prov_drbg_st {
+ CRYPTO_RWLOCK *lock;
+ PROV_CTX *provctx;
+
+ /* Virtual functions are cached here */
+ int (*instantiate)(PROV_DRBG *drbg,
+ const unsigned char *entropy, size_t entropylen,
+ const unsigned char *nonce, size_t noncelen,
+ const unsigned char *pers, size_t perslen);
+ int (*uninstantiate)(PROV_DRBG *ctx);
+ int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len);
+ int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len);
+
+ /* Parent PROV_RAND and its dispatch table functions */
+ void *parent;
+ OSSL_FUNC_rand_enable_locking_fn *parent_enable_locking;
+ OSSL_FUNC_rand_lock_fn *parent_lock;
+ OSSL_FUNC_rand_unlock_fn *parent_unlock;
+ OSSL_FUNC_rand_get_ctx_params_fn *parent_get_ctx_params;
+ OSSL_FUNC_rand_nonce_fn *parent_nonce;
+ OSSL_FUNC_rand_get_seed_fn *parent_get_seed;
+ OSSL_FUNC_rand_clear_seed_fn *parent_clear_seed;
+
+ /*
+ * Stores the return value of openssl_get_fork_id() as of when we last
+ * reseeded. The DRBG reseeds automatically whenever drbg->fork_id !=
+ * openssl_get_fork_id(). Used to provide fork-safety and reseed this
+ * DRBG in the child process.
+ */
+ int fork_id;
+ unsigned short flags; /* various external flags */
+
+ /*
+ * The following parameters are setup by the per-type "init" function.
+ *
+ * The supported types and their init functions are:
+ * (1) CTR_DRBG: drbg_ctr_init().
+ * (2) HMAC_DRBG: drbg_hmac_init().
+ * (3) HASH_DRBG: drbg_hash_init().
+ *
+ * The parameters are closely related to the ones described in
+ * section '10.2.1 CTR_DRBG' of [NIST SP 800-90Ar1], with one
+ * crucial difference: In the NIST standard, all counts are given
+ * in bits, whereas in OpenSSL entropy counts are given in bits
+ * and buffer lengths are given in bytes.
+ *
+ * Since this difference has lead to some confusion in the past,
+ * (see [GitHub Issue #2443], formerly [rt.openssl.org #4055])
+ * the 'len' suffix has been added to all buffer sizes for
+ * clarification.
+ */
+
+ unsigned int strength;
+ size_t max_request;
+ size_t min_entropylen, max_entropylen;
+ size_t min_noncelen, max_noncelen;
+ size_t max_perslen, max_adinlen;
+
+ /*
+ * Counts the number of generate requests since the last reseed
+ * (Starts at 1). This value is the reseed_counter as defined in
+ * NIST SP 800-90Ar1
+ */
+ unsigned int generate_counter;
+ /*
+ * Maximum number of generate requests until a reseed is required.
+ * This value is ignored if it is zero.
+ */
+ unsigned int reseed_interval;
+ /* Stores the time when the last reseeding occurred */
+ time_t reseed_time;
+ /*
+ * Specifies the maximum time interval (in seconds) between reseeds.
+ * This value is ignored if it is zero.
+ */
+ time_t reseed_time_interval;
+ /*
+ * Counts the number of reseeds since instantiation.
+ * This value is ignored if it is zero.
+ *
+ * This counter is used only for seed propagation from the <master> DRBG
+ * to its two children, the <public> and <private> DRBG. This feature is
+ * very special and its sole purpose is to ensure that any randomness which
+ * is added by PROV_add() or PROV_seed() will have an immediate effect on
+ * the output of PROV_bytes() resp. PROV_priv_bytes().
+ */
+ TSAN_QUALIFIER unsigned int reseed_counter;
+ unsigned int reseed_next_counter;
+ unsigned int parent_reseed_counter;
+
+ size_t seedlen;
+ DRBG_STATUS state;
+
+ /* DRBG specific data */
+ void *data;
+
+ /* Entropy and nonce gathering callbacks */
+ void *callback_arg;
+ OSSL_INOUT_CALLBACK *get_entropy_fn;
+ OSSL_CALLBACK *cleanup_entropy_fn;
+ OSSL_INOUT_CALLBACK *get_nonce_fn;
+ OSSL_CALLBACK *cleanup_nonce_fn;
+
+ OSSL_FIPS_IND_DECLARE
+};
+
+PROV_DRBG *ossl_rand_drbg_new
+ (void *provctx, void *parent, const OSSL_DISPATCH *parent_dispatch,
+ int (*dnew)(PROV_DRBG *ctx),
+ void (*dfree)(void *vctx),
+ int (*instantiate)(PROV_DRBG *drbg,
+ const unsigned char *entropy, size_t entropylen,
+ const unsigned char *nonce, size_t noncelen,
+ const unsigned char *pers, size_t perslen),
+ int (*uninstantiate)(PROV_DRBG *ctx),
+ int (*reseed)(PROV_DRBG *drbg, const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adin_len),
+ int (*generate)(PROV_DRBG *, unsigned char *out, size_t outlen,
+ const unsigned char *adin, size_t adin_len));
+void ossl_rand_drbg_free(PROV_DRBG *drbg);
+
+int ossl_prov_drbg_instantiate(PROV_DRBG *drbg, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pers, size_t perslen);
+
+int ossl_prov_drbg_uninstantiate(PROV_DRBG *drbg);
+
+int ossl_prov_drbg_reseed(PROV_DRBG *drbg, int prediction_resistance,
+ const unsigned char *ent, size_t ent_len,
+ const unsigned char *adin, size_t adinlen);
+
+int ossl_prov_drbg_generate(PROV_DRBG *drbg, unsigned char *out, size_t outlen,
+ unsigned int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adinlen);
+
+/* Seeding api */
+OSSL_FUNC_rand_get_seed_fn ossl_drbg_get_seed;
+OSSL_FUNC_rand_clear_seed_fn ossl_drbg_clear_seed;
+
+/* Verify that an array of numeric values is all zero */
+#define PROV_DRBG_VERIFY_ZEROIZATION(v) \
+ { \
+ size_t i; \
+ \
+ for (i = 0; i < OSSL_NELEM(v); i++) \
+ if ((v)[i] != 0) \
+ goto err; \
+ }
+
+/* locking api */
+OSSL_FUNC_rand_enable_locking_fn ossl_drbg_enable_locking;
+OSSL_FUNC_rand_lock_fn ossl_drbg_lock;
+OSSL_FUNC_rand_unlock_fn ossl_drbg_unlock;
+
+/* Common parameters for all of our DRBGs */
+int ossl_drbg_get_ctx_params(PROV_DRBG *drbg, OSSL_PARAM params[]);
+int ossl_drbg_get_ctx_params_no_lock(PROV_DRBG *drbg, OSSL_PARAM params[],
+ int *complete);
+int ossl_drbg_set_ctx_params(PROV_DRBG *drbg, const OSSL_PARAM params[]);
+
+#define OSSL_PARAM_DRBG_SETTABLE_CTX_COMMON \
+ OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_REQUESTS, NULL), \
+ OSSL_PARAM_uint64(OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL, NULL)
+
+#define OSSL_PARAM_DRBG_GETTABLE_CTX_COMMON \
+ OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL), \
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL), \
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MIN_ENTROPYLEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_ENTROPYLEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MIN_NONCELEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_NONCELEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_PERSLEN, NULL), \
+ OSSL_PARAM_size_t(OSSL_DRBG_PARAM_MAX_ADINLEN, NULL), \
+ OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_COUNTER, NULL), \
+ OSSL_PARAM_time_t(OSSL_DRBG_PARAM_RESEED_TIME, NULL), \
+ OSSL_PARAM_uint(OSSL_DRBG_PARAM_RESEED_REQUESTS, NULL), \
+ OSSL_PARAM_uint64(OSSL_DRBG_PARAM_RESEED_TIME_INTERVAL, NULL)
+
+/* Confirm digest is allowed to be used with a DRBG */
+int ossl_drbg_verify_digest(PROV_DRBG *drbg, OSSL_LIB_CTX *libctx, const EVP_MD *md);
+
+#endif
diff --git a/crypto/openssl/providers/implementations/rands/fips_crng_test.c b/crypto/openssl/providers/implementations/rands/fips_crng_test.c
new file mode 100644
index 000000000000..209a1adb2785
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/fips_crng_test.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * Implementation of SP 800-90B section 4.4 Approved Continuous Health Tests.
+ */
+
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/params.h>
+#include <openssl/self_test.h>
+#include <openssl/proverr.h>
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/implementations.h"
+#include "internal/cryptlib.h"
+#include "crypto/rand_pool.h"
+#include "drbg_local.h"
+#include "prov/seeding.h"
+#include "crypto/context.h"
+
+static OSSL_FUNC_rand_newctx_fn crng_test_new;
+static OSSL_FUNC_rand_freectx_fn crng_test_free;
+static OSSL_FUNC_rand_instantiate_fn crng_test_instantiate;
+static OSSL_FUNC_rand_uninstantiate_fn crng_test_uninstantiate;
+static OSSL_FUNC_rand_generate_fn crng_test_generate;
+static OSSL_FUNC_rand_reseed_fn crng_test_reseed;
+static OSSL_FUNC_rand_gettable_ctx_params_fn crng_test_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn crng_test_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn crng_test_verify_zeroization;
+static OSSL_FUNC_rand_enable_locking_fn crng_test_enable_locking;
+static OSSL_FUNC_rand_lock_fn crng_test_lock;
+static OSSL_FUNC_rand_unlock_fn crng_test_unlock;
+static OSSL_FUNC_rand_get_seed_fn crng_test_get_seed;
+static OSSL_FUNC_rand_clear_seed_fn crng_test_clear_seed;
+
+#ifndef ENTROPY_H
+# define ENTROPY_H 6 /* default to six bits per byte of entropy */
+#endif
+#ifndef ENTROPY_APT_W
+# define ENTROPY_APT_W 512
+#endif
+
+typedef struct crng_testal_st {
+ void *provctx;
+ CRYPTO_RWLOCK *lock;
+ int state;
+
+ /* State for SP 800-90B 4.4.1 Repetition Count Test */
+ struct {
+ unsigned int b;
+ uint8_t a;
+ } rct;
+
+ /* State for SP 800-90B 4.4.2 Adaptive Proportion Test */
+ struct {
+ unsigned int b;
+ unsigned int i;
+ uint8_t a;
+ } apt;
+
+ /* Parent PROV_RAND and its dispatch table functions */
+ void *parent;
+ OSSL_FUNC_rand_enable_locking_fn *parent_enable_locking;
+ OSSL_FUNC_rand_lock_fn *parent_lock;
+ OSSL_FUNC_rand_unlock_fn *parent_unlock;
+ OSSL_FUNC_rand_get_ctx_params_fn *parent_get_ctx_params;
+ OSSL_FUNC_rand_gettable_ctx_params_fn *parent_gettable_ctx_params;
+ OSSL_FUNC_rand_get_seed_fn *parent_get_seed;
+ OSSL_FUNC_rand_clear_seed_fn *parent_clear_seed;
+} CRNG_TEST;
+
+/*
+ * Some helper functions
+ */
+static int lock_parent(CRNG_TEST *crngt)
+{
+ void *parent = crngt->parent;
+
+ if (parent != NULL
+ && crngt->parent_lock != NULL
+ && !crngt->parent_lock(parent)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED);
+ return 0;
+ }
+ return 1;
+}
+
+static void unlock_parent(CRNG_TEST *crngt)
+{
+ void *parent = crngt->parent;
+
+ if (parent != NULL && crngt->parent_unlock != NULL)
+ crngt->parent_unlock(parent);
+}
+
+/*
+ * Implementation of SP 800-90B section 4.4.1: Repetition Count Test
+ */
+static int RCT_test(CRNG_TEST *crngt, uint8_t next)
+{
+ /*
+ * Critical values for this test are computed using:
+ *
+ * C = 1 + \left\lceil\frac{-log_2 \alpha}H\right\rceil
+ *
+ * where alpha = 2^-20 and H is the expected entropy per sample.
+ */
+ static const unsigned int rct_c[9] = {
+ 41, /* H = 0.5 */
+ 21, 11, 8, 6, 5, 5, 4, 4 /* H = 1, ..., 8 */
+ };
+
+ if (ossl_likely(crngt->rct.b != 0)
+ && ossl_unlikely(next == crngt->rct.a))
+ return ossl_likely(++crngt->rct.b < rct_c[ENTROPY_H]);
+ crngt->rct.a = next;
+ crngt->rct.b = 1;
+ return 1;
+}
+
+/*
+ * Implementation of SP 800-90B section 4.4.2: Adaptive Proportion Test
+ */
+static int APT_test(CRNG_TEST *crngt, uint8_t next)
+{
+ /*
+ * Critical values for this test are drawn from a binomial
+ * distribution with n = 512, p = 2^-H at a critical threshold of
+ * 2^-20. H being the expected entropy per sample. Refer SP 800-90B
+ * section 4.4.2, table 2.
+ */
+ static const unsigned int apt_c[9] = {
+ 410, /* H = 0.5 */
+ 311, 177, 103, 62, 39, 25, 18, 13 /* H = 1, ..., 8 */
+ };
+
+ if (ossl_likely(crngt->apt.b != 0)) {
+ if (ossl_unlikely(crngt->apt.a == next)
+ && ossl_unlikely(++crngt->apt.b >= apt_c[ENTROPY_H])) {
+ crngt->apt.b = 0;
+ return 0;
+ }
+ if (ossl_unlikely(++crngt->apt.i >= ENTROPY_APT_W))
+ crngt->apt.b = 0;
+ return 1;
+ }
+ crngt->apt.a = next;
+ crngt->apt.b = 1;
+ crngt->apt.i = 1;
+ return 1;
+}
+
+static int crng_test(CRNG_TEST *crngt, const unsigned char *buf, size_t n)
+{
+ size_t i;
+
+ for (i = 0; i < n; i++)
+ if (!RCT_test(crngt, buf[i]) || !APT_test(crngt, buf[i])) {
+ crngt->state = EVP_RAND_STATE_ERROR;
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_ENTROPY_SOURCE_FAILED_CONTINUOUS_TESTS);
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_DISPATCH *find_call(const OSSL_DISPATCH *dispatch,
+ int function)
+{
+ if (dispatch != NULL)
+ while (dispatch->function_id != 0) {
+ if (dispatch->function_id == function)
+ return dispatch;
+ dispatch++;
+ }
+ return NULL;
+}
+
+static void *crng_test_new(void *provctx, void *parent,
+ const OSSL_DISPATCH *p_dispatch)
+{
+ CRNG_TEST *crngt = OPENSSL_zalloc(sizeof(*crngt));
+ const OSSL_DISPATCH *pfunc;
+
+ if (crngt == NULL)
+ return NULL;
+
+ crngt->provctx = provctx;
+ crngt->state = EVP_RAND_STATE_UNINITIALISED;
+
+ /* Extract parent's functions */
+ if (parent != NULL) {
+ crngt->parent = parent;
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_ENABLE_LOCKING)) != NULL)
+ crngt->parent_enable_locking = OSSL_FUNC_rand_enable_locking(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_LOCK)) != NULL)
+ crngt->parent_lock = OSSL_FUNC_rand_lock(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_UNLOCK)) != NULL)
+ crngt->parent_unlock = OSSL_FUNC_rand_unlock(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS)) != NULL)
+ crngt->parent_gettable_ctx_params = OSSL_FUNC_rand_gettable_ctx_params(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_CTX_PARAMS)) != NULL)
+ crngt->parent_get_ctx_params = OSSL_FUNC_rand_get_ctx_params(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_GET_SEED)) != NULL)
+ crngt->parent_get_seed = OSSL_FUNC_rand_get_seed(pfunc);
+ if ((pfunc = find_call(p_dispatch, OSSL_FUNC_RAND_CLEAR_SEED)) != NULL)
+ crngt->parent_clear_seed = OSSL_FUNC_rand_clear_seed(pfunc);
+ }
+
+ return crngt;
+}
+
+static void crng_test_free(void *vcrngt)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ if (crngt != NULL) {
+ CRYPTO_THREAD_lock_free(crngt->lock);
+ OPENSSL_free(crngt);
+ }
+}
+
+static int crng_test_instantiate(void *vcrngt, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr,
+ size_t pstr_len,
+ ossl_unused const OSSL_PARAM params[])
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ /* Start up health tests should go here */
+ crngt->state = EVP_RAND_STATE_READY;
+ return 1;
+}
+
+static int crng_test_uninstantiate(void *vcrngt)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ crngt->state = EVP_RAND_STATE_UNINITIALISED;
+ return 1;
+}
+
+static int crng_test_generate(void *vcrngt, unsigned char *out, size_t outlen,
+ unsigned int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adin_len)
+{
+ unsigned char *p;
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ if (!crng_test_get_seed(crngt, &p, 0, outlen, outlen, prediction_resistance,
+ adin, adin_len))
+ return 0;
+ memcpy(out, p, outlen);
+ crng_test_clear_seed(crngt, p, outlen);
+ return 1;
+}
+
+static int crng_test_reseed(ossl_unused void *vcrngt,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *ent,
+ ossl_unused size_t ent_len,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ return 1;
+}
+
+static int crng_test_verify_zeroization(ossl_unused void *vcrngt)
+{
+ return 1;
+}
+
+static size_t crng_test_get_seed(void *vcrngt, unsigned char **pout,
+ int entropy, size_t min_len,
+ size_t max_len,
+ int prediction_resistance,
+ const unsigned char *adin,
+ size_t adin_len)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+ size_t n;
+ int r = 0;
+
+ /* Without a parent, we rely on the up calls */
+ if (crngt->parent == NULL
+ || crngt->parent_get_seed == NULL) {
+ n = ossl_prov_get_entropy(crngt->provctx, pout, entropy,
+ min_len, max_len);
+ if (n == 0)
+ return 0;
+ r = crng_test(crngt, *pout, n);
+ return r > 0 ? n : 0;
+ }
+
+ /* Grab seed from our parent */
+ if (!lock_parent(crngt))
+ return 0;
+
+ n = crngt->parent_get_seed(crngt->parent, pout, entropy,
+ min_len, max_len, prediction_resistance,
+ adin, adin_len);
+ if (n > 0 && crng_test(crngt, *pout, n) > 0)
+ r = n;
+ else if (crngt->parent_clear_seed != NULL)
+ crngt->parent_clear_seed(crngt->parent, *pout, n);
+ unlock_parent(crngt);
+ return r;
+}
+
+static void crng_test_clear_seed(void *vcrngt,
+ unsigned char *out, size_t outlen)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ if (crngt->parent == NULL || crngt->parent_get_seed == NULL)
+ ossl_prov_cleanup_entropy(crngt->provctx, out, outlen);
+ else if (crngt->parent_clear_seed != NULL)
+ crngt->parent_clear_seed(crngt->parent, out, outlen);
+}
+
+static int crng_test_enable_locking(void *vcrngt)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ if (crngt != NULL && crngt->lock == NULL) {
+ if (crngt->parent_enable_locking != NULL)
+ if (!crngt->parent_enable_locking(crngt->parent)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_PARENT_LOCKING_NOT_ENABLED);
+ return 0;
+ }
+ crngt->lock = CRYPTO_THREAD_lock_new();
+ if (crngt->lock == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_CREATE_LOCK);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int crng_test_lock(ossl_unused void *vcrngt)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ return crngt->lock == NULL || CRYPTO_THREAD_write_lock(crngt->lock);
+}
+
+static void crng_test_unlock(ossl_unused void *vcrngt)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+
+ if (crngt->lock != NULL)
+ CRYPTO_THREAD_unlock(crngt->lock);
+}
+
+static int crng_test_get_ctx_params(void *vcrngt, OSSL_PARAM params[])
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+ OSSL_PARAM *p;
+
+ if (crngt->parent != NULL && crngt->parent_get_ctx_params != NULL)
+ return crngt->parent_get_ctx_params(crngt->parent, params);
+
+ /* No parent means we are using call backs for entropy */
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE);
+ if (p != NULL && !OSSL_PARAM_set_int(p, crngt->state))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_set_int(p, 1024))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, 128))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR);
+ if (p != NULL && !OSSL_PARAM_set_int(p, 0))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *crng_test_gettable_ctx_params(void *vcrngt,
+ void *provctx)
+{
+ CRNG_TEST *crngt = (CRNG_TEST *)vcrngt;
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+ OSSL_PARAM_int(OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR, NULL),
+ OSSL_PARAM_END
+ };
+
+ if (crngt->parent != NULL && crngt->parent_gettable_ctx_params != NULL)
+ return crngt->parent_gettable_ctx_params(crngt->parent, provctx);
+ return known_gettable_ctx_params;
+}
+
+const OSSL_DISPATCH ossl_crng_test_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))crng_test_new },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))crng_test_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))crng_test_instantiate },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))crng_test_uninstantiate },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))crng_test_generate },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))crng_test_reseed },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))crng_test_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))crng_test_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))crng_test_unlock },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))crng_test_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))crng_test_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))crng_test_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))crng_test_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))crng_test_clear_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/seed_src.c b/crypto/openssl/providers/implementations/rands/seed_src.c
new file mode 100644
index 000000000000..1faab39138d2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seed_src.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2020-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/rand.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/e_os2.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/randerr.h>
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "crypto/rand.h"
+#include "crypto/rand_pool.h"
+
+static OSSL_FUNC_rand_newctx_fn seed_src_new;
+static OSSL_FUNC_rand_freectx_fn seed_src_free;
+static OSSL_FUNC_rand_instantiate_fn seed_src_instantiate;
+static OSSL_FUNC_rand_uninstantiate_fn seed_src_uninstantiate;
+static OSSL_FUNC_rand_generate_fn seed_src_generate;
+static OSSL_FUNC_rand_reseed_fn seed_src_reseed;
+static OSSL_FUNC_rand_gettable_ctx_params_fn seed_src_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn seed_src_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn seed_src_verify_zeroization;
+static OSSL_FUNC_rand_enable_locking_fn seed_src_enable_locking;
+static OSSL_FUNC_rand_lock_fn seed_src_lock;
+static OSSL_FUNC_rand_unlock_fn seed_src_unlock;
+static OSSL_FUNC_rand_get_seed_fn seed_get_seed;
+static OSSL_FUNC_rand_clear_seed_fn seed_clear_seed;
+
+typedef struct {
+ void *provctx;
+ int state;
+} PROV_SEED_SRC;
+
+static void *seed_src_new(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ PROV_SEED_SRC *s;
+
+ if (parent != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT);
+ return NULL;
+ }
+
+ s = OPENSSL_zalloc(sizeof(*s));
+ if (s == NULL)
+ return NULL;
+
+ s->provctx = provctx;
+ s->state = EVP_RAND_STATE_UNINITIALISED;
+ return s;
+}
+
+static void seed_src_free(void *vseed)
+{
+ OPENSSL_free(vseed);
+}
+
+static int seed_src_instantiate(void *vseed, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr, size_t pstr_len,
+ ossl_unused const OSSL_PARAM params[])
+{
+ PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed;
+
+ s->state = EVP_RAND_STATE_READY;
+ return 1;
+}
+
+static int seed_src_uninstantiate(void *vseed)
+{
+ PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed;
+
+ s->state = EVP_RAND_STATE_UNINITIALISED;
+ return 1;
+}
+
+static int seed_src_generate(void *vseed, unsigned char *out, size_t outlen,
+ unsigned int strength,
+ ossl_unused int prediction_resistance,
+ const unsigned char *adin,
+ size_t adin_len)
+{
+ PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed;
+ size_t entropy_available;
+ RAND_POOL *pool;
+
+ if (s->state != EVP_RAND_STATE_READY) {
+ ERR_raise(ERR_LIB_PROV,
+ s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE
+ : PROV_R_NOT_INSTANTIATED);
+ return 0;
+ }
+
+ pool = ossl_rand_pool_new(strength, 1, outlen, outlen);
+ if (pool == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB);
+ return 0;
+ }
+
+ /* Get entropy by polling system entropy sources. */
+ entropy_available = ossl_pool_acquire_entropy(pool);
+
+ if (entropy_available > 0) {
+ if (!ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) {
+ ossl_rand_pool_free(pool);
+ return 0;
+ }
+ memcpy(out, ossl_rand_pool_buffer(pool), ossl_rand_pool_length(pool));
+ }
+
+ ossl_rand_pool_free(pool);
+ return entropy_available > 0;
+}
+
+static int seed_src_reseed(void *vseed,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *ent,
+ ossl_unused size_t ent_len,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed;
+
+ if (s->state != EVP_RAND_STATE_READY) {
+ ERR_raise(ERR_LIB_PROV,
+ s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE
+ : PROV_R_NOT_INSTANTIATED);
+ return 0;
+ }
+ return 1;
+}
+
+static int seed_src_get_ctx_params(void *vseed, OSSL_PARAM params[])
+{
+ PROV_SEED_SRC *s = (PROV_SEED_SRC *)vseed;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE);
+ if (p != NULL && !OSSL_PARAM_set_int(p, s->state))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_set_int(p, 1024))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, 128))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *seed_src_gettable_ctx_params(ossl_unused void *vseed,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int seed_src_verify_zeroization(ossl_unused void *vseed)
+{
+ return 1;
+}
+
+static size_t seed_get_seed(void *vseed, unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len,
+ int prediction_resistance,
+ const unsigned char *adin, size_t adin_len)
+{
+ size_t ret = 0;
+ size_t entropy_available = 0;
+ RAND_POOL *pool;
+
+ pool = ossl_rand_pool_new(entropy, 1, min_len, max_len);
+ if (pool == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB);
+ return 0;
+ }
+
+ /* Get entropy by polling system entropy sources. */
+ entropy_available = ossl_pool_acquire_entropy(pool);
+
+ if (entropy_available > 0
+ && ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) {
+ ret = ossl_rand_pool_length(pool);
+ *pout = ossl_rand_pool_detach(pool);
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK);
+ }
+ ossl_rand_pool_free(pool);
+ return ret;
+}
+
+static void seed_clear_seed(ossl_unused void *vdrbg,
+ unsigned char *out, size_t outlen)
+{
+ OPENSSL_secure_clear_free(out, outlen);
+}
+
+static int seed_src_enable_locking(ossl_unused void *vseed)
+{
+ return 1;
+}
+
+int seed_src_lock(ossl_unused void *vctx)
+{
+ return 1;
+}
+
+void seed_src_unlock(ossl_unused void *vctx)
+{
+}
+
+const OSSL_DISPATCH ossl_seed_src_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))seed_src_new },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))seed_src_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))seed_src_instantiate },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))seed_src_uninstantiate },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))seed_src_generate },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))seed_src_reseed },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))seed_src_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))seed_src_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))seed_src_unlock },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))seed_src_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))seed_src_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))seed_src_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))seed_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))seed_clear_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/rands/seed_src_jitter.c b/crypto/openssl/providers/implementations/rands/seed_src_jitter.c
new file mode 100644
index 000000000000..ac57f1c14bff
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seed_src_jitter.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/rand.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/e_os2.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/randerr.h>
+#include <openssl/proverr.h>
+#include <openssl/self_test.h>
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "crypto/rand.h"
+#include "crypto/rand_pool.h"
+
+#ifndef OPENSSL_NO_JITTER
+# include <jitterentropy.h>
+
+# define JITTER_MAX_NUM_TRIES 3
+
+static OSSL_FUNC_rand_newctx_fn jitter_new;
+static OSSL_FUNC_rand_freectx_fn jitter_free;
+static OSSL_FUNC_rand_instantiate_fn jitter_instantiate;
+static OSSL_FUNC_rand_uninstantiate_fn jitter_uninstantiate;
+static OSSL_FUNC_rand_generate_fn jitter_generate;
+static OSSL_FUNC_rand_reseed_fn jitter_reseed;
+static OSSL_FUNC_rand_gettable_ctx_params_fn jitter_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn jitter_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn jitter_verify_zeroization;
+static OSSL_FUNC_rand_enable_locking_fn jitter_enable_locking;
+static OSSL_FUNC_rand_lock_fn jitter_lock;
+static OSSL_FUNC_rand_unlock_fn jitter_unlock;
+static OSSL_FUNC_rand_get_seed_fn jitter_get_seed;
+static OSSL_FUNC_rand_clear_seed_fn jitter_clear_seed;
+
+typedef struct {
+ void *provctx;
+ int state;
+} PROV_JITTER;
+
+static size_t get_jitter_random_value(PROV_JITTER *s, unsigned char *buf, size_t len);
+
+/*
+ * Acquire entropy from jitterentropy library
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
+ */
+static size_t ossl_prov_acquire_entropy_from_jitter(PROV_JITTER *s,
+ RAND_POOL *pool)
+{
+ size_t bytes_needed;
+ unsigned char *buffer;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /* entropy_factor */);
+ if (bytes_needed > 0) {
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+
+ if (buffer != NULL) {
+ if (get_jitter_random_value(s, buffer, bytes_needed) == bytes_needed) {
+ ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed);
+ } else {
+ ossl_rand_pool_add_end(pool, 0, 0);
+ }
+ }
+ }
+
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+/* Obtain random bytes from the jitter library */
+static size_t get_jitter_random_value(PROV_JITTER *s,
+ unsigned char *buf, size_t len)
+{
+ struct rand_data *jitter_ec = NULL;
+ ssize_t result = 0;
+ size_t num_tries;
+
+ /* Retry intermittent failures, then give up */
+ for (num_tries = 0; num_tries < JITTER_MAX_NUM_TRIES; num_tries++) {
+ /* Allocate a fresh collector */
+ jitter_ec = jent_entropy_collector_alloc(0, JENT_FORCE_FIPS);
+ if (jitter_ec == NULL)
+ continue;
+
+ /* Do not use _safe API as per typical security policies */
+ result = jent_read_entropy(jitter_ec, (char *) buf, len);
+ jent_entropy_collector_free(jitter_ec);
+
+ /*
+ * Permanent Failure
+ * https://github.com/smuellerDD/jitterentropy-library/blob/master/doc/jitterentropy.3#L234
+ */
+ if (result < -5) {
+ ossl_set_error_state(OSSL_SELF_TEST_TYPE_CRNG);
+ break;
+ }
+
+ /* Success */
+ if (result >= 0 && (size_t)result == len)
+ return len;
+ }
+
+ /* Permanent failure or too many intermittent failures */
+ s->state = EVP_RAND_STATE_ERROR;
+ ERR_raise_data(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY,
+ "jent_read_entropy (%d)", result);
+ return 0;
+}
+
+static void *jitter_new(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ PROV_JITTER *s;
+
+ if (parent != NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_SEED_SOURCES_MUST_NOT_HAVE_A_PARENT);
+ return NULL;
+ }
+
+ s = OPENSSL_zalloc(sizeof(*s));
+ if (s == NULL)
+ return NULL;
+
+ s->provctx = provctx;
+ s->state = EVP_RAND_STATE_UNINITIALISED;
+ return s;
+}
+
+static void jitter_free(void *vseed)
+{
+ OPENSSL_free(vseed);
+}
+
+static int jitter_instantiate(void *vseed, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr,
+ size_t pstr_len,
+ ossl_unused const OSSL_PARAM params[])
+{
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+ int ret;
+
+ if ((ret = jent_entropy_init_ex(0, JENT_FORCE_FIPS)) != 0) {
+ ERR_raise_data(ERR_LIB_RAND, RAND_R_ERROR_RETRIEVING_ENTROPY,
+ "jent_entropy_init_ex (%d)", ret);
+ s->state = EVP_RAND_STATE_ERROR;
+ return 0;
+ }
+
+ s->state = EVP_RAND_STATE_READY;
+ return 1;
+}
+
+static int jitter_uninstantiate(void *vseed)
+{
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+
+ s->state = EVP_RAND_STATE_UNINITIALISED;
+ return 1;
+}
+
+static int jitter_generate(void *vseed, unsigned char *out, size_t outlen,
+ unsigned int strength,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+ size_t entropy_available;
+ RAND_POOL *pool;
+
+ if (s->state != EVP_RAND_STATE_READY) {
+ ERR_raise(ERR_LIB_PROV,
+ s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE
+ : PROV_R_NOT_INSTANTIATED);
+ return 0;
+ }
+
+ pool = ossl_rand_pool_new(strength, 1, outlen, outlen);
+ if (pool == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB);
+ return 0;
+ }
+
+ /* Get entropy from jitter entropy library. */
+ entropy_available = ossl_prov_acquire_entropy_from_jitter(s, pool);
+
+ if (entropy_available > 0) {
+ if (!ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) {
+ ossl_rand_pool_free(pool);
+ return 0;
+ }
+ memcpy(out, ossl_rand_pool_buffer(pool), ossl_rand_pool_length(pool));
+ }
+
+ ossl_rand_pool_free(pool);
+ return entropy_available > 0;
+}
+
+static int jitter_reseed(void *vseed,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *ent,
+ ossl_unused size_t ent_len,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+
+ if (s->state != EVP_RAND_STATE_READY) {
+ ERR_raise(ERR_LIB_PROV,
+ s->state == EVP_RAND_STATE_ERROR ? PROV_R_IN_ERROR_STATE
+ : PROV_R_NOT_INSTANTIATED);
+ return 0;
+ }
+ return 1;
+}
+
+static int jitter_get_ctx_params(void *vseed, OSSL_PARAM params[])
+{
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE);
+ if (p != NULL && !OSSL_PARAM_set_int(p, s->state))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_set_int(p, 1024))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, 128))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *jitter_gettable_ctx_params(ossl_unused void *vseed,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int jitter_verify_zeroization(ossl_unused void *vseed)
+{
+ return 1;
+}
+
+static size_t jitter_get_seed(void *vseed, unsigned char **pout,
+ int entropy, size_t min_len,
+ size_t max_len,
+ int prediction_resistance,
+ const unsigned char *adin,
+ size_t adin_len)
+{
+ size_t ret = 0;
+ size_t entropy_available = 0;
+ RAND_POOL *pool;
+ PROV_JITTER *s = (PROV_JITTER *)vseed;
+
+ pool = ossl_rand_pool_new(entropy, 1, min_len, max_len);
+ if (pool == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RAND_LIB);
+ return 0;
+ }
+
+ /* Get entropy from jitter entropy library. */
+ entropy_available = ossl_prov_acquire_entropy_from_jitter(s, pool);
+
+ if (entropy_available > 0
+ && ossl_rand_pool_adin_mix_in(pool, adin, adin_len)) {
+ ret = ossl_rand_pool_length(pool);
+ *pout = ossl_rand_pool_detach(pool);
+ } else {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ENTROPY_SOURCE_STRENGTH_TOO_WEAK);
+ }
+ ossl_rand_pool_free(pool);
+ return ret;
+}
+
+# ifndef OPENSSL_NO_FIPS_JITTER
+size_t ossl_rand_jitter_get_seed(unsigned char **pout, int entropy, size_t min_len, size_t max_len)
+{
+ size_t ret = 0;
+ OSSL_PARAM params[1] = { OSSL_PARAM_END };
+ PROV_JITTER *s = jitter_new(NULL, NULL, NULL);
+
+ if (s == NULL)
+ return ret;
+ if (!jitter_instantiate(s, 0, 0, NULL, 0, params))
+ goto end;
+ ret = jitter_get_seed(s, pout, entropy, min_len, max_len, 0, NULL, 0);
+ end:
+ jitter_free(s);
+ return ret;
+}
+# endif
+
+static void jitter_clear_seed(ossl_unused void *vdrbg,
+ unsigned char *out, size_t outlen)
+{
+ OPENSSL_secure_clear_free(out, outlen);
+}
+
+static int jitter_enable_locking(ossl_unused void *vseed)
+{
+ return 1;
+}
+
+int jitter_lock(ossl_unused void *vctx)
+{
+ return 1;
+}
+
+void jitter_unlock(ossl_unused void *vctx)
+{
+}
+
+const OSSL_DISPATCH ossl_jitter_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))jitter_new },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))jitter_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))jitter_instantiate },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))jitter_uninstantiate },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))jitter_generate },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))jitter_reseed },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))jitter_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))jitter_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))jitter_unlock },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))jitter_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))jitter_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))jitter_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))jitter_get_seed },
+ { OSSL_FUNC_RAND_CLEAR_SEED, (void(*)(void))jitter_clear_seed },
+ OSSL_DISPATCH_END
+};
+#else
+NON_EMPTY_TRANSLATION_UNIT
+#endif
diff --git a/crypto/openssl/providers/implementations/rands/seeding/build.info b/crypto/openssl/providers/implementations/rands/seeding/build.info
new file mode 100644
index 000000000000..9c5eefee2d0c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/build.info
@@ -0,0 +1,15 @@
+$COMMON=rand_unix.c rand_win.c rand_tsc.c
+IF[{- $config{target} =~ /vxworks/i -}]
+ $COMMON=$COMMON rand_vxworks.c
+ENDIF
+IF[{- $config{target} =~ /vms/i -}]
+ $COMMON=$COMMON rand_vms.c
+ENDIF
+IF[{- !$disabled{asm} && $config{target} =~ '.*aarch64' -}]
+ $COMMON=$COMMON rand_cpu_arm64.c
+ELSE
+ $COMMON=$COMMON rand_cpu_x86.c
+ENDIF
+
+SOURCE[../../../libdefault.a]=$COMMON
+
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c
new file mode 100644
index 000000000000..a8530e02b576
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_arm64.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include <openssl/opensslconf.h>
+#include "crypto/rand_pool.h"
+#include "prov/seeding.h"
+
+
+#ifdef OPENSSL_RAND_SEED_RDCPU
+#include "crypto/arm_arch.h"
+
+size_t OPENSSL_rndrrs_bytes(unsigned char *buf, size_t len);
+
+static size_t get_hardware_random_value(unsigned char *buf, size_t len);
+
+/*
+ * Acquire entropy using Arm-specific cpu instructions
+ *
+ * Uses the RNDRRS instruction. RNDR is never needed since
+ * RNDRRS will always be available if RNDR is an available
+ * instruction.
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
+ */
+size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool)
+{
+ size_t bytes_needed;
+ unsigned char *buffer;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ if (bytes_needed > 0) {
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+
+ if (buffer != NULL) {
+ if (get_hardware_random_value(buffer, bytes_needed) == bytes_needed)
+ ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed);
+ else
+ ossl_rand_pool_add_end(pool, 0, 0);
+ }
+ }
+
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+static size_t get_hardware_random_value(unsigned char *buf, size_t len)
+{
+ /* Always use RNDRRS or nothing */
+ if (OPENSSL_armcap_P & ARMV8_RNG) {
+ if (OPENSSL_rndrrs_bytes(buf, len) != len)
+ return 0;
+ } else {
+ return 0;
+ }
+ return len;
+}
+
+#else
+NON_EMPTY_TRANSLATION_UNIT
+#endif /* OPENSSL_RAND_SEED_RDCPU */
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c
new file mode 100644
index 000000000000..0e062fa45aa2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_cpu_x86.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include <openssl/opensslconf.h>
+#include "crypto/rand_pool.h"
+#include "prov/seeding.h"
+
+#ifdef OPENSSL_RAND_SEED_RDCPU
+# if defined(OPENSSL_SYS_TANDEM) && defined(_TNS_X_TARGET)
+# include <builtin.h> /* _rdrand64 */
+# include <string.h> /* memcpy */
+# else
+size_t OPENSSL_ia32_rdseed_bytes(unsigned char *buf, size_t len);
+size_t OPENSSL_ia32_rdrand_bytes(unsigned char *buf, size_t len);
+# endif
+
+static size_t get_hardware_random_value(unsigned char *buf, size_t len);
+
+/*
+ * Acquire entropy using Intel-specific cpu instructions
+ *
+ * Uses the RDSEED instruction if available, otherwise uses
+ * RDRAND if available.
+ *
+ * For the differences between RDSEED and RDRAND, and why RDSEED
+ * is the preferred choice, see https://goo.gl/oK3KcN
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
+ */
+size_t ossl_prov_acquire_entropy_from_cpu(RAND_POOL *pool)
+{
+ size_t bytes_needed;
+ unsigned char *buffer;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ if (bytes_needed > 0) {
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+
+ if (buffer != NULL) {
+ if (get_hardware_random_value(buffer, bytes_needed) == bytes_needed) {
+ ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed);
+ } else {
+ ossl_rand_pool_add_end(pool, 0, 0);
+ }
+ }
+ }
+
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+#if defined(OPENSSL_SYS_TANDEM) && defined(_TNS_X_TARGET)
+/* Obtain random bytes from the x86 hardware random function in 64 bit chunks */
+static size_t get_hardware_random_value(unsigned char *buf, size_t len)
+{
+ size_t bytes_remaining = len;
+
+ while (bytes_remaining > 0) {
+ /* Always use 64 bit fetch, then use the lower bytes as needed. */
+ /* The platform is big-endian. */
+ uint64_t random_value = 0;
+
+ if (_rdrand64(&random_value) != 0) {
+ unsigned char *random_buffer = (unsigned char *)&random_value;
+
+ if (bytes_remaining >= sizeof(random_value)) {
+ memcpy(buf, random_buffer, sizeof(random_value));
+ bytes_remaining -= sizeof(random_value);
+ buf += sizeof(random_value);
+ } else {
+ memcpy(buf,
+ random_buffer + (sizeof(random_value) - bytes_remaining),
+ bytes_remaining);
+ bytes_remaining = 0; /* This will terminate the loop */
+ }
+ } else
+ break;
+ }
+ if (bytes_remaining == 0)
+ return len;
+ return 0;
+}
+#else
+static size_t get_hardware_random_value(unsigned char *buf, size_t len) {
+ /* Whichever comes first, use RDSEED, RDRAND or nothing */
+ if ((OPENSSL_ia32cap_P[2] & (1 << 18)) != 0) {
+ if (OPENSSL_ia32_rdseed_bytes(buf, len) != len)
+ return 0;
+ } else if ((OPENSSL_ia32cap_P[1] & (1 << (62 - 32))) != 0) {
+ if (OPENSSL_ia32_rdrand_bytes(buf, len) != len)
+ return 0;
+ } else
+ return 0;
+ return len;
+}
+#endif
+
+#else
+NON_EMPTY_TRANSLATION_UNIT
+#endif
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c b/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c
new file mode 100644
index 000000000000..98dd836b24d9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_tsc.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include <openssl/opensslconf.h>
+#include "crypto/rand_pool.h"
+#include "prov/seeding.h"
+
+#ifdef OPENSSL_RAND_SEED_RDTSC
+/*
+ * IMPORTANT NOTE: It is not currently possible to use this code
+ * because we are not sure about the amount of randomness it provides.
+ * Some SP800-90B tests have been run, but there is internal skepticism.
+ * So for now this code is not used.
+ */
+# error "RDTSC enabled? Should not be possible!"
+
+/*
+ * Acquire entropy from high-speed clock
+ *
+ * Since we get some randomness from the low-order bits of the
+ * high-speed clock, it can help.
+ *
+ * Returns the total entropy count, if it exceeds the requested
+ * entropy count. Otherwise, returns an entropy count of 0.
+ */
+size_t ossl_prov_acquire_entropy_from_tsc(RAND_POOL *pool)
+{
+ unsigned char c;
+ int i;
+
+ if ((OPENSSL_ia32cap_P[0] & (1 << 4)) != 0) {
+ for (i = 0; i < TSC_READ_COUNT; i++) {
+ c = (unsigned char)(OPENSSL_rdtsc() & 0xFF);
+ ossl_rand_pool_add(pool, &c, 1, 4);
+ }
+ }
+ return ossl_rand_pool_entropy_available(pool);
+}
+#else
+NON_EMPTY_TRANSLATION_UNIT
+#endif
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c b/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c
new file mode 100644
index 000000000000..c3a5d8b3bf24
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_unix.c
@@ -0,0 +1,802 @@
+/*
+ * Copyright 1995-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+#include "internal/e_os.h"
+#include <stdio.h>
+#include "internal/cryptlib.h"
+#include <openssl/rand.h>
+#include <openssl/crypto.h>
+#include "crypto/rand_pool.h"
+#include "crypto/rand.h"
+#include "internal/dso.h"
+#include "internal/nelem.h"
+#include "prov/seeding.h"
+
+#ifndef OPENSSL_SYS_UEFI
+# ifdef __linux
+# include <sys/syscall.h>
+# ifdef DEVRANDOM_WAIT
+# include <sys/shm.h>
+# include <sys/utsname.h>
+# endif
+# endif
+# if defined(__FreeBSD__) || defined(__NetBSD__)
+# include <sys/types.h>
+# include <sys/sysctl.h>
+# include <sys/param.h>
+# endif
+# if defined(__FreeBSD__) && __FreeBSD_version >= 1200061
+# include <sys/random.h>
+# endif
+# if defined(__OpenBSD__)
+# include <sys/param.h>
+# endif
+# if defined(__DragonFly__)
+# include <sys/param.h>
+# include <sys/random.h>
+# endif
+#endif
+
+#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \
+ || defined(__DJGPP__)
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <fcntl.h>
+# include <unistd.h>
+# include <sys/time.h>
+
+static uint64_t get_time_stamp(void);
+
+/* Macro to convert two thirty two bit values into a sixty four bit one */
+# define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b))
+
+/*
+ * Check for the existence and support of POSIX timers. The standard
+ * says that the _POSIX_TIMERS macro will have a positive value if they
+ * are available.
+ *
+ * However, we want an additional constraint: that the timer support does
+ * not require an extra library dependency. Early versions of glibc
+ * require -lrt to be specified on the link line to access the timers,
+ * so this needs to be checked for.
+ *
+ * It is worse because some libraries define __GLIBC__ but don't
+ * support the version testing macro (e.g. uClibc). This means
+ * an extra check is needed.
+ *
+ * The final condition is:
+ * "have posix timers and either not glibc or glibc without -lrt"
+ *
+ * The nested #if sequences are required to avoid using a parameterised
+ * macro that might be undefined.
+ */
+# undef OSSL_POSIX_TIMER_OKAY
+/* On some systems, _POSIX_TIMERS is defined but empty.
+ * Subtracting by 0 when comparing avoids an error in this case. */
+# if defined(_POSIX_TIMERS) && _POSIX_TIMERS -0 > 0
+# if defined(__GLIBC__)
+# if defined(__GLIBC_PREREQ)
+# if __GLIBC_PREREQ(2, 17)
+# define OSSL_POSIX_TIMER_OKAY
+# endif
+# endif
+# else
+# define OSSL_POSIX_TIMER_OKAY
+# endif
+# endif
+#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS))
+ || defined(__DJGPP__) */
+
+#if defined(OPENSSL_RAND_SEED_NONE)
+/* none means none. this simplifies the following logic */
+# undef OPENSSL_RAND_SEED_OS
+# undef OPENSSL_RAND_SEED_GETRANDOM
+# undef OPENSSL_RAND_SEED_DEVRANDOM
+# undef OPENSSL_RAND_SEED_RDTSC
+# undef OPENSSL_RAND_SEED_RDCPU
+# undef OPENSSL_RAND_SEED_EGD
+#endif
+
+#if defined(OPENSSL_SYS_UEFI) && !defined(OPENSSL_RAND_SEED_NONE)
+# error "UEFI only supports seeding NONE"
+#endif
+
+#if !(defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) \
+ || defined(OPENSSL_SYS_VMS) || defined(OPENSSL_SYS_VXWORKS) \
+ || defined(OPENSSL_SYS_UEFI))
+
+# if defined(OPENSSL_SYS_VOS)
+
+# ifndef OPENSSL_RAND_SEED_OS
+# error "Unsupported seeding method configured; must be os"
+# endif
+
+# if defined(OPENSSL_SYS_VOS_HPPA) && defined(OPENSSL_SYS_VOS_IA32)
+# error "Unsupported HP-PA and IA32 at the same time."
+# endif
+# if !defined(OPENSSL_SYS_VOS_HPPA) && !defined(OPENSSL_SYS_VOS_IA32)
+# error "Must have one of HP-PA or IA32"
+# endif
+
+/*
+ * The following algorithm repeatedly samples the real-time clock (RTC) to
+ * generate a sequence of unpredictable data. The algorithm relies upon the
+ * uneven execution speed of the code (due to factors such as cache misses,
+ * interrupts, bus activity, and scheduling) and upon the rather large
+ * relative difference between the speed of the clock and the rate at which
+ * it can be read. If it is ported to an environment where execution speed
+ * is more constant or where the RTC ticks at a much slower rate, or the
+ * clock can be read with fewer instructions, it is likely that the results
+ * would be far more predictable. This should only be used for legacy
+ * platforms.
+ *
+ * As a precaution, we assume only 2 bits of entropy per byte.
+ */
+size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
+{
+ short int code;
+ int i, k;
+ size_t bytes_needed;
+ struct timespec ts;
+ unsigned char v;
+# ifdef OPENSSL_SYS_VOS_HPPA
+ long duration;
+ extern void s$sleep(long *_duration, short int *_code);
+# else
+ long long duration;
+ extern void s$sleep2(long long *_duration, short int *_code);
+# endif
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 4 /*entropy_factor*/);
+
+ for (i = 0; i < bytes_needed; i++) {
+ /*
+ * burn some cpu; hope for interrupts, cache collisions, bus
+ * interference, etc.
+ */
+ for (k = 0; k < 99; k++)
+ ts.tv_nsec = random();
+
+# ifdef OPENSSL_SYS_VOS_HPPA
+ /* sleep for 1/1024 of a second (976 us). */
+ duration = 1;
+ s$sleep(&duration, &code);
+# else
+ /* sleep for 1/65536 of a second (15 us). */
+ duration = 1;
+ s$sleep2(&duration, &code);
+# endif
+
+ /* Get wall clock time, take 8 bits. */
+ clock_gettime(CLOCK_REALTIME, &ts);
+ v = (unsigned char)(ts.tv_nsec & 0xFF);
+ ossl_rand_pool_add(pool, arg, &v, sizeof(v), 2);
+ }
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+}
+
+# else
+
+# if defined(OPENSSL_RAND_SEED_EGD) && \
+ (defined(OPENSSL_NO_EGD) || !defined(DEVRANDOM_EGD))
+# error "Seeding uses EGD but EGD is turned off or no device given"
+# endif
+
+# if defined(OPENSSL_RAND_SEED_DEVRANDOM) && !defined(DEVRANDOM)
+# error "Seeding uses urandom but DEVRANDOM is not configured"
+# endif
+
+# if defined(OPENSSL_RAND_SEED_OS)
+# if !defined(DEVRANDOM)
+# error "OS seeding requires DEVRANDOM to be configured"
+# endif
+# define OPENSSL_RAND_SEED_GETRANDOM
+# define OPENSSL_RAND_SEED_DEVRANDOM
+# endif
+
+# if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND)
+/*
+ * sysctl_random(): Use sysctl() to read a random number from the kernel
+ * Returns the number of bytes returned in buf on success, -1 on failure.
+ */
+static ssize_t sysctl_random(char *buf, size_t buflen)
+{
+ int mib[2];
+ size_t done = 0;
+ size_t len;
+
+ /*
+ * Note: sign conversion between size_t and ssize_t is safe even
+ * without a range check, see comment in syscall_random()
+ */
+
+ /*
+ * On FreeBSD old implementations returned longs, newer versions support
+ * variable sizes up to 256 byte. The code below would not work properly
+ * when the sysctl returns long and we want to request something not a
+ * multiple of longs, which should never be the case.
+ */
+#if defined(__FreeBSD__)
+ if (!ossl_assert(buflen % sizeof(long) == 0)) {
+ errno = EINVAL;
+ return -1;
+ }
+#endif
+
+ /*
+ * On NetBSD before 4.0 KERN_ARND was an alias for KERN_URND, and only
+ * filled in an int, leaving the rest uninitialized. Since NetBSD 4.0
+ * it returns a variable number of bytes with the current version supporting
+ * up to 256 bytes.
+ * Just return an error on older NetBSD versions.
+ */
+#if defined(__NetBSD__) && __NetBSD_Version__ < 400000000
+ errno = ENOSYS;
+ return -1;
+#endif
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_ARND;
+
+ do {
+ len = buflen > 256 ? 256 : buflen;
+ if (sysctl(mib, 2, buf, &len, NULL, 0) == -1)
+ return done > 0 ? done : -1;
+ done += len;
+ buf += len;
+ buflen -= len;
+ } while (buflen > 0);
+
+ return done;
+}
+# endif
+
+# if defined(OPENSSL_RAND_SEED_GETRANDOM)
+
+# if defined(__linux) && !defined(__NR_getrandom)
+# if defined(__arm__)
+# define __NR_getrandom (__NR_SYSCALL_BASE+384)
+# elif defined(__i386__)
+# define __NR_getrandom 355
+# elif defined(__x86_64__)
+# if defined(__ILP32__)
+# define __NR_getrandom (__X32_SYSCALL_BIT + 318)
+# else
+# define __NR_getrandom 318
+# endif
+# elif defined(__xtensa__)
+# define __NR_getrandom 338
+# elif defined(__s390__) || defined(__s390x__)
+# define __NR_getrandom 349
+# elif defined(__bfin__)
+# define __NR_getrandom 389
+# elif defined(__powerpc__)
+# define __NR_getrandom 359
+# elif defined(__mips__) || defined(__mips64)
+# if _MIPS_SIM == _MIPS_SIM_ABI32
+# define __NR_getrandom (__NR_Linux + 353)
+# elif _MIPS_SIM == _MIPS_SIM_ABI64
+# define __NR_getrandom (__NR_Linux + 313)
+# elif _MIPS_SIM == _MIPS_SIM_NABI32
+# define __NR_getrandom (__NR_Linux + 317)
+# endif
+# elif defined(__hppa__)
+# define __NR_getrandom (__NR_Linux + 339)
+# elif defined(__sparc__)
+# define __NR_getrandom 347
+# elif defined(__ia64__)
+# define __NR_getrandom 1339
+# elif defined(__alpha__)
+# define __NR_getrandom 511
+# elif defined(__sh__)
+# if defined(__SH5__)
+# define __NR_getrandom 373
+# else
+# define __NR_getrandom 384
+# endif
+# elif defined(__avr32__)
+# define __NR_getrandom 317
+# elif defined(__microblaze__)
+# define __NR_getrandom 385
+# elif defined(__m68k__)
+# define __NR_getrandom 352
+# elif defined(__cris__)
+# define __NR_getrandom 356
+# else /* generic (f.e. aarch64, loongarch, loongarch64) */
+# define __NR_getrandom 278
+# endif
+# endif
+
+/*
+ * syscall_random(): Try to get random data using a system call
+ * returns the number of bytes returned in buf, or < 0 on error.
+ */
+static ssize_t syscall_random(void *buf, size_t buflen)
+{
+ /*
+ * Note: 'buflen' equals the size of the buffer which is used by the
+ * get_entropy() callback of the RAND_DRBG. It is roughly bounded by
+ *
+ * 2 * RAND_POOL_FACTOR * (RAND_DRBG_STRENGTH / 8) = 2^14
+ *
+ * which is way below the OSSL_SSIZE_MAX limit. Therefore sign conversion
+ * between size_t and ssize_t is safe even without a range check.
+ */
+
+ /*
+ * Do runtime detection to find getentropy().
+ *
+ * Known OSs that should support this:
+ * - Darwin since 16 (OSX 10.12, IOS 10.0).
+ * - Solaris since 11.3
+ * - OpenBSD since 5.6
+ * - Linux since 3.17 with glibc 2.25
+ *
+ * Note: Sometimes getentropy() can be provided but not implemented
+ * internally. So we need to check errno for ENOSYS
+ */
+# if !defined(__DragonFly__) && !defined(__NetBSD__) && !defined(__FreeBSD__)
+# if defined(__GNUC__) && __GNUC__>=2 && defined(__ELF__) && !defined(__hpux)
+ extern int getentropy(void *buffer, size_t length) __attribute__((weak));
+
+ if (getentropy != NULL) {
+ if (getentropy(buf, buflen) == 0)
+ return (ssize_t)buflen;
+ if (errno != ENOSYS)
+ return -1;
+ }
+# elif defined(OPENSSL_APPLE_CRYPTO_RANDOM)
+
+ if (CCRandomGenerateBytes(buf, buflen) == kCCSuccess)
+ return (ssize_t)buflen;
+
+ return -1;
+# else
+ union {
+ void *p;
+ int (*f)(void *buffer, size_t length);
+ } p_getentropy;
+
+ /*
+ * We could cache the result of the lookup, but we normally don't
+ * call this function often.
+ */
+ ERR_set_mark();
+ p_getentropy.p = DSO_global_lookup("getentropy");
+ ERR_pop_to_mark();
+ if (p_getentropy.p != NULL)
+ return p_getentropy.f(buf, buflen) == 0 ? (ssize_t)buflen : -1;
+# endif
+# endif /* !__DragonFly__ && !__NetBSD__ && !__FreeBSD__ */
+
+ /* Linux supports this since version 3.17 */
+# if defined(__linux) && defined(__NR_getrandom)
+ return syscall(__NR_getrandom, buf, buflen, 0);
+# elif (defined(__DragonFly__) && __DragonFly_version >= 500700) \
+ || (defined(__NetBSD__) && __NetBSD_Version >= 1000000000) \
+ || (defined(__FreeBSD__) && __FreeBSD_version >= 1200061)
+ return getrandom(buf, buflen, 0);
+# elif (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(KERN_ARND)
+ return sysctl_random(buf, buflen);
+# elif defined(__wasi__)
+ if (getentropy(buf, buflen) == 0)
+ return (ssize_t)buflen;
+ return -1;
+# else
+ errno = ENOSYS;
+ return -1;
+# endif
+}
+# endif /* defined(OPENSSL_RAND_SEED_GETRANDOM) */
+
+# if defined(OPENSSL_RAND_SEED_DEVRANDOM)
+static const char *random_device_paths[] = { DEVRANDOM };
+static struct random_device {
+ int fd;
+ dev_t dev;
+ ino_t ino;
+ mode_t mode;
+ dev_t rdev;
+} random_devices[OSSL_NELEM(random_device_paths)];
+static int keep_random_devices_open = 1;
+
+# if defined(__linux) && defined(DEVRANDOM_WAIT) \
+ && defined(OPENSSL_RAND_SEED_GETRANDOM)
+static void *shm_addr;
+
+static void cleanup_shm(void)
+{
+ shmdt(shm_addr);
+}
+
+/*
+ * Ensure that the system randomness source has been adequately seeded.
+ * This is done by having the first start of libcrypto, wait until the device
+ * /dev/random becomes able to supply a byte of entropy. Subsequent starts
+ * of the library and later reseedings do not need to do this.
+ */
+static int wait_random_seeded(void)
+{
+ static int seeded = OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID < 0;
+ static const int kernel_version[] = { DEVRANDOM_SAFE_KERNEL };
+ int kernel[2];
+ int shm_id, fd, r;
+ char c, *p;
+ struct utsname un;
+ fd_set fds;
+
+ if (!seeded) {
+ /* See if anything has created the global seeded indication */
+ if ((shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1, 0)) == -1) {
+ /*
+ * Check the kernel's version and fail if it is too recent.
+ *
+ * Linux kernels from 4.8 onwards do not guarantee that
+ * /dev/urandom is properly seeded when /dev/random becomes
+ * readable. However, such kernels support the getentropy(2)
+ * system call and this should always succeed which renders
+ * this alternative but essentially identical source moot.
+ */
+ if (uname(&un) == 0) {
+ kernel[0] = atoi(un.release);
+ p = strchr(un.release, '.');
+ kernel[1] = p == NULL ? 0 : atoi(p + 1);
+ if (kernel[0] > kernel_version[0]
+ || (kernel[0] == kernel_version[0]
+ && kernel[1] >= kernel_version[1])) {
+ return 0;
+ }
+ }
+ /* Open /dev/random and wait for it to be readable */
+ if ((fd = open(DEVRANDOM_WAIT, O_RDONLY)) != -1) {
+ if (DEVRANDM_WAIT_USE_SELECT && fd < FD_SETSIZE) {
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ while ((r = select(fd + 1, &fds, NULL, NULL, NULL)) < 0
+ && errno == EINTR);
+ } else {
+ while ((r = read(fd, &c, 1)) < 0 && errno == EINTR);
+ }
+ close(fd);
+ if (r == 1) {
+ seeded = 1;
+ /* Create the shared memory indicator */
+ shm_id = shmget(OPENSSL_RAND_SEED_DEVRANDOM_SHM_ID, 1,
+ IPC_CREAT | S_IRUSR | S_IRGRP | S_IROTH);
+ }
+ }
+ }
+ if (shm_id != -1) {
+ seeded = 1;
+ /*
+ * Map the shared memory to prevent its premature destruction.
+ * If this call fails, it isn't a big problem.
+ */
+ shm_addr = shmat(shm_id, NULL, SHM_RDONLY);
+ if (shm_addr != (void *)-1)
+ OPENSSL_atexit(&cleanup_shm);
+ }
+ }
+ return seeded;
+}
+# else /* defined __linux && DEVRANDOM_WAIT && OPENSSL_RAND_SEED_GETRANDOM */
+static int wait_random_seeded(void)
+{
+ return 1;
+}
+# endif
+
+/*
+ * Verify that the file descriptor associated with the random source is
+ * still valid. The rationale for doing this is the fact that it is not
+ * uncommon for daemons to close all open file handles when daemonizing.
+ * So the handle might have been closed or even reused for opening
+ * another file.
+ */
+static int check_random_device(struct random_device *rd)
+{
+ struct stat st;
+
+ return rd->fd != -1
+ && fstat(rd->fd, &st) != -1
+ && rd->dev == st.st_dev
+ && rd->ino == st.st_ino
+ && ((rd->mode ^ st.st_mode) & ~(S_IRWXU | S_IRWXG | S_IRWXO)) == 0
+ && rd->rdev == st.st_rdev;
+}
+
+/*
+ * Open a random device if required and return its file descriptor or -1 on error
+ */
+static int get_random_device(size_t n)
+{
+ struct stat st;
+ struct random_device *rd = &random_devices[n];
+
+ /* reuse existing file descriptor if it is (still) valid */
+ if (check_random_device(rd))
+ return rd->fd;
+
+ /* open the random device ... */
+ if ((rd->fd = open(random_device_paths[n], O_RDONLY)) == -1)
+ return rd->fd;
+
+ /* ... and cache its relevant stat(2) data */
+ if (fstat(rd->fd, &st) != -1) {
+ rd->dev = st.st_dev;
+ rd->ino = st.st_ino;
+ rd->mode = st.st_mode;
+ rd->rdev = st.st_rdev;
+ } else {
+ close(rd->fd);
+ rd->fd = -1;
+ }
+
+ return rd->fd;
+}
+
+/*
+ * Close a random device making sure it is a random device
+ */
+static void close_random_device(size_t n)
+{
+ struct random_device *rd = &random_devices[n];
+
+ if (check_random_device(rd))
+ close(rd->fd);
+ rd->fd = -1;
+}
+
+int ossl_rand_pool_init(void)
+{
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(random_devices); i++)
+ random_devices[i].fd = -1;
+
+ return 1;
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+ size_t i;
+
+ for (i = 0; i < OSSL_NELEM(random_devices); i++)
+ close_random_device(i);
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+ if (!keep)
+ ossl_rand_pool_cleanup();
+
+ keep_random_devices_open = keep;
+}
+
+# else /* !defined(OPENSSL_RAND_SEED_DEVRANDOM) */
+
+int ossl_rand_pool_init(void)
+{
+ return 1;
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+}
+
+# endif /* defined(OPENSSL_RAND_SEED_DEVRANDOM) */
+
+/*
+ * Try the various seeding methods in turn, exit when successful.
+ *
+ * If more than one entropy source is available, is it
+ * preferable to stop as soon as enough entropy has been collected
+ * (as favored by @rsalz) or should one rather be defensive and add
+ * more entropy than requested and/or from different sources?
+ *
+ * Currently, the user can select multiple entropy sources in the
+ * configure step, yet in practice only the first available source
+ * will be used. A more flexible solution has been requested, but
+ * currently it is not clear how this can be achieved without
+ * overengineering the problem. There are many parameters which
+ * could be taken into account when selecting the order and amount
+ * of input from the different entropy sources (trust, quality,
+ * possibility of blocking).
+ */
+size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
+{
+# if defined(OPENSSL_RAND_SEED_NONE)
+ return ossl_rand_pool_entropy_available(pool);
+# else
+ size_t entropy_available = 0;
+
+ (void)entropy_available; /* avoid compiler warning */
+
+# if defined(OPENSSL_RAND_SEED_GETRANDOM)
+ {
+ size_t bytes_needed;
+ unsigned char *buffer;
+ ssize_t bytes;
+ /* Maximum allowed number of consecutive unsuccessful attempts */
+ int attempts = 3;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ while (bytes_needed != 0 && attempts-- > 0) {
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ bytes = syscall_random(buffer, bytes_needed);
+ if (bytes > 0) {
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ bytes_needed -= bytes;
+ attempts = 3; /* reset counter after successful attempt */
+ } else if (bytes < 0 && errno != EINTR) {
+ break;
+ }
+ }
+ }
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+# if defined(OPENSSL_RAND_SEED_DEVRANDOM)
+ if (wait_random_seeded()) {
+ size_t bytes_needed;
+ unsigned char *buffer;
+ size_t i;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ for (i = 0; bytes_needed > 0 && i < OSSL_NELEM(random_device_paths);
+ i++) {
+ ssize_t bytes = 0;
+ /* Maximum number of consecutive unsuccessful attempts */
+ int attempts = 3;
+ const int fd = get_random_device(i);
+
+ if (fd == -1)
+ continue;
+
+ while (bytes_needed != 0 && attempts-- > 0) {
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ bytes = read(fd, buffer, bytes_needed);
+
+ if (bytes > 0) {
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ bytes_needed -= bytes;
+ attempts = 3; /* reset counter on successful attempt */
+ } else if (bytes < 0 && errno != EINTR) {
+ break;
+ }
+ }
+ if (bytes < 0 || !keep_random_devices_open)
+ close_random_device(i);
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);
+ }
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+ }
+# endif
+
+# if defined(OPENSSL_RAND_SEED_RDTSC)
+ entropy_available = ossl_prov_acquire_entropy_from_tsc(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+# if defined(OPENSSL_RAND_SEED_RDCPU)
+ entropy_available = ossl_prov_acquire_entropy_from_cpu(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+# if defined(OPENSSL_RAND_SEED_EGD)
+ {
+ static const char *paths[] = { DEVRANDOM_EGD, NULL };
+ size_t bytes_needed;
+ unsigned char *buffer;
+ int i;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ for (i = 0; bytes_needed > 0 && paths[i] != NULL; i++) {
+ size_t bytes = 0;
+ int num;
+
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ num = RAND_query_egd_bytes(paths[i],
+ buffer, (int)bytes_needed);
+ if (num == (int)bytes_needed)
+ bytes = bytes_needed;
+
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);
+ }
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+ }
+# endif
+
+ return ossl_rand_pool_entropy_available(pool);
+# endif
+}
+# endif
+#endif
+
+#if (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS)) \
+ || defined(__DJGPP__)
+int ossl_pool_add_nonce_data(RAND_POOL *pool)
+{
+ struct {
+ pid_t pid;
+ CRYPTO_THREAD_ID tid;
+ uint64_t time;
+ } data;
+
+ /* Erase the entire structure including any padding */
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Add process id, thread id, and a high resolution timestamp to
+ * ensure that the nonce is unique with high probability for
+ * different process instances.
+ */
+ data.pid = getpid();
+ data.tid = CRYPTO_THREAD_get_current_id();
+ data.time = get_time_stamp();
+
+ return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+/*
+ * Get the current time with the highest possible resolution
+ *
+ * The time stamp is added to the nonce, so it is optimized for not repeating.
+ * The current time is ideal for this purpose, provided the computer's clock
+ * is synchronized.
+ */
+static uint64_t get_time_stamp(void)
+{
+# if defined(OSSL_POSIX_TIMER_OKAY)
+ {
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
+ return TWO32TO64(ts.tv_sec, ts.tv_nsec);
+ }
+# endif
+# if defined(__unix__) \
+ || (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L)
+ {
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) == 0)
+ return TWO32TO64(tv.tv_sec, tv.tv_usec);
+ }
+# endif
+ return time(NULL);
+}
+
+#endif /* (defined(OPENSSL_SYS_UNIX) && !defined(OPENSSL_SYS_VXWORKS))
+ || defined(__DJGPP__) */
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c b/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c
new file mode 100644
index 000000000000..4ff879491a6b
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_vms.c
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2001-2022 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/e_os.h"
+
+#define __NEW_STARLET 1 /* New starlet definitions since VMS 7.0 */
+#include <unistd.h>
+#include "internal/cryptlib.h"
+#include "internal/nelem.h"
+#include <openssl/rand.h>
+#include "crypto/rand.h"
+#include "crypto/rand_pool.h"
+#include "prov/seeding.h"
+#include <descrip.h>
+#include <dvidef.h>
+#include <jpidef.h>
+#include <rmidef.h>
+#include <syidef.h>
+#include <ssdef.h>
+#include <starlet.h>
+#include <efndef.h>
+#include <gen64def.h>
+#include <iosbdef.h>
+#include <iledef.h>
+#include <lib$routines.h>
+#ifdef __DECC
+# pragma message disable DOLLARID
+#endif
+
+#include <dlfcn.h> /* SYS$GET_ENTROPY presence */
+
+#ifndef OPENSSL_RAND_SEED_OS
+# error "Unsupported seeding method configured; must be os"
+#endif
+
+/*
+ * DATA COLLECTION METHOD
+ * ======================
+ *
+ * This is a method to get low quality entropy.
+ * It works by collecting all kinds of statistical data that
+ * VMS offers and using them as random seed.
+ */
+
+/* We need to make sure we have the right size pointer in some cases */
+#if __INITIAL_POINTER_SIZE == 64
+# pragma pointer_size save
+# pragma pointer_size 32
+#endif
+typedef uint32_t *uint32_t__ptr32;
+#if __INITIAL_POINTER_SIZE == 64
+# pragma pointer_size restore
+#endif
+
+struct item_st {
+ short length, code; /* length is number of bytes */
+};
+
+static const struct item_st DVI_item_data[] = {
+ {4, DVI$_ERRCNT},
+ {4, DVI$_REFCNT},
+};
+
+static const struct item_st JPI_item_data[] = {
+ {4, JPI$_BUFIO},
+ {4, JPI$_CPUTIM},
+ {4, JPI$_DIRIO},
+ {4, JPI$_IMAGECOUNT},
+ {4, JPI$_PAGEFLTS},
+ {4, JPI$_PID},
+ {4, JPI$_PPGCNT},
+ {4, JPI$_WSPEAK},
+ /*
+ * Note: the direct result is just a 32-bit address. However, it points
+ * to a list of 4 32-bit words, so we make extra space for them so we can
+ * do in-place replacement of values
+ */
+ {16, JPI$_FINALEXC},
+};
+
+static const struct item_st JPI_item_data_64bit[] = {
+ {8, JPI$_LAST_LOGIN_I},
+ {8, JPI$_LOGINTIM},
+};
+
+static const struct item_st RMI_item_data[] = {
+ {4, RMI$_COLPG},
+ {4, RMI$_MWAIT},
+ {4, RMI$_CEF},
+ {4, RMI$_PFW},
+ {4, RMI$_LEF},
+ {4, RMI$_LEFO},
+ {4, RMI$_HIB},
+ {4, RMI$_HIBO},
+ {4, RMI$_SUSP},
+ {4, RMI$_SUSPO},
+ {4, RMI$_FPG},
+ {4, RMI$_COM},
+ {4, RMI$_COMO},
+ {4, RMI$_CUR},
+#if defined __alpha
+ {4, RMI$_FRLIST},
+ {4, RMI$_MODLIST},
+#endif
+ {4, RMI$_FAULTS},
+ {4, RMI$_PREADS},
+ {4, RMI$_PWRITES},
+ {4, RMI$_PWRITIO},
+ {4, RMI$_PREADIO},
+ {4, RMI$_GVALFLTS},
+ {4, RMI$_WRTINPROG},
+ {4, RMI$_FREFLTS},
+ {4, RMI$_DZROFLTS},
+ {4, RMI$_SYSFAULTS},
+ {4, RMI$_ISWPCNT},
+ {4, RMI$_DIRIO},
+ {4, RMI$_BUFIO},
+ {4, RMI$_MBREADS},
+ {4, RMI$_MBWRITES},
+ {4, RMI$_LOGNAM},
+ {4, RMI$_FCPCALLS},
+ {4, RMI$_FCPREAD},
+ {4, RMI$_FCPWRITE},
+ {4, RMI$_FCPCACHE},
+ {4, RMI$_FCPCPU},
+ {4, RMI$_FCPHIT},
+ {4, RMI$_FCPSPLIT},
+ {4, RMI$_FCPFAULT},
+ {4, RMI$_ENQNEW},
+ {4, RMI$_ENQCVT},
+ {4, RMI$_DEQ},
+ {4, RMI$_BLKAST},
+ {4, RMI$_ENQWAIT},
+ {4, RMI$_ENQNOTQD},
+ {4, RMI$_DLCKSRCH},
+ {4, RMI$_DLCKFND},
+ {4, RMI$_NUMLOCKS},
+ {4, RMI$_NUMRES},
+ {4, RMI$_ARRLOCPK},
+ {4, RMI$_DEPLOCPK},
+ {4, RMI$_ARRTRAPK},
+ {4, RMI$_TRCNGLOS},
+ {4, RMI$_RCVBUFFL},
+ {4, RMI$_ENQNEWLOC},
+ {4, RMI$_ENQNEWIN},
+ {4, RMI$_ENQNEWOUT},
+ {4, RMI$_ENQCVTLOC},
+ {4, RMI$_ENQCVTIN},
+ {4, RMI$_ENQCVTOUT},
+ {4, RMI$_DEQLOC},
+ {4, RMI$_DEQIN},
+ {4, RMI$_DEQOUT},
+ {4, RMI$_BLKLOC},
+ {4, RMI$_BLKIN},
+ {4, RMI$_BLKOUT},
+ {4, RMI$_DIRIN},
+ {4, RMI$_DIROUT},
+ /* We currently get a fault when trying these */
+#if 0
+ {140, RMI$_MSCP_EVERYTHING}, /* 35 32-bit words */
+ {152, RMI$_DDTM_ALL}, /* 38 32-bit words */
+ {80, RMI$_TMSCP_EVERYTHING} /* 20 32-bit words */
+#endif
+ {4, RMI$_LPZ_PAGCNT},
+ {4, RMI$_LPZ_HITS},
+ {4, RMI$_LPZ_MISSES},
+ {4, RMI$_LPZ_EXPCNT},
+ {4, RMI$_LPZ_ALLOCF},
+ {4, RMI$_LPZ_ALLOC2},
+ {4, RMI$_ACCESS},
+ {4, RMI$_ALLOC},
+ {4, RMI$_FCPCREATE},
+ {4, RMI$_VOLWAIT},
+ {4, RMI$_FCPTURN},
+ {4, RMI$_FCPERASE},
+ {4, RMI$_OPENS},
+ {4, RMI$_FIDHIT},
+ {4, RMI$_FIDMISS},
+ {4, RMI$_FILHDR_HIT},
+ {4, RMI$_DIRFCB_HIT},
+ {4, RMI$_DIRFCB_MISS},
+ {4, RMI$_DIRDATA_HIT},
+ {4, RMI$_EXTHIT},
+ {4, RMI$_EXTMISS},
+ {4, RMI$_QUOHIT},
+ {4, RMI$_QUOMISS},
+ {4, RMI$_STORAGMAP_HIT},
+ {4, RMI$_VOLLCK},
+ {4, RMI$_SYNCHLCK},
+ {4, RMI$_SYNCHWAIT},
+ {4, RMI$_ACCLCK},
+ {4, RMI$_XQPCACHEWAIT},
+ {4, RMI$_DIRDATA_MISS},
+ {4, RMI$_FILHDR_MISS},
+ {4, RMI$_STORAGMAP_MISS},
+ {4, RMI$_PROCCNTMAX},
+ {4, RMI$_PROCBATCNT},
+ {4, RMI$_PROCINTCNT},
+ {4, RMI$_PROCNETCNT},
+ {4, RMI$_PROCSWITCHCNT},
+ {4, RMI$_PROCBALSETCNT},
+ {4, RMI$_PROCLOADCNT},
+ {4, RMI$_BADFLTS},
+ {4, RMI$_EXEFAULTS},
+ {4, RMI$_HDRINSWAPS},
+ {4, RMI$_HDROUTSWAPS},
+ {4, RMI$_IOPAGCNT},
+ {4, RMI$_ISWPCNTPG},
+ {4, RMI$_OSWPCNT},
+ {4, RMI$_OSWPCNTPG},
+ {4, RMI$_RDFAULTS},
+ {4, RMI$_TRANSFLTS},
+ {4, RMI$_WRTFAULTS},
+#if defined __alpha
+ {4, RMI$_USERPAGES},
+#endif
+ {4, RMI$_VMSPAGES},
+ {4, RMI$_TTWRITES},
+ {4, RMI$_BUFOBJPAG},
+ {4, RMI$_BUFOBJPAGPEAK},
+ {4, RMI$_BUFOBJPAGS01},
+ {4, RMI$_BUFOBJPAGS2},
+ {4, RMI$_BUFOBJPAGMAXS01},
+ {4, RMI$_BUFOBJPAGMAXS2},
+ {4, RMI$_BUFOBJPAGPEAKS01},
+ {4, RMI$_BUFOBJPAGPEAKS2},
+ {4, RMI$_BUFOBJPGLTMAXS01},
+ {4, RMI$_BUFOBJPGLTMAXS2},
+ {4, RMI$_DLCK_INCMPLT},
+ {4, RMI$_DLCKMSGS_IN},
+ {4, RMI$_DLCKMSGS_OUT},
+ {4, RMI$_MCHKERRS},
+ {4, RMI$_MEMERRS},
+};
+
+static const struct item_st RMI_item_data_64bit[] = {
+#if defined __ia64
+ {8, RMI$_FRLIST},
+ {8, RMI$_MODLIST},
+#endif
+ {8, RMI$_LCKMGR_REQCNT},
+ {8, RMI$_LCKMGR_REQTIME},
+ {8, RMI$_LCKMGR_SPINCNT},
+ {8, RMI$_LCKMGR_SPINTIME},
+ {8, RMI$_CPUINTSTK},
+ {8, RMI$_CPUMPSYNCH},
+ {8, RMI$_CPUKERNEL},
+ {8, RMI$_CPUEXEC},
+ {8, RMI$_CPUSUPER},
+ {8, RMI$_CPUUSER},
+#if defined __ia64
+ {8, RMI$_USERPAGES},
+#endif
+ {8, RMI$_TQETOTAL},
+ {8, RMI$_TQESYSUB},
+ {8, RMI$_TQEUSRTIMR},
+ {8, RMI$_TQEUSRWAKE},
+};
+
+static const struct item_st SYI_item_data[] = {
+ {4, SYI$_PAGEFILE_FREE},
+};
+
+/*
+ * Input:
+ * items_data - an array of lengths and codes
+ * items_data_num - number of elements in that array
+ *
+ * Output:
+ * items - pre-allocated ILE3 array to be filled.
+ * It's assumed to have items_data_num elements plus
+ * one extra for the terminating NULL element
+ * databuffer - pre-allocated 32-bit word array.
+ *
+ * Returns the number of elements used in databuffer
+ */
+static size_t prepare_item_list(const struct item_st *items_input,
+ size_t items_input_num,
+ ILE3 *items,
+ uint32_t__ptr32 databuffer)
+{
+ size_t data_sz = 0;
+
+ for (; items_input_num-- > 0; items_input++, items++) {
+
+ items->ile3$w_code = items_input->code;
+ /* Special treatment of JPI$_FINALEXC */
+ if (items->ile3$w_code == JPI$_FINALEXC)
+ items->ile3$w_length = 4;
+ else
+ items->ile3$w_length = items_input->length;
+
+ items->ile3$ps_bufaddr = databuffer;
+ items->ile3$ps_retlen_addr = 0;
+
+ databuffer += items_input->length / sizeof(databuffer[0]);
+ data_sz += items_input->length;
+ }
+ /* Terminating NULL entry */
+ items->ile3$w_length = items->ile3$w_code = 0;
+ items->ile3$ps_bufaddr = items->ile3$ps_retlen_addr = NULL;
+
+ return data_sz / sizeof(databuffer[0]);
+}
+
+static void massage_JPI(ILE3 *items)
+{
+ /*
+ * Special treatment of JPI$_FINALEXC
+ * The result of that item's data buffer is a 32-bit address to a list of
+ * 4 32-bit words.
+ */
+ for (; items->ile3$w_length != 0; items++) {
+ if (items->ile3$w_code == JPI$_FINALEXC) {
+ uint32_t *data = items->ile3$ps_bufaddr;
+ uint32_t *ptr = (uint32_t *)*data;
+ size_t j;
+
+ /*
+ * We know we made space for 4 32-bit words, so we can do in-place
+ * replacement.
+ */
+ for (j = 0; j < 4; j++)
+ data[j] = ptr[j];
+
+ break;
+ }
+ }
+}
+
+/*
+ * This number expresses how many bits of data contain 1 bit of entropy.
+ *
+ * For the moment, we assume about 0.05 entropy bits per data bit, or 1
+ * bit of entropy per 20 data bits.
+ */
+#define ENTROPY_FACTOR 20
+
+size_t data_collect_method(RAND_POOL *pool)
+{
+ ILE3 JPI_items_64bit[OSSL_NELEM(JPI_item_data_64bit) + 1];
+ ILE3 RMI_items_64bit[OSSL_NELEM(RMI_item_data_64bit) + 1];
+ ILE3 DVI_items[OSSL_NELEM(DVI_item_data) + 1];
+ ILE3 JPI_items[OSSL_NELEM(JPI_item_data) + 1];
+ ILE3 RMI_items[OSSL_NELEM(RMI_item_data) + 1];
+ ILE3 SYI_items[OSSL_NELEM(SYI_item_data) + 1];
+ union {
+ /* This ensures buffer starts at 64 bit boundary */
+ uint64_t dummy;
+ uint32_t buffer[OSSL_NELEM(JPI_item_data_64bit) * 2
+ + OSSL_NELEM(RMI_item_data_64bit) * 2
+ + OSSL_NELEM(DVI_item_data)
+ + OSSL_NELEM(JPI_item_data)
+ + OSSL_NELEM(RMI_item_data)
+ + OSSL_NELEM(SYI_item_data)
+ + 4 /* For JPI$_FINALEXC */];
+ } data;
+ size_t total_elems = 0;
+ size_t total_length = 0;
+ size_t bytes_needed = ossl_rand_pool_bytes_needed(pool, ENTROPY_FACTOR);
+ size_t bytes_remaining = ossl_rand_pool_bytes_remaining(pool);
+
+ /* Take all the 64-bit items first, to ensure proper alignment of data */
+ total_elems +=
+ prepare_item_list(JPI_item_data_64bit, OSSL_NELEM(JPI_item_data_64bit),
+ JPI_items_64bit, &data.buffer[total_elems]);
+ total_elems +=
+ prepare_item_list(RMI_item_data_64bit, OSSL_NELEM(RMI_item_data_64bit),
+ RMI_items_64bit, &data.buffer[total_elems]);
+ /* Now the 32-bit items */
+ total_elems += prepare_item_list(DVI_item_data, OSSL_NELEM(DVI_item_data),
+ DVI_items, &data.buffer[total_elems]);
+ total_elems += prepare_item_list(JPI_item_data, OSSL_NELEM(JPI_item_data),
+ JPI_items, &data.buffer[total_elems]);
+ total_elems += prepare_item_list(RMI_item_data, OSSL_NELEM(RMI_item_data),
+ RMI_items, &data.buffer[total_elems]);
+ total_elems += prepare_item_list(SYI_item_data, OSSL_NELEM(SYI_item_data),
+ SYI_items, &data.buffer[total_elems]);
+ total_length = total_elems * sizeof(data.buffer[0]);
+
+ /* Fill data.buffer with various info bits from this process */
+ {
+ uint32_t status;
+ uint32_t efn;
+ IOSB iosb;
+ $DESCRIPTOR(SYSDEVICE, "SYS$SYSDEVICE:");
+
+ if ((status = sys$getdviw(EFN$C_ENF, 0, &SYSDEVICE, DVI_items,
+ 0, 0, 0, 0, 0)) != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items_64bit, 0, 0, 0))
+ != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$getjpiw(EFN$C_ENF, 0, 0, JPI_items, 0, 0, 0))
+ != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$getsyiw(EFN$C_ENF, 0, 0, SYI_items, 0, 0, 0))
+ != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ /*
+ * The RMI service is a bit special, as there is no synchronous
+ * variant, so we MUST create an event flag to synchronise on.
+ */
+ if ((status = lib$get_ef(&efn)) != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$getrmi(efn, 0, 0, RMI_items_64bit, &iosb, 0, 0))
+ != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
+ lib$signal(iosb.iosb$l_getxxi_status);
+ return 0;
+ }
+ if ((status = sys$getrmi(efn, 0, 0, RMI_items, &iosb, 0, 0))
+ != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if ((status = sys$synch(efn, &iosb)) != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ if (iosb.iosb$l_getxxi_status != SS$_NORMAL) {
+ lib$signal(iosb.iosb$l_getxxi_status);
+ return 0;
+ }
+ if ((status = lib$free_ef(&efn)) != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+ }
+
+ massage_JPI(JPI_items);
+
+ /*
+ * If we can't feed the requirements from the caller, we're in deep trouble.
+ */
+ if (!ossl_assert(total_length >= bytes_needed)) {
+ ERR_raise_data(ERR_LIB_RAND, RAND_R_RANDOM_POOL_UNDERFLOW,
+ "Needed: %zu, Available: %zu",
+ bytes_needed, total_length);
+ return 0;
+ }
+
+ /*
+ * Try not to overfeed the pool
+ */
+ if (total_length > bytes_remaining)
+ total_length = bytes_remaining;
+
+ /* We give the pessimistic value for the amount of entropy */
+ ossl_rand_pool_add(pool, (unsigned char *)data.buffer, total_length,
+ 8 * total_length / ENTROPY_FACTOR);
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+/*
+ * SYS$GET_ENTROPY METHOD
+ * ======================
+ *
+ * This is a high entropy method based on a new system service that is
+ * based on getentropy() from FreeBSD 12. It's only used if available,
+ * and its availability is detected at run-time.
+ *
+ * We assume that this function provides full entropy random output.
+ */
+#define PUBLIC_VECTORS "SYS$LIBRARY:SYS$PUBLIC_VECTORS.EXE"
+#define GET_ENTROPY "SYS$GET_ENTROPY"
+
+static int get_entropy_address_flag = 0;
+static int (*get_entropy_address)(void *buffer, size_t buffer_size) = NULL;
+static int init_get_entropy_address(void)
+{
+ if (get_entropy_address_flag == 0)
+ get_entropy_address = dlsym(dlopen(PUBLIC_VECTORS, 0), GET_ENTROPY);
+ get_entropy_address_flag = 1;
+ return get_entropy_address != NULL;
+}
+
+size_t get_entropy_method(RAND_POOL *pool)
+{
+ /*
+ * The documentation says that SYS$GET_ENTROPY will give a maximum of
+ * 256 bytes of data.
+ */
+ unsigned char buffer[256];
+ size_t bytes_needed;
+ size_t bytes_to_get = 0;
+ uint32_t status;
+
+ for (bytes_needed = ossl_rand_pool_bytes_needed(pool, 1);
+ bytes_needed > 0;
+ bytes_needed -= bytes_to_get) {
+ bytes_to_get =
+ bytes_needed > sizeof(buffer) ? sizeof(buffer) : bytes_needed;
+
+ status = get_entropy_address(buffer, bytes_to_get);
+ if (status == SS$_RETRY) {
+ /* Set to zero so the loop doesn't diminish |bytes_needed| */
+ bytes_to_get = 0;
+ /* Should sleep some amount of time */
+ continue;
+ }
+
+ if (status != SS$_NORMAL) {
+ lib$signal(status);
+ return 0;
+ }
+
+ ossl_rand_pool_add(pool, buffer, bytes_to_get, 8 * bytes_to_get);
+ }
+
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+/*
+ * MAIN ENTROPY ACQUISITION FUNCTIONS
+ * ==================================
+ *
+ * These functions are called by the RAND / DRBG functions
+ */
+
+size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
+{
+ if (init_get_entropy_address())
+ return get_entropy_method(pool);
+ return data_collect_method(pool);
+}
+
+int ossl_pool_add_nonce_data(RAND_POOL *pool)
+{
+ /*
+ * Two variables to ensure that two nonces won't ever be the same
+ */
+ static unsigned __int64 last_time = 0;
+ static unsigned __int32 last_seq = 0;
+
+ struct {
+ pid_t pid;
+ CRYPTO_THREAD_ID tid;
+ unsigned __int64 time;
+ unsigned __int32 seq;
+ } data;
+
+ /* Erase the entire structure including any padding */
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Add process id, thread id, a timestamp, and a sequence number in case
+ * the same time stamp is repeated, to ensure that the nonce is unique
+ * with high probability for different process instances.
+ *
+ * The normal OpenVMS time is specified to be high granularity (100ns),
+ * but the time update granularity given by sys$gettim() may be lower.
+ *
+ * OpenVMS version 8.4 (which is the latest for Alpha and Itanium) and
+ * on have sys$gettim_prec() as well, which is supposedly having a better
+ * time update granularity, but tests on Itanium (and even Alpha) have
+ * shown that compared with sys$gettim(), the difference is marginal,
+ * so of very little significance in terms of entropy.
+ * Given that, and that it's a high ask to expect everyone to have
+ * upgraded to OpenVMS version 8.4, only sys$gettim() is used, and a
+ * sequence number is added as well, in case sys$gettim() returns the
+ * same time value more than once.
+ *
+ * This function is assumed to be called under thread lock, and does
+ * therefore not take concurrency into account.
+ */
+ data.pid = getpid();
+ data.tid = CRYPTO_THREAD_get_current_id();
+ data.seq = 0;
+ sys$gettim((void*)&data.time);
+
+ if (data.time == last_time) {
+ data.seq = ++last_seq;
+ } else {
+ last_time = data.time;
+ last_seq = 0;
+ }
+
+ return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+int ossl_rand_pool_init(void)
+{
+ return 1;
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+}
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c b/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c
new file mode 100644
index 000000000000..64acf6903f9c
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_vxworks.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2019-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/opensslconf.h>
+
+#include <openssl/rand.h>
+#include "crypto/rand_pool.h"
+#include "crypto/rand.h"
+#include "internal/cryptlib.h"
+#include "prov/seeding.h"
+#include <version.h>
+#include <taskLib.h>
+
+#if defined(OPENSSL_RAND_SEED_NONE)
+/* none means none */
+# undef OPENSSL_RAND_SEED_OS
+#endif
+
+#if defined(OPENSSL_RAND_SEED_OS)
+# if _WRS_VXWORKS_MAJOR >= 7
+# define RAND_SEED_VXRANDLIB
+# else
+# error "VxWorks <7 only support RAND_SEED_NONE"
+# endif
+#endif
+
+#if defined(RAND_SEED_VXRANDLIB)
+# include <randomNumGen.h>
+#endif
+
+/* Macro to convert two thirty two bit values into a sixty four bit one */
+#define TWO32TO64(a, b) ((((uint64_t)(a)) << 32) + (b))
+
+static uint64_t get_time_stamp(void)
+{
+ struct timespec ts;
+
+ if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
+ return TWO32TO64(ts.tv_sec, ts.tv_nsec);
+ return time(NULL);
+}
+
+static uint64_t get_timer_bits(void)
+{
+ uint64_t res = OPENSSL_rdtsc();
+ struct timespec ts;
+
+ if (res != 0)
+ return res;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
+ return TWO32TO64(ts.tv_sec, ts.tv_nsec);
+ return time(NULL);
+}
+
+/*
+ * empty implementation
+ * vxworks does not need to init/cleanup or keep open the random lib
+ */
+int ossl_rand_pool_init(void)
+{
+ return 1;
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+}
+
+int ossl_pool_add_nonce_data(RAND_POOL *pool)
+{
+ struct {
+ pid_t pid;
+ CRYPTO_THREAD_ID tid;
+ uint64_t time;
+ } data;
+
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Add process id, thread id, and a high resolution timestamp to
+ * ensure that the nonce is unique with high probability for
+ * different process instances.
+ */
+ data.pid = getpid();
+ data.tid = CRYPTO_THREAD_get_current_id();
+ data.time = get_time_stamp();
+
+ return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
+{
+#if defined(RAND_SEED_VXRANDLIB)
+ /* vxRandLib based entropy method */
+ size_t bytes_needed;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ if (bytes_needed > 0) {
+ int retryCount = 0;
+ STATUS result = ERROR;
+ unsigned char *buffer;
+
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ while ((result != OK) && (retryCount < 10)) {
+ RANDOM_NUM_GEN_STATUS status = randStatus();
+
+ if ((status == RANDOM_NUM_GEN_ENOUGH_ENTROPY)
+ || (status == RANDOM_NUM_GEN_MAX_ENTROPY)) {
+ result = randBytes(buffer, bytes_needed);
+ if (result == OK)
+ ossl_rand_pool_add_end(pool, bytes_needed, 8 * bytes_needed);
+ /*
+ * no else here: randStatus said ok, if randBytes failed
+ * it will result in another loop or no entropy
+ */
+ } else {
+ /*
+ * give a minimum delay here to allow OS to collect more
+ * entropy. taskDelay duration will depend on the system tick,
+ * this is by design as the sw-random lib uses interrupts
+ * which will at least happen during ticks
+ */
+ taskDelay(5);
+ }
+ retryCount++;
+ }
+ }
+ return ossl_rand_pool_entropy_available(pool);
+#else
+ /*
+ * SEED_NONE means none, without randlib we dont have entropy and
+ * rely on it being added externally
+ */
+ return ossl_rand_pool_entropy_available(pool);
+#endif /* defined(RAND_SEED_VXRANDLIB) */
+}
diff --git a/crypto/openssl/providers/implementations/rands/seeding/rand_win.c b/crypto/openssl/providers/implementations/rands/seeding/rand_win.c
new file mode 100644
index 000000000000..ee2d3e4d7f61
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/seeding/rand_win.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/cryptlib.h"
+#include <openssl/rand.h>
+#include "crypto/rand_pool.h"
+#include "crypto/rand.h"
+#include "prov/seeding.h"
+
+#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32)
+
+# ifndef OPENSSL_RAND_SEED_OS
+# error "Unsupported seeding method configured; must be os"
+# endif
+
+# include <windows.h>
+/* On Windows Vista or higher use BCrypt instead of the legacy CryptoAPI */
+# if defined(_MSC_VER) && _MSC_VER > 1500 /* 1500 = Visual Studio 2008 */ \
+ && defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600
+# define USE_BCRYPTGENRANDOM
+# endif
+
+# ifdef USE_BCRYPTGENRANDOM
+# include <bcrypt.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "bcrypt.lib")
+# endif
+# ifndef STATUS_SUCCESS
+# define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
+# endif
+# else
+# include <wincrypt.h>
+/*
+ * Intel hardware RNG CSP -- available from
+ * http://developer.intel.com/design/security/rng/redist_license.htm
+ */
+# define PROV_INTEL_SEC 22
+# define INTEL_DEF_PROV L"Intel Hardware Cryptographic Service Provider"
+# endif
+
+size_t ossl_pool_acquire_entropy(RAND_POOL *pool)
+{
+# ifndef USE_BCRYPTGENRANDOM
+ HCRYPTPROV hProvider;
+# endif
+ unsigned char *buffer;
+ size_t bytes_needed;
+ size_t entropy_available = 0;
+
+
+# ifdef OPENSSL_RAND_SEED_RDTSC
+ entropy_available = ossl_prov_acquire_entropy_from_tsc(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+# ifdef OPENSSL_RAND_SEED_RDCPU
+ entropy_available = ossl_prov_acquire_entropy_from_cpu(pool);
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+# ifdef USE_BCRYPTGENRANDOM
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ if (BCryptGenRandom(NULL, buffer, bytes_needed,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG) == STATUS_SUCCESS)
+ bytes = bytes_needed;
+
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ }
+ if (entropy_available > 0)
+ return entropy_available;
+# else
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ /* poll the CryptoAPI PRNG */
+ if (CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
+ if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0)
+ bytes = bytes_needed;
+
+ CryptReleaseContext(hProvider, 0);
+ }
+
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ }
+ if (entropy_available > 0)
+ return entropy_available;
+
+ bytes_needed = ossl_rand_pool_bytes_needed(pool, 1 /*entropy_factor*/);
+ buffer = ossl_rand_pool_add_begin(pool, bytes_needed);
+ if (buffer != NULL) {
+ size_t bytes = 0;
+ /* poll the Pentium PRG with CryptoAPI */
+ if (CryptAcquireContextW(&hProvider, NULL,
+ INTEL_DEF_PROV, PROV_INTEL_SEC,
+ CRYPT_VERIFYCONTEXT | CRYPT_SILENT) != 0) {
+ if (CryptGenRandom(hProvider, bytes_needed, buffer) != 0)
+ bytes = bytes_needed;
+
+ CryptReleaseContext(hProvider, 0);
+ }
+ ossl_rand_pool_add_end(pool, bytes, 8 * bytes);
+ entropy_available = ossl_rand_pool_entropy_available(pool);
+ }
+ if (entropy_available > 0)
+ return entropy_available;
+# endif
+
+ return ossl_rand_pool_entropy_available(pool);
+}
+
+
+int ossl_pool_add_nonce_data(RAND_POOL *pool)
+{
+ struct {
+ DWORD pid;
+ DWORD tid;
+ FILETIME time;
+ } data;
+
+ /* Erase the entire structure including any padding */
+ memset(&data, 0, sizeof(data));
+
+ /*
+ * Add process id, thread id, and a high resolution timestamp to
+ * ensure that the nonce is unique with high probability for
+ * different process instances.
+ */
+ data.pid = GetCurrentProcessId();
+ data.tid = GetCurrentThreadId();
+ GetSystemTimeAsFileTime(&data.time);
+
+ return ossl_rand_pool_add(pool, (unsigned char *)&data, sizeof(data), 0);
+}
+
+int ossl_rand_pool_init(void)
+{
+ return 1;
+}
+
+void ossl_rand_pool_cleanup(void)
+{
+}
+
+void ossl_rand_pool_keep_random_devices_open(int keep)
+{
+}
+
+#endif
diff --git a/crypto/openssl/providers/implementations/rands/test_rng.c b/crypto/openssl/providers/implementations/rands/test_rng.c
new file mode 100644
index 000000000000..e81c82d23fa8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/rands/test_rng.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/e_os2.h>
+#include <openssl/params.h>
+#include <openssl/core_names.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/randerr.h>
+#include "prov/securitycheck.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/provider_util.h"
+#include "prov/implementations.h"
+
+static OSSL_FUNC_rand_newctx_fn test_rng_new;
+static OSSL_FUNC_rand_freectx_fn test_rng_free;
+static OSSL_FUNC_rand_instantiate_fn test_rng_instantiate;
+static OSSL_FUNC_rand_uninstantiate_fn test_rng_uninstantiate;
+static OSSL_FUNC_rand_generate_fn test_rng_generate;
+static OSSL_FUNC_rand_reseed_fn test_rng_reseed;
+static OSSL_FUNC_rand_nonce_fn test_rng_nonce;
+static OSSL_FUNC_rand_settable_ctx_params_fn test_rng_settable_ctx_params;
+static OSSL_FUNC_rand_set_ctx_params_fn test_rng_set_ctx_params;
+static OSSL_FUNC_rand_gettable_ctx_params_fn test_rng_gettable_ctx_params;
+static OSSL_FUNC_rand_get_ctx_params_fn test_rng_get_ctx_params;
+static OSSL_FUNC_rand_verify_zeroization_fn test_rng_verify_zeroization;
+static OSSL_FUNC_rand_enable_locking_fn test_rng_enable_locking;
+static OSSL_FUNC_rand_lock_fn test_rng_lock;
+static OSSL_FUNC_rand_unlock_fn test_rng_unlock;
+static OSSL_FUNC_rand_get_seed_fn test_rng_get_seed;
+
+typedef struct {
+ void *provctx;
+ unsigned int generate;
+ int state;
+ unsigned int strength;
+ size_t max_request;
+ unsigned char *entropy, *nonce;
+ size_t entropy_len, entropy_pos, nonce_len;
+ CRYPTO_RWLOCK *lock;
+ uint32_t seed;
+} PROV_TEST_RNG;
+
+static void *test_rng_new(void *provctx, void *parent,
+ const OSSL_DISPATCH *parent_dispatch)
+{
+ PROV_TEST_RNG *t;
+
+ t = OPENSSL_zalloc(sizeof(*t));
+ if (t == NULL)
+ return NULL;
+
+ t->max_request = INT_MAX;
+ t->provctx = provctx;
+ t->state = EVP_RAND_STATE_UNINITIALISED;
+ return t;
+}
+
+static void test_rng_free(void *vtest)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ if (t == NULL)
+ return;
+ OPENSSL_free(t->entropy);
+ OPENSSL_free(t->nonce);
+ CRYPTO_THREAD_lock_free(t->lock);
+ OPENSSL_free(t);
+}
+
+static int test_rng_instantiate(void *vtest, unsigned int strength,
+ int prediction_resistance,
+ const unsigned char *pstr, size_t pstr_len,
+ const OSSL_PARAM params[])
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ if (!test_rng_set_ctx_params(t, params) || strength > t->strength)
+ return 0;
+
+ t->state = EVP_RAND_STATE_READY;
+ t->entropy_pos = 0;
+ t->seed = 221953166; /* Value doesn't matter, so long as it isn't zero */
+
+ return 1;
+}
+
+static int test_rng_uninstantiate(void *vtest)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ t->entropy_pos = 0;
+ t->state = EVP_RAND_STATE_UNINITIALISED;
+ return 1;
+}
+
+static unsigned char gen_byte(PROV_TEST_RNG *t)
+{
+ uint32_t n;
+
+ /*
+ * Implement the 32 bit xorshift as suggested by George Marsaglia in:
+ * https://doi.org/10.18637/jss.v008.i14
+ *
+ * This is a very fast PRNG so there is no need to extract bytes one at a
+ * time and use the entire value each time.
+ */
+ n = t->seed;
+ n ^= n << 13;
+ n ^= n >> 17;
+ n ^= n << 5;
+ t->seed = n;
+
+ return n & 0xff;
+}
+
+static int test_rng_generate(void *vtest, unsigned char *out, size_t outlen,
+ unsigned int strength, int prediction_resistance,
+ const unsigned char *adin, size_t adin_len)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+ size_t i;
+
+ if (strength > t->strength)
+ return 0;
+ if (t->generate) {
+ for (i = 0; i < outlen; i++)
+ out[i] = gen_byte(t);
+ } else {
+ if (t->entropy_len - t->entropy_pos < outlen)
+ return 0;
+
+ memcpy(out, t->entropy + t->entropy_pos, outlen);
+ t->entropy_pos += outlen;
+ }
+ return 1;
+}
+
+static int test_rng_reseed(ossl_unused void *vtest,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *ent,
+ ossl_unused size_t ent_len,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ return 1;
+}
+
+static size_t test_rng_nonce(void *vtest, unsigned char *out,
+ unsigned int strength, size_t min_noncelen,
+ size_t max_noncelen)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+ size_t i;
+
+ if (strength > t->strength)
+ return 0;
+
+ if (t->generate) {
+ for (i = 0; i < min_noncelen; i++)
+ out[i] = gen_byte(t);
+ return min_noncelen;
+ }
+
+ if (t->nonce == NULL)
+ return 0;
+ i = t->nonce_len > max_noncelen ? max_noncelen : t->nonce_len;
+ if (out != NULL)
+ memcpy(out, t->nonce, i);
+ return i;
+}
+
+static int test_rng_get_ctx_params(void *vtest, OSSL_PARAM params[])
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+ OSSL_PARAM *p;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STATE);
+ if (p != NULL && !OSSL_PARAM_set_int(p, t->state))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_set_int(p, t->strength))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, t->max_request))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_GENERATE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, t->generate))
+ return 0;
+
+#ifdef FIPS_MODULE
+ p = OSSL_PARAM_locate(params, OSSL_RAND_PARAM_FIPS_APPROVED_INDICATOR);
+ if (p != NULL && !OSSL_PARAM_set_int(p, 0))
+ return 0;
+#endif /* FIPS_MODULE */
+ return 1;
+}
+
+static const OSSL_PARAM *test_rng_gettable_ctx_params(ossl_unused void *vtest,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_int(OSSL_RAND_PARAM_STATE, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_GENERATE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+ };
+ return known_gettable_ctx_params;
+}
+
+static int test_rng_set_ctx_params(void *vtest, const OSSL_PARAM params[])
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+ const OSSL_PARAM *p;
+ void *ptr = NULL;
+ size_t size = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_STRENGTH);
+ if (p != NULL && !OSSL_PARAM_get_uint(p, &t->strength))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_TEST_ENTROPY);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size))
+ return 0;
+ OPENSSL_free(t->entropy);
+ t->entropy = ptr;
+ t->entropy_len = size;
+ t->entropy_pos = 0;
+ ptr = NULL;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_TEST_NONCE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_octet_string(p, &ptr, 0, &size))
+ return 0;
+ OPENSSL_free(t->nonce);
+ t->nonce = ptr;
+ t->nonce_len = size;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_MAX_REQUEST);
+ if (p != NULL && !OSSL_PARAM_get_size_t(p, &t->max_request))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_RAND_PARAM_GENERATE);
+ if (p != NULL && !OSSL_PARAM_get_uint(p, &t->generate))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *test_rng_settable_ctx_params(ossl_unused void *vtest,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_RAND_PARAM_TEST_ENTROPY, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_RAND_PARAM_TEST_NONCE, NULL, 0),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_STRENGTH, NULL),
+ OSSL_PARAM_size_t(OSSL_RAND_PARAM_MAX_REQUEST, NULL),
+ OSSL_PARAM_uint(OSSL_RAND_PARAM_GENERATE, NULL),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int test_rng_verify_zeroization(ossl_unused void *vtest)
+{
+ return 1;
+}
+
+static size_t test_rng_get_seed(void *vtest, unsigned char **pout,
+ int entropy, size_t min_len, size_t max_len,
+ ossl_unused int prediction_resistance,
+ ossl_unused const unsigned char *adin,
+ ossl_unused size_t adin_len)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ *pout = t->entropy;
+ return t->entropy_len > max_len ? max_len : t->entropy_len;
+}
+
+static int test_rng_enable_locking(void *vtest)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ if (t != NULL && t->lock == NULL) {
+ t->lock = CRYPTO_THREAD_lock_new();
+ if (t->lock == NULL) {
+ ERR_raise(ERR_LIB_PROV, RAND_R_FAILED_TO_CREATE_LOCK);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int test_rng_lock(void *vtest)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ if (t == NULL || t->lock == NULL)
+ return 1;
+ return CRYPTO_THREAD_write_lock(t->lock);
+}
+
+static void test_rng_unlock(void *vtest)
+{
+ PROV_TEST_RNG *t = (PROV_TEST_RNG *)vtest;
+
+ if (t != NULL && t->lock != NULL)
+ CRYPTO_THREAD_unlock(t->lock);
+}
+
+const OSSL_DISPATCH ossl_test_rng_functions[] = {
+ { OSSL_FUNC_RAND_NEWCTX, (void(*)(void))test_rng_new },
+ { OSSL_FUNC_RAND_FREECTX, (void(*)(void))test_rng_free },
+ { OSSL_FUNC_RAND_INSTANTIATE,
+ (void(*)(void))test_rng_instantiate },
+ { OSSL_FUNC_RAND_UNINSTANTIATE,
+ (void(*)(void))test_rng_uninstantiate },
+ { OSSL_FUNC_RAND_GENERATE, (void(*)(void))test_rng_generate },
+ { OSSL_FUNC_RAND_RESEED, (void(*)(void))test_rng_reseed },
+ { OSSL_FUNC_RAND_NONCE, (void(*)(void))test_rng_nonce },
+ { OSSL_FUNC_RAND_ENABLE_LOCKING, (void(*)(void))test_rng_enable_locking },
+ { OSSL_FUNC_RAND_LOCK, (void(*)(void))test_rng_lock },
+ { OSSL_FUNC_RAND_UNLOCK, (void(*)(void))test_rng_unlock },
+ { OSSL_FUNC_RAND_SETTABLE_CTX_PARAMS,
+ (void(*)(void))test_rng_settable_ctx_params },
+ { OSSL_FUNC_RAND_SET_CTX_PARAMS, (void(*)(void))test_rng_set_ctx_params },
+ { OSSL_FUNC_RAND_GETTABLE_CTX_PARAMS,
+ (void(*)(void))test_rng_gettable_ctx_params },
+ { OSSL_FUNC_RAND_GET_CTX_PARAMS, (void(*)(void))test_rng_get_ctx_params },
+ { OSSL_FUNC_RAND_VERIFY_ZEROIZATION,
+ (void(*)(void))test_rng_verify_zeroization },
+ { OSSL_FUNC_RAND_GET_SEED, (void(*)(void))test_rng_get_seed },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/signature/build.info b/crypto/openssl/providers/implementations/signature/build.info
new file mode 100644
index 000000000000..0fd39841f340
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/build.info
@@ -0,0 +1,45 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$DSA_GOAL=../../libdefault.a ../../libfips.a
+$EC_GOAL=../../libdefault.a ../../libfips.a
+$MAC_GOAL=../../libdefault.a ../../libfips.a
+$RSA_GOAL=../../libdefault.a ../../libfips.a
+$SM2_GOAL=../../libdefault.a
+$ML_DSA_GOAL=../../libdefault.a ../../libfips.a
+$SLH_DSA_GOAL=../../libdefault.a ../../libfips.a
+
+IF[{- !$disabled{dsa} -}]
+ SOURCE[$DSA_GOAL]=dsa_sig.c
+ENDIF
+
+IF[{- !$disabled{ec} -}]
+ SOURCE[$EC_GOAL]=ecdsa_sig.c
+ IF[{- !$disabled{ecx} -}]
+ SOURCE[$EC_GOAL]=eddsa_sig.c
+ ENDIF
+ENDIF
+
+IF[{- !$disabled{sm2} -}]
+ SOURCE[$SM2_GOAL]=sm2_sig.c
+ENDIF
+
+SOURCE[$RSA_GOAL]=rsa_sig.c
+
+DEPEND[ml_dsa_sig.o]=../../common/include/prov/der_ml_dsa.h
+DEPEND[slh_dsa_sig.o]=../../common/include/prov/der_slh_dsa.h
+DEPEND[rsa_sig.o]=../../common/include/prov/der_rsa.h
+DEPEND[dsa_sig.o]=../../common/include/prov/der_dsa.h
+DEPEND[ecdsa_sig.o]=../../common/include/prov/der_ec.h
+DEPEND[eddsa_sig.o]=../../common/include/prov/der_ecx.h
+DEPEND[sm2_sig.o]=../../common/include/prov/der_sm2.h
+
+SOURCE[$MAC_GOAL]=mac_legacy_sig.c
+
+IF[{- !$disabled{'ml-dsa'} -}]
+ SOURCE[$ML_DSA_GOAL]=ml_dsa_sig.c
+ENDIF
+
+IF[{- !$disabled{'slh-dsa'} -}]
+ SOURCE[$DSA_GOAL]=slh_dsa_sig.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/signature/dsa_sig.c b/crypto/openssl/providers/implementations/signature/dsa_sig.c
new file mode 100644
index 000000000000..887f6cbb9018
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/dsa_sig.c
@@ -0,0 +1,1085 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * DSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/dsa.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "internal/cryptlib.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+#include "prov/der_dsa.h"
+#include "crypto/dsa.h"
+
+static OSSL_FUNC_signature_newctx_fn dsa_newctx;
+static OSSL_FUNC_signature_sign_init_fn dsa_sign_init;
+static OSSL_FUNC_signature_verify_init_fn dsa_verify_init;
+static OSSL_FUNC_signature_sign_fn dsa_sign;
+static OSSL_FUNC_signature_sign_message_update_fn dsa_signverify_message_update;
+static OSSL_FUNC_signature_sign_message_final_fn dsa_sign_message_final;
+static OSSL_FUNC_signature_verify_fn dsa_verify;
+static OSSL_FUNC_signature_verify_message_update_fn dsa_signverify_message_update;
+static OSSL_FUNC_signature_verify_message_final_fn dsa_verify_message_final;
+static OSSL_FUNC_signature_digest_sign_init_fn dsa_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn dsa_digest_signverify_update;
+static OSSL_FUNC_signature_digest_sign_final_fn dsa_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn dsa_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_update_fn dsa_digest_signverify_update;
+static OSSL_FUNC_signature_digest_verify_final_fn dsa_digest_verify_final;
+static OSSL_FUNC_signature_freectx_fn dsa_freectx;
+static OSSL_FUNC_signature_dupctx_fn dsa_dupctx;
+static OSSL_FUNC_signature_query_key_types_fn dsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_get_ctx_params_fn dsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn dsa_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn dsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn dsa_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn dsa_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn dsa_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn dsa_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn dsa_settable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_params_fn dsa_sigalg_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn dsa_sigalg_settable_ctx_params;
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes DSA structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ DSA *dsa;
+ /* |operation| reuses EVP's operation bitfield */
+ int operation;
+
+ /*
+ * Flag to determine if a full sigalg is run (1) or if a composable
+ * signature algorithm is run (0).
+ *
+ * When a full sigalg is run (1), this currently affects the following
+ * other flags, which are to remain untouched after their initialization:
+ *
+ * - flag_allow_md (initialized to 0)
+ */
+ unsigned int flag_sigalg : 1;
+ /*
+ * Flag to determine if the hash function can be changed (1) or not (0)
+ * Because it's dangerous to change during a DigestSign or DigestVerify
+ * operation, this flag is cleared by their Init function, and set again
+ * by their Final function.
+ */
+ unsigned int flag_allow_md : 1;
+
+ /* If this is set to 1 then the generated k is not random */
+ unsigned int nonce_type;
+
+ /* The Algorithm Identifier of the combined signature algorithm */
+ unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+
+ /* main digest */
+ char mdname[OSSL_MAX_NAME_SIZE];
+ EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+
+ /* Signature, for verification */
+ unsigned char *sig;
+ size_t siglen;
+
+ OSSL_FIPS_IND_DECLARE
+} PROV_DSA_CTX;
+
+static size_t dsa_get_md_size(const PROV_DSA_CTX *pdsactx)
+{
+ int md_size;
+
+ if (pdsactx->md != NULL) {
+ md_size = EVP_MD_get_size(pdsactx->md);
+ if (md_size <= 0)
+ return 0;
+ return (size_t)md_size;
+ }
+ return 0;
+}
+
+static void *dsa_newctx(void *provctx, const char *propq)
+{
+ PROV_DSA_CTX *pdsactx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ pdsactx = OPENSSL_zalloc(sizeof(PROV_DSA_CTX));
+ if (pdsactx == NULL)
+ return NULL;
+
+ pdsactx->libctx = PROV_LIBCTX_OF(provctx);
+ pdsactx->flag_allow_md = 1;
+ OSSL_FIPS_IND_INIT(pdsactx)
+ if (propq != NULL && (pdsactx->propq = OPENSSL_strdup(propq)) == NULL) {
+ OPENSSL_free(pdsactx);
+ pdsactx = NULL;
+ }
+ return pdsactx;
+}
+
+static int dsa_setup_md(PROV_DSA_CTX *ctx,
+ const char *mdname, const char *mdprops,
+ const char *desc)
+{
+ EVP_MD *md = NULL;
+
+ if (mdprops == NULL)
+ mdprops = ctx->propq;
+
+ if (mdname != NULL) {
+ WPACKET pkt;
+ int md_nid;
+ size_t mdname_len = strlen(mdname);
+ unsigned char *aid = NULL;
+
+ md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
+ md_nid = ossl_digest_get_approved_nid(md);
+
+ if (md == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s could not be fetched", mdname);
+ goto err;
+ }
+ if (md_nid == NID_undef) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest=%s", mdname);
+ goto err;
+ }
+ if (mdname_len >= sizeof(ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s exceeds name buffer length", mdname);
+ goto err;
+ }
+ /* XOF digests don't work */
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ goto err;
+ }
+#ifdef FIPS_MODULE
+ {
+ int sha1_allowed
+ = ((ctx->operation
+ & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0);
+
+ if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE1,
+ ctx->libctx,
+ md_nid, sha1_allowed, 0, desc,
+ ossl_fips_config_signature_digest_check))
+ goto err;
+ }
+#endif
+
+ if (!ctx->flag_allow_md) {
+ if (ctx->mdname[0] != '\0'
+ && !EVP_MD_is_a(md, ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest %s != %s", mdname, ctx->mdname);
+ goto err;
+ }
+ EVP_MD_free(md);
+ return 1;
+ }
+
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+
+ /*
+ * We do not care about DER writing errors.
+ * All it really means is that for some reason, there's no
+ * AlgorithmIdentifier to be had, but the operation itself is
+ * still valid, just as long as it's not used to construct
+ * anything that needs an AlgorithmIdentifier.
+ */
+ ctx->aid_len = 0;
+ if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf))
+ && ossl_DER_w_algorithmIdentifier_DSA_with_MD(&pkt, -1, ctx->dsa,
+ md_nid)
+ && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &ctx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && ctx->aid_len != 0)
+ memmove(ctx->aid_buf, aid, ctx->aid_len);
+
+ ctx->mdctx = NULL;
+ ctx->md = md;
+ OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname));
+ }
+
+ return 1;
+ err:
+ EVP_MD_free(md);
+ return 0;
+}
+
+#ifdef FIPS_MODULE
+
+static int dsa_sign_check_approved(PROV_DSA_CTX *ctx, int signing,
+ const char *desc)
+{
+ /* DSA Signing is not approved in FIPS 140-3 */
+ if (signing
+ && !OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2,
+ ctx->libctx, desc, "DSA",
+ ossl_fips_config_dsa_sign_disallowed))
+ return 0;
+ return 1;
+}
+
+static int dsa_check_key(PROV_DSA_CTX *ctx, int sign, const char *desc)
+{
+ int approved = ossl_dsa_check_key(ctx->dsa, sign);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE0,
+ ctx->libctx, desc, "DSA Key",
+ ossl_fips_config_signature_digest_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int
+dsa_signverify_init(void *vpdsactx, void *vdsa,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (!ossl_prov_is_running()
+ || pdsactx == NULL)
+ return 0;
+
+ if (vdsa == NULL && pdsactx->dsa == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (vdsa != NULL) {
+ if (!DSA_up_ref(vdsa))
+ return 0;
+ DSA_free(pdsactx->dsa);
+ pdsactx->dsa = vdsa;
+ }
+
+ pdsactx->operation = operation;
+
+ OSSL_FIPS_IND_SET_APPROVED(pdsactx)
+ if (!set_ctx_params(pdsactx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ {
+ int operation_is_sign
+ = (operation & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) != 0;
+
+ if (!dsa_sign_check_approved(pdsactx, operation_is_sign, desc))
+ return 0;
+ if (!dsa_check_key(pdsactx, operation_is_sign, desc))
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static int dsa_sign_init(void *vpdsactx, void *vdsa, const OSSL_PARAM params[])
+{
+ return dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params,
+ EVP_PKEY_OP_SIGN, "DSA Sign Init");
+}
+
+/*
+ * Sign tbs without digesting it first. This is suitable for "primitive"
+ * signing and signing the digest of a message, i.e. should be used with
+ * implementations of the keytype related algorithms.
+ */
+static int dsa_sign_directly(void *vpdsactx,
+ unsigned char *sig, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ int ret;
+ unsigned int sltmp;
+ size_t dsasize = DSA_size(pdsactx->dsa);
+ size_t mdsize = dsa_get_md_size(pdsactx);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef FIPS_MODULE
+ if (!dsa_sign_check_approved(pdsactx, 1, "Sign"))
+ return 0;
+#endif
+
+ if (sig == NULL) {
+ *siglen = dsasize;
+ return 1;
+ }
+
+ if (sigsize < dsasize)
+ return 0;
+
+ if (mdsize != 0 && tbslen != mdsize)
+ return 0;
+
+ ret = ossl_dsa_sign_int(0, tbs, tbslen, sig, &sltmp, pdsactx->dsa,
+ pdsactx->nonce_type, pdsactx->mdname,
+ pdsactx->libctx, pdsactx->propq);
+ if (ret <= 0)
+ return 0;
+
+ *siglen = sltmp;
+ return 1;
+}
+
+static int dsa_signverify_message_update(void *vpdsactx,
+ const unsigned char *data,
+ size_t datalen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx == NULL)
+ return 0;
+
+ return EVP_DigestUpdate(pdsactx->mdctx, data, datalen);
+}
+
+static int dsa_sign_message_final(void *vpdsactx, unsigned char *sig,
+ size_t *siglen, size_t sigsize)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running() || pdsactx == NULL || pdsactx->mdctx == NULL)
+ return 0;
+ /*
+ * If sig is NULL then we're just finding out the sig size. Other fields
+ * are ignored. Defer to dsa_sign.
+ */
+ if (sig != NULL) {
+ /*
+ * When this function is used through dsa_digest_sign_final(),
+ * there is the possibility that some externally provided digests
+ * exceed EVP_MAX_MD_SIZE. We should probably handle that
+ * somehow but that problem is much larger than just in DSA.
+ */
+ if (!EVP_DigestFinal_ex(pdsactx->mdctx, digest, &dlen))
+ return 0;
+ }
+
+ return dsa_sign_directly(vpdsactx, sig, siglen, sigsize, digest, dlen);
+}
+
+/*
+ * If signing a message, digest tbs and sign the result.
+ * Otherwise, sign tbs directly.
+ */
+static int dsa_sign(void *vpdsactx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *tbs, size_t tbslen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->operation == EVP_PKEY_OP_SIGNMSG) {
+ /*
+ * If |sig| is NULL, the caller is only looking for the sig length.
+ * DO NOT update the input in this case.
+ */
+ if (sig == NULL)
+ return dsa_sign_message_final(pdsactx, sig, siglen, sigsize);
+
+ if (dsa_signverify_message_update(pdsactx, tbs, tbslen) <= 0)
+ return 0;
+ return dsa_sign_message_final(pdsactx, sig, siglen, sigsize);
+ }
+ return dsa_sign_directly(pdsactx, sig, siglen, sigsize, tbs, tbslen);
+}
+
+static int dsa_verify_init(void *vpdsactx, void *vdsa,
+ const OSSL_PARAM params[])
+{
+ return dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params,
+ EVP_PKEY_OP_VERIFY, "DSA Verify Init");
+}
+
+static int dsa_verify_directly(void *vpdsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ size_t mdsize = dsa_get_md_size(pdsactx);
+
+ if (!ossl_prov_is_running() || (mdsize != 0 && tbslen != mdsize))
+ return 0;
+
+ return DSA_verify(0, tbs, tbslen, sig, siglen, pdsactx->dsa);
+}
+
+static int dsa_verify_set_sig(void *vpdsactx,
+ const unsigned char *sig, size_t siglen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ OSSL_PARAM params[2];
+
+ params[0] =
+ OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE,
+ (unsigned char *)sig, siglen);
+ params[1] = OSSL_PARAM_construct_end();
+ return dsa_sigalg_set_ctx_params(pdsactx, params);
+}
+
+static int dsa_verify_message_final(void *vpdsactx)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (pdsactx == NULL || pdsactx->mdctx == NULL)
+ return 0;
+
+ /*
+ * The digests used here are all known (see dsa_get_md_nid()), so they
+ * should not exceed the internal buffer size of EVP_MAX_MD_SIZE.
+ */
+ if (!EVP_DigestFinal_ex(pdsactx->mdctx, digest, &dlen))
+ return 0;
+
+ return dsa_verify_directly(vpdsactx, pdsactx->sig, pdsactx->siglen,
+ digest, dlen);
+}
+
+/*
+ * If verifying a message, digest tbs and verify the result.
+ * Otherwise, verify tbs directly.
+ */
+static int dsa_verify(void *vpdsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->operation == EVP_PKEY_OP_VERIFYMSG) {
+ if (dsa_verify_set_sig(pdsactx, sig, siglen) <= 0)
+ return 0;
+ if (dsa_signverify_message_update(pdsactx, tbs, tbslen) <= 0)
+ return 0;
+ return dsa_verify_message_final(pdsactx);
+ }
+ return dsa_verify_directly(pdsactx, sig, siglen, tbs, tbslen);
+}
+
+/* DigestSign/DigestVerify wrappers */
+
+static int dsa_digest_signverify_init(void *vpdsactx, const char *mdname,
+ void *vdsa, const OSSL_PARAM params[],
+ int operation, const char *desc)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!dsa_signverify_init(vpdsactx, vdsa, dsa_set_ctx_params, params,
+ operation, desc))
+ return 0;
+
+ if (mdname != NULL
+ /* was dsa_setup_md already called in dsa_signverify_init()? */
+ && (mdname[0] == '\0' || OPENSSL_strcasecmp(pdsactx->mdname, mdname) != 0)
+ && !dsa_setup_md(pdsactx, mdname, NULL, desc))
+ return 0;
+
+ pdsactx->flag_allow_md = 0;
+
+ if (pdsactx->mdctx == NULL) {
+ pdsactx->mdctx = EVP_MD_CTX_new();
+ if (pdsactx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(pdsactx->mdctx, pdsactx->md, params))
+ goto error;
+
+ return 1;
+
+ error:
+ EVP_MD_CTX_free(pdsactx->mdctx);
+ pdsactx->mdctx = NULL;
+ return 0;
+}
+
+static int dsa_digest_sign_init(void *vpdsactx, const char *mdname,
+ void *vdsa, const OSSL_PARAM params[])
+{
+ return dsa_digest_signverify_init(vpdsactx, mdname, vdsa, params,
+ EVP_PKEY_OP_SIGNMSG,
+ "DSA Digest Sign Init");
+}
+
+static int dsa_digest_signverify_update(void *vpdsactx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (pdsactx->flag_sigalg)
+ return 0;
+
+ return dsa_signverify_message_update(vpdsactx, data, datalen);
+}
+
+static int dsa_digest_sign_final(void *vpdsactx, unsigned char *sig,
+ size_t *siglen, size_t sigsize)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ int ok = 0;
+
+ if (pdsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (pdsactx->flag_sigalg)
+ return 0;
+
+ ok = dsa_sign_message_final(pdsactx, sig, siglen, sigsize);
+
+ pdsactx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static int dsa_digest_verify_init(void *vpdsactx, const char *mdname,
+ void *vdsa, const OSSL_PARAM params[])
+{
+ return dsa_digest_signverify_init(vpdsactx, mdname, vdsa, params,
+ EVP_PKEY_OP_VERIFYMSG,
+ "DSA Digest Verify Init");
+}
+
+int dsa_digest_verify_final(void *vpdsactx, const unsigned char *sig,
+ size_t siglen)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ int ok = 0;
+
+ if (pdsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_verify */
+ if (pdsactx->flag_sigalg)
+ return 0;
+
+ if (dsa_verify_set_sig(pdsactx, sig, siglen))
+ ok = dsa_verify_message_final(vpdsactx);
+
+ pdsactx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static void dsa_freectx(void *vpdsactx)
+{
+ PROV_DSA_CTX *ctx = (PROV_DSA_CTX *)vpdsactx;
+
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+ OPENSSL_free(ctx->sig);
+ OPENSSL_free(ctx->propq);
+ DSA_free(ctx->dsa);
+ OPENSSL_free(ctx);
+}
+
+static void *dsa_dupctx(void *vpdsactx)
+{
+ PROV_DSA_CTX *srcctx = (PROV_DSA_CTX *)vpdsactx;
+ PROV_DSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->dsa = NULL;
+ dstctx->propq = NULL;
+
+ if (srcctx->dsa != NULL && !DSA_up_ref(srcctx->dsa))
+ goto err;
+ dstctx->dsa = srcctx->dsa;
+
+ if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md))
+ goto err;
+ dstctx->md = srcctx->md;
+
+ if (srcctx->mdctx != NULL) {
+ dstctx->mdctx = EVP_MD_CTX_new();
+ if (dstctx->mdctx == NULL
+ || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx))
+ goto err;
+ }
+
+ if (srcctx->propq != NULL) {
+ dstctx->propq = OPENSSL_strdup(srcctx->propq);
+ if (dstctx->propq == NULL)
+ goto err;
+ }
+
+ return dstctx;
+ err:
+ dsa_freectx(dstctx);
+ return NULL;
+}
+
+static int dsa_get_ctx_params(void *vpdsactx, OSSL_PARAM *params)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ OSSL_PARAM *p;
+
+ if (pdsactx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p,
+ pdsactx->aid_len == 0 ? NULL : pdsactx->aid_buf,
+ pdsactx->aid_len))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, pdsactx->mdname))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, pdsactx->nonce_type))
+ return 0;
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(pdsactx, params))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL),
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dsa_gettable_ctx_params(ossl_unused void *ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+/**
+ * @brief Setup common params for dsa_set_ctx_params and dsa_sigalg_set_ctx_params
+ * The caller is responsible for checking |vpdsactx| is not NULL and |params|
+ * is not empty.
+ */
+static int dsa_common_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[])
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ const OSSL_PARAM *p;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(pdsactx, OSSL_FIPS_IND_SETTABLE2, params,
+ OSSL_SIGNATURE_PARAM_FIPS_SIGN_CHECK))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE);
+ if (p != NULL
+ && !OSSL_PARAM_get_uint(p, &pdsactx->nonce_type))
+ return 0;
+ return 1;
+}
+
+#define DSA_COMMON_SETTABLE_CTX_PARAMS \
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), \
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) \
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) \
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_CHECK) \
+ OSSL_PARAM_END
+
+static int dsa_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[])
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ const OSSL_PARAM *p;
+ int ret;
+
+ if (pdsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((ret = dsa_common_set_ctx_params(pdsactx, params)) <= 0)
+ return ret;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL) {
+ char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = mdname;
+ char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = mdprops;
+ const OSSL_PARAM *propsp =
+ OSSL_PARAM_locate_const(params,
+ OSSL_SIGNATURE_PARAM_PROPERTIES);
+
+ if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname)))
+ return 0;
+ if (propsp != NULL
+ && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops)))
+ return 0;
+ if (!dsa_setup_md(pdsactx, mdname, mdprops, "DSA Set Ctx"))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0),
+ DSA_COMMON_SETTABLE_CTX_PARAMS
+};
+
+static const OSSL_PARAM settable_ctx_params_no_digest[] = {
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *dsa_settable_ctx_params(void *vpdsactx,
+ ossl_unused void *provctx)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx != NULL && !pdsactx->flag_allow_md)
+ return settable_ctx_params_no_digest;
+ return settable_ctx_params;
+}
+
+static int dsa_get_ctx_md_params(void *vpdsactx, OSSL_PARAM *params)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_get_params(pdsactx->mdctx, params);
+}
+
+static const OSSL_PARAM *dsa_gettable_ctx_md_params(void *vpdsactx)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->md == NULL)
+ return 0;
+
+ return EVP_MD_gettable_ctx_params(pdsactx->md);
+}
+
+static int dsa_set_ctx_md_params(void *vpdsactx, const OSSL_PARAM params[])
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_set_params(pdsactx->mdctx, params);
+}
+
+static const OSSL_PARAM *dsa_settable_ctx_md_params(void *vpdsactx)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx->md == NULL)
+ return 0;
+
+ return EVP_MD_settable_ctx_params(pdsactx->md);
+}
+
+const OSSL_DISPATCH ossl_dsa_signature_functions[] = {
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))dsa_newctx },
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))dsa_sign_init },
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))dsa_sign },
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))dsa_verify_init },
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))dsa_verify },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+ (void (*)(void))dsa_digest_sign_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+ (void (*)(void))dsa_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+ (void (*)(void))dsa_digest_sign_final },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+ (void (*)(void))dsa_digest_verify_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+ (void (*)(void))dsa_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+ (void (*)(void))dsa_digest_verify_final },
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))dsa_freectx },
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))dsa_dupctx },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))dsa_get_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+ (void (*)(void))dsa_gettable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))dsa_set_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+ (void (*)(void))dsa_settable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+ (void (*)(void))dsa_get_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))dsa_gettable_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+ (void (*)(void))dsa_set_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))dsa_settable_ctx_md_params },
+ OSSL_DISPATCH_END
+};
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * So called sigalgs (composite DSA+hash) implemented below. They
+ * are pretty much hard coded.
+ */
+
+static OSSL_FUNC_signature_query_key_types_fn dsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_settable_ctx_params_fn dsa_sigalg_settable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn dsa_sigalg_set_ctx_params;
+
+/*
+ * dsa_sigalg_signverify_init() is almost like dsa_digest_signverify_init(),
+ * just doesn't allow fetching an MD from whatever the user chooses.
+ */
+static int dsa_sigalg_signverify_init(void *vpdsactx, void *vdsa,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[],
+ const char *mdname,
+ int operation, const char *desc)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!dsa_signverify_init(vpdsactx, vdsa, set_ctx_params, params, operation,
+ desc))
+ return 0;
+
+ if (!dsa_setup_md(pdsactx, mdname, NULL, desc))
+ return 0;
+
+ pdsactx->flag_sigalg = 1;
+ pdsactx->flag_allow_md = 0;
+
+ if (pdsactx->mdctx == NULL) {
+ pdsactx->mdctx = EVP_MD_CTX_new();
+ if (pdsactx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(pdsactx->mdctx, pdsactx->md, params))
+ goto error;
+
+ return 1;
+
+ error:
+ EVP_MD_CTX_free(pdsactx->mdctx);
+ pdsactx->mdctx = NULL;
+ return 0;
+}
+
+static const char **dsa_sigalg_query_key_types(void)
+{
+ static const char *keytypes[] = { "DSA", NULL };
+
+ return keytypes;
+}
+
+static const OSSL_PARAM settable_sigalg_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0),
+ DSA_COMMON_SETTABLE_CTX_PARAMS
+};
+
+static const OSSL_PARAM *dsa_sigalg_settable_ctx_params(void *vpdsactx,
+ ossl_unused void *provctx)
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+
+ if (pdsactx != NULL && pdsactx->operation == EVP_PKEY_OP_VERIFYMSG)
+ return settable_sigalg_ctx_params;
+ return NULL;
+}
+
+static int dsa_sigalg_set_ctx_params(void *vpdsactx, const OSSL_PARAM params[])
+{
+ PROV_DSA_CTX *pdsactx = (PROV_DSA_CTX *)vpdsactx;
+ const OSSL_PARAM *p;
+ int ret;
+
+ if (pdsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((ret = dsa_common_set_ctx_params(pdsactx, params)) <= 0)
+ return ret;
+
+ if (pdsactx->operation == EVP_PKEY_OP_VERIFYMSG) {
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE);
+ if (p != NULL) {
+ OPENSSL_free(pdsactx->sig);
+ pdsactx->sig = NULL;
+ pdsactx->siglen = 0;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&pdsactx->sig,
+ 0, &pdsactx->siglen))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define IMPL_DSA_SIGALG(md, MD) \
+ static OSSL_FUNC_signature_sign_init_fn dsa_##md##_sign_init; \
+ static OSSL_FUNC_signature_sign_message_init_fn \
+ dsa_##md##_sign_message_init; \
+ static OSSL_FUNC_signature_verify_init_fn dsa_##md##_verify_init; \
+ static OSSL_FUNC_signature_verify_message_init_fn \
+ dsa_##md##_verify_message_init; \
+ \
+ static int \
+ dsa_##md##_sign_init(void *vpdsactx, void *vdsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "DSA-" #MD " Sign Init"; \
+ \
+ return dsa_sigalg_signverify_init(vpdsactx, vdsa, \
+ dsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGN, \
+ desc); \
+ } \
+ \
+ static int \
+ dsa_##md##_sign_message_init(void *vpdsactx, void *vdsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "DSA-" #MD " Sign Message Init"; \
+ \
+ return dsa_sigalg_signverify_init(vpdsactx, vdsa, \
+ dsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGNMSG, \
+ desc); \
+ } \
+ \
+ static int \
+ dsa_##md##_verify_init(void *vpdsactx, void *vdsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "DSA-" #MD " Verify Init"; \
+ \
+ return dsa_sigalg_signverify_init(vpdsactx, vdsa, \
+ dsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFY, \
+ desc); \
+ } \
+ \
+ static int \
+ dsa_##md##_verify_message_init(void *vpdsactx, void *vdsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "DSA-" #MD " Verify Message Init"; \
+ \
+ return dsa_sigalg_signverify_init(vpdsactx, vdsa, \
+ dsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFYMSG, \
+ desc); \
+ } \
+ \
+ const OSSL_DISPATCH ossl_dsa_##md##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))dsa_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))dsa_##md##_sign_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))dsa_sign }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))dsa_##md##_sign_message_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \
+ (void (*)(void))dsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \
+ (void (*)(void))dsa_sign_message_final }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))dsa_##md##_verify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, \
+ (void (*)(void))dsa_verify }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))dsa_##md##_verify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \
+ (void (*)(void))dsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \
+ (void (*)(void))dsa_verify_message_final }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))dsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))dsa_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \
+ (void (*)(void))dsa_sigalg_query_key_types }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))dsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))dsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))dsa_sigalg_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))dsa_sigalg_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+IMPL_DSA_SIGALG(sha1, SHA1);
+IMPL_DSA_SIGALG(sha224, SHA2-224);
+IMPL_DSA_SIGALG(sha256, SHA2-256);
+IMPL_DSA_SIGALG(sha384, SHA2-384);
+IMPL_DSA_SIGALG(sha512, SHA2-512);
+IMPL_DSA_SIGALG(sha3_224, SHA3-224);
+IMPL_DSA_SIGALG(sha3_256, SHA3-256);
+IMPL_DSA_SIGALG(sha3_384, SHA3-384);
+IMPL_DSA_SIGALG(sha3_512, SHA3-512);
diff --git a/crypto/openssl/providers/implementations/signature/ecdsa_sig.c b/crypto/openssl/providers/implementations/signature/ecdsa_sig.c
new file mode 100644
index 000000000000..73bfbf4aa9c1
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/ecdsa_sig.c
@@ -0,0 +1,1108 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * ECDSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h> /* memcpy */
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/dsa.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "internal/cryptlib.h"
+#include "internal/deterministic_nonce.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/securitycheck.h"
+#include "prov/der_ec.h"
+#include "crypto/ec.h"
+
+static OSSL_FUNC_signature_newctx_fn ecdsa_newctx;
+static OSSL_FUNC_signature_sign_init_fn ecdsa_sign_init;
+static OSSL_FUNC_signature_verify_init_fn ecdsa_verify_init;
+static OSSL_FUNC_signature_sign_fn ecdsa_sign;
+static OSSL_FUNC_signature_sign_message_update_fn ecdsa_signverify_message_update;
+static OSSL_FUNC_signature_sign_message_final_fn ecdsa_sign_message_final;
+static OSSL_FUNC_signature_verify_fn ecdsa_verify;
+static OSSL_FUNC_signature_verify_message_update_fn ecdsa_signverify_message_update;
+static OSSL_FUNC_signature_verify_message_final_fn ecdsa_verify_message_final;
+static OSSL_FUNC_signature_digest_sign_init_fn ecdsa_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn ecdsa_digest_signverify_update;
+static OSSL_FUNC_signature_digest_sign_final_fn ecdsa_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn ecdsa_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_update_fn ecdsa_digest_signverify_update;
+static OSSL_FUNC_signature_digest_verify_final_fn ecdsa_digest_verify_final;
+static OSSL_FUNC_signature_freectx_fn ecdsa_freectx;
+static OSSL_FUNC_signature_dupctx_fn ecdsa_dupctx;
+static OSSL_FUNC_signature_query_key_types_fn ecdsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_get_ctx_params_fn ecdsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn ecdsa_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn ecdsa_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn ecdsa_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn ecdsa_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn ecdsa_settable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_sigalg_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_sigalg_settable_ctx_params;
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes DSA structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ EC_KEY *ec;
+ /* |operation| reuses EVP's operation bitfield */
+ int operation;
+
+ /*
+ * Flag to determine if a full sigalg is run (1) or if a composable
+ * signature algorithm is run (0).
+ *
+ * When a full sigalg is run (1), this currently affects the following
+ * other flags, which are to remain untouched after their initialization:
+ *
+ * - flag_allow_md (initialized to 0)
+ */
+ unsigned int flag_sigalg : 1;
+ /*
+ * Flag to determine if the hash function can be changed (1) or not (0)
+ * Because it's dangerous to change during a DigestSign or DigestVerify
+ * operation, this flag is cleared by their Init function, and set again
+ * by their Final function.
+ */
+ unsigned int flag_allow_md : 1;
+
+ /* The Algorithm Identifier of the combined signature algorithm */
+ unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+
+ /* main digest */
+ char mdname[OSSL_MAX_NAME_SIZE];
+ EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+ size_t mdsize;
+
+ /* Signature, for verification */
+ unsigned char *sig;
+ size_t siglen;
+
+ /*
+ * Internally used to cache the results of calling the EC group
+ * sign_setup() methods which are then passed to the sign operation.
+ * This is used by CAVS failure tests to terminate a loop if the signature
+ * is not valid.
+ * This could of also been done with a simple flag.
+ */
+ BIGNUM *kinv;
+ BIGNUM *r;
+#if !defined(OPENSSL_NO_ACVP_TESTS)
+ /*
+ * This indicates that KAT (CAVS) test is running. Externally an app will
+ * override the random callback such that the generated private key and k
+ * are known.
+ * Normal operation will loop to choose a new k if the signature is not
+ * valid - but for this mode of operation it forces a failure instead.
+ */
+ unsigned int kattest;
+#endif
+#ifdef FIPS_MODULE
+ /*
+ * FIPS 140-3 IG 2.4.B mandates that verification based on a digest of a
+ * message is not permitted. However, signing based on a digest is still
+ * permitted.
+ */
+ int verify_message;
+#endif
+ /* If this is set then the generated k is not random */
+ unsigned int nonce_type;
+ OSSL_FIPS_IND_DECLARE
+} PROV_ECDSA_CTX;
+
+static void *ecdsa_newctx(void *provctx, const char *propq)
+{
+ PROV_ECDSA_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(PROV_ECDSA_CTX));
+ if (ctx == NULL)
+ return NULL;
+
+ OSSL_FIPS_IND_INIT(ctx)
+ ctx->flag_allow_md = 1;
+#ifdef FIPS_MODULE
+ ctx->verify_message = 1;
+#endif
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) {
+ OPENSSL_free(ctx);
+ ctx = NULL;
+ }
+ return ctx;
+}
+
+static int ecdsa_setup_md(PROV_ECDSA_CTX *ctx,
+ const char *mdname, const char *mdprops,
+ const char *desc)
+{
+ EVP_MD *md = NULL;
+ size_t mdname_len;
+ int md_nid, md_size;
+ WPACKET pkt;
+ unsigned char *aid = NULL;
+
+ if (mdname == NULL)
+ return 1;
+
+ mdname_len = strlen(mdname);
+ if (mdname_len >= sizeof(ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s exceeds name buffer length", mdname);
+ return 0;
+ }
+ if (mdprops == NULL)
+ mdprops = ctx->propq;
+ md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
+ if (md == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s could not be fetched", mdname);
+ return 0;
+ }
+ md_size = EVP_MD_get_size(md);
+ if (md_size <= 0) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s has invalid md size %d", mdname, md_size);
+ goto err;
+ }
+ md_nid = ossl_digest_get_approved_nid(md);
+#ifdef FIPS_MODULE
+ if (md_nid == NID_undef) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest=%s", mdname);
+ goto err;
+ }
+#endif
+ /* XOF digests don't work */
+ if (EVP_MD_xof(md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ goto err;
+ }
+
+#ifdef FIPS_MODULE
+ {
+ int sha1_allowed
+ = ((ctx->operation
+ & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0);
+
+ if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE1,
+ ctx->libctx,
+ md_nid, sha1_allowed, 0, desc,
+ ossl_fips_config_signature_digest_check))
+ goto err;
+ }
+#endif
+
+ if (!ctx->flag_allow_md) {
+ if (ctx->mdname[0] != '\0' && !EVP_MD_is_a(md, ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest %s != %s", mdname, ctx->mdname);
+ goto err;
+ }
+ EVP_MD_free(md);
+ return 1;
+ }
+
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+
+ ctx->aid_len = 0;
+#ifndef FIPS_MODULE
+ if (md_nid != NID_undef) {
+#else
+ {
+#endif
+ if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf))
+ && ossl_DER_w_algorithmIdentifier_ECDSA_with_MD(&pkt, -1, ctx->ec,
+ md_nid)
+ && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &ctx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && ctx->aid_len != 0)
+ memmove(ctx->aid_buf, aid, ctx->aid_len);
+ }
+
+ ctx->mdctx = NULL;
+ ctx->md = md;
+ ctx->mdsize = (size_t)md_size;
+ OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname));
+
+ return 1;
+ err:
+ EVP_MD_free(md);
+ return 0;
+}
+
+static int
+ecdsa_signverify_init(PROV_ECDSA_CTX *ctx, void *ec,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ if (!ossl_prov_is_running()
+ || ctx == NULL)
+ return 0;
+
+ if (ec == NULL && ctx->ec == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (ec != NULL) {
+ if (!EC_KEY_up_ref(ec))
+ return 0;
+ EC_KEY_free(ctx->ec);
+ ctx->ec = ec;
+ }
+
+ ctx->operation = operation;
+
+ OSSL_FIPS_IND_SET_APPROVED(ctx)
+ if (!set_ctx_params(ctx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_ec_key_check(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE0, ctx->libctx,
+ EC_KEY_get0_group(ctx->ec), desc,
+ (operation & (EVP_PKEY_OP_SIGN
+ | EVP_PKEY_OP_SIGNMSG)) != 0))
+ return 0;
+#endif
+ return 1;
+}
+
+static int ecdsa_sign_init(void *vctx, void *ec, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+#ifdef FIPS_MODULE
+ ctx->verify_message = 1;
+#endif
+ return ecdsa_signverify_init(ctx, ec, ecdsa_set_ctx_params, params,
+ EVP_PKEY_OP_SIGN, "ECDSA Sign Init");
+}
+
+/*
+ * Sign tbs without digesting it first. This is suitable for "primitive"
+ * signing and signing the digest of a message.
+ */
+static int ecdsa_sign_directly(void *vctx,
+ unsigned char *sig, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ int ret;
+ unsigned int sltmp;
+ size_t ecsize = ECDSA_size(ctx->ec);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sig == NULL) {
+ *siglen = ecsize;
+ return 1;
+ }
+
+#if !defined(OPENSSL_NO_ACVP_TESTS)
+ if (ctx->kattest && !ECDSA_sign_setup(ctx->ec, NULL, &ctx->kinv, &ctx->r))
+ return 0;
+#endif
+
+ if (sigsize < (size_t)ecsize)
+ return 0;
+
+ if (ctx->mdsize != 0 && tbslen != ctx->mdsize)
+ return 0;
+
+ if (ctx->nonce_type != 0) {
+ const char *mdname = NULL;
+
+ if (ctx->mdname[0] != '\0')
+ mdname = ctx->mdname;
+ ret = ossl_ecdsa_deterministic_sign(tbs, tbslen, sig, &sltmp,
+ ctx->ec, ctx->nonce_type,
+ mdname,
+ ctx->libctx, ctx->propq);
+ } else {
+ ret = ECDSA_sign_ex(0, tbs, tbslen, sig, &sltmp, ctx->kinv, ctx->r,
+ ctx->ec);
+ }
+ if (ret <= 0)
+ return 0;
+
+ *siglen = sltmp;
+ return 1;
+}
+
+static int ecdsa_signverify_message_update(void *vctx,
+ const unsigned char *data,
+ size_t datalen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx == NULL)
+ return 0;
+
+ return EVP_DigestUpdate(ctx->mdctx, data, datalen);
+}
+
+static int ecdsa_sign_message_final(void *vctx, unsigned char *sig,
+ size_t *siglen, size_t sigsize)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running() || ctx == NULL)
+ return 0;
+ if (ctx->mdctx == NULL)
+ return 0;
+ /*
+ * If sig is NULL then we're just finding out the sig size. Other fields
+ * are ignored. Defer to ecdsa_sign.
+ */
+ if (sig != NULL
+ && !EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen))
+ return 0;
+ return ecdsa_sign_directly(vctx, sig, siglen, sigsize, digest, dlen);
+}
+
+/*
+ * If signing a message, digest tbs and sign the result.
+ * Otherwise, sign tbs directly.
+ */
+static int ecdsa_sign(void *vctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *tbs, size_t tbslen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->operation == EVP_PKEY_OP_SIGNMSG) {
+ /*
+ * If |sig| is NULL, the caller is only looking for the sig length.
+ * DO NOT update the input in this case.
+ */
+ if (sig == NULL)
+ return ecdsa_sign_message_final(ctx, sig, siglen, sigsize);
+
+ if (ecdsa_signverify_message_update(ctx, tbs, tbslen) <= 0)
+ return 0;
+ return ecdsa_sign_message_final(ctx, sig, siglen, sigsize);
+ }
+ return ecdsa_sign_directly(ctx, sig, siglen, sigsize, tbs, tbslen);
+}
+
+static int ecdsa_verify_init(void *vctx, void *ec, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+#ifdef FIPS_MODULE
+ ctx->verify_message = 0;
+#endif
+ return ecdsa_signverify_init(ctx, ec, ecdsa_set_ctx_params, params,
+ EVP_PKEY_OP_VERIFY, "ECDSA Verify Init");
+}
+
+static int ecdsa_verify_directly(void *vctx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running() || (ctx->mdsize != 0 && tbslen != ctx->mdsize))
+ return 0;
+
+ return ECDSA_verify(0, tbs, tbslen, sig, siglen, ctx->ec);
+}
+
+static int ecdsa_verify_set_sig(void *vctx,
+ const unsigned char *sig, size_t siglen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ OSSL_PARAM params[2];
+
+ params[0] =
+ OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE,
+ (unsigned char *)sig, siglen);
+ params[1] = OSSL_PARAM_construct_end();
+ return ecdsa_sigalg_set_ctx_params(ctx, params);
+}
+
+static int ecdsa_verify_message_final(void *vctx)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running() || ctx == NULL || ctx->mdctx == NULL)
+ return 0;
+
+ /*
+ * The digests used here are all known (see ecdsa_get_md_nid()), so they
+ * should not exceed the internal buffer size of EVP_MAX_MD_SIZE.
+ */
+ if (!EVP_DigestFinal_ex(ctx->mdctx, digest, &dlen))
+ return 0;
+
+ return ecdsa_verify_directly(vctx, ctx->sig, ctx->siglen,
+ digest, dlen);
+}
+
+/*
+ * If verifying a message, digest tbs and verify the result.
+ * Otherwise, verify tbs directly.
+ */
+static int ecdsa_verify(void *vctx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) {
+ if (ecdsa_verify_set_sig(ctx, sig, siglen) <= 0)
+ return 0;
+ if (ecdsa_signverify_message_update(ctx, tbs, tbslen) <= 0)
+ return 0;
+ return ecdsa_verify_message_final(ctx);
+ }
+ return ecdsa_verify_directly(ctx, sig, siglen, tbs, tbslen);
+}
+
+/* DigestSign/DigestVerify wrappers */
+
+static int ecdsa_digest_signverify_init(void *vctx, const char *mdname,
+ void *ec, const OSSL_PARAM params[],
+ int operation, const char *desc)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+#ifdef FIPS_MODULE
+ ctx->verify_message = 1;
+#endif
+ if (!ecdsa_signverify_init(vctx, ec, ecdsa_set_ctx_params, params,
+ operation, desc))
+ return 0;
+
+ if (mdname != NULL
+ /* was ecdsa_setup_md already called in ecdsa_signverify_init()? */
+ && (mdname[0] == '\0' || OPENSSL_strcasecmp(ctx->mdname, mdname) != 0)
+ && !ecdsa_setup_md(ctx, mdname, NULL, desc))
+ return 0;
+
+ ctx->flag_allow_md = 0;
+
+ if (ctx->mdctx == NULL) {
+ ctx->mdctx = EVP_MD_CTX_new();
+ if (ctx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params))
+ goto error;
+ return 1;
+error:
+ EVP_MD_CTX_free(ctx->mdctx);
+ ctx->mdctx = NULL;
+ return 0;
+}
+
+static int ecdsa_digest_sign_init(void *vctx, const char *mdname, void *ec,
+ const OSSL_PARAM params[])
+{
+ return ecdsa_digest_signverify_init(vctx, mdname, ec, params,
+ EVP_PKEY_OP_SIGNMSG,
+ "ECDSA Digest Sign Init");
+}
+
+static int ecdsa_digest_signverify_update(void *vctx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx == NULL || ctx->mdctx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (ctx->flag_sigalg)
+ return 0;
+
+ return ecdsa_signverify_message_update(vctx, data, datalen);
+}
+
+int ecdsa_digest_sign_final(void *vctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ int ok = 0;
+
+ if (ctx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (ctx->flag_sigalg)
+ return 0;
+
+ ok = ecdsa_sign_message_final(ctx, sig, siglen, sigsize);
+
+ ctx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static int ecdsa_digest_verify_init(void *vctx, const char *mdname, void *ec,
+ const OSSL_PARAM params[])
+{
+ return ecdsa_digest_signverify_init(vctx, mdname, ec, params,
+ EVP_PKEY_OP_VERIFYMSG,
+ "ECDSA Digest Verify Init");
+}
+
+int ecdsa_digest_verify_final(void *vctx, const unsigned char *sig,
+ size_t siglen)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ int ok = 0;
+
+ if (!ossl_prov_is_running() || ctx == NULL || ctx->mdctx == NULL)
+ return 0;
+
+ /* Sigalg implementations shouldn't do digest_verify */
+ if (ctx->flag_sigalg)
+ return 0;
+
+ if (ecdsa_verify_set_sig(ctx, sig, siglen))
+ ok = ecdsa_verify_message_final(ctx);
+
+ ctx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static void ecdsa_freectx(void *vctx)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+ OPENSSL_free(ctx->propq);
+ OPENSSL_free(ctx->sig);
+ EC_KEY_free(ctx->ec);
+ BN_clear_free(ctx->kinv);
+ BN_clear_free(ctx->r);
+ OPENSSL_free(ctx);
+}
+
+static void *ecdsa_dupctx(void *vctx)
+{
+ PROV_ECDSA_CTX *srcctx = (PROV_ECDSA_CTX *)vctx;
+ PROV_ECDSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->ec = NULL;
+ dstctx->propq = NULL;
+
+ if (srcctx->ec != NULL && !EC_KEY_up_ref(srcctx->ec))
+ goto err;
+ /* Test KATS should not need to be supported */
+ if (srcctx->kinv != NULL || srcctx->r != NULL)
+ goto err;
+ dstctx->ec = srcctx->ec;
+
+ if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md))
+ goto err;
+ dstctx->md = srcctx->md;
+
+ if (srcctx->mdctx != NULL) {
+ dstctx->mdctx = EVP_MD_CTX_new();
+ if (dstctx->mdctx == NULL
+ || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx))
+ goto err;
+ }
+
+ if (srcctx->propq != NULL) {
+ dstctx->propq = OPENSSL_strdup(srcctx->propq);
+ if (dstctx->propq == NULL)
+ goto err;
+ }
+
+ return dstctx;
+ err:
+ ecdsa_freectx(dstctx);
+ return NULL;
+}
+
+static int ecdsa_get_ctx_params(void *vctx, OSSL_PARAM *params)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (ctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL && !OSSL_PARAM_set_octet_string(p,
+ ctx->aid_len == 0 ? NULL : ctx->aid_buf,
+ ctx->aid_len))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, ctx->mdsize))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, ctx->md == NULL
+ ? ctx->mdname
+ : EVP_MD_get0_name(ctx->md)))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->nonce_type))
+ return 0;
+
+#ifdef FIPS_MODULE
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, ctx->verify_message))
+ return 0;
+#endif
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(ctx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL),
+#ifdef FIPS_MODULE
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE, NULL),
+#endif
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ecdsa_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+/**
+ * @brief Set up common params for ecdsa_set_ctx_params and
+ * ecdsa_sigalg_set_ctx_params. The caller is responsible for checking |vctx| is
+ * not NULL and |params| is not empty.
+ */
+static int ecdsa_common_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK))
+ return 0;
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(ctx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+
+#if !defined(OPENSSL_NO_ACVP_TESTS)
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_KAT);
+ if (p != NULL && !OSSL_PARAM_get_uint(p, &ctx->kattest))
+ return 0;
+#endif
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_NONCE_TYPE);
+ if (p != NULL
+ && !OSSL_PARAM_get_uint(p, &ctx->nonce_type))
+ return 0;
+ return 1;
+}
+
+#define ECDSA_COMMON_SETTABLE_CTX_PARAMS \
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_KAT, NULL), \
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_NONCE_TYPE, NULL), \
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK) \
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK) \
+ OSSL_PARAM_END
+
+static int ecdsa_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ const OSSL_PARAM *p;
+ size_t mdsize = 0;
+ int ret;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((ret = ecdsa_common_set_ctx_params(ctx, params)) <= 0)
+ return ret;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL) {
+ char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = mdname;
+ char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = mdprops;
+ const OSSL_PARAM *propsp =
+ OSSL_PARAM_locate_const(params,
+ OSSL_SIGNATURE_PARAM_PROPERTIES);
+
+ if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname)))
+ return 0;
+ if (propsp != NULL
+ && !OSSL_PARAM_get_utf8_string(propsp, &pmdprops, sizeof(mdprops)))
+ return 0;
+ if (!ecdsa_setup_md(ctx, mdname, mdprops, "ECDSA Set Ctx"))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE);
+ if (p != NULL) {
+ if (!OSSL_PARAM_get_size_t(p, &mdsize)
+ || (!ctx->flag_allow_md && mdsize != ctx->mdsize))
+ return 0;
+ ctx->mdsize = mdsize;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0),
+ ECDSA_COMMON_SETTABLE_CTX_PARAMS
+};
+
+static const OSSL_PARAM *ecdsa_settable_ctx_params(void *vctx,
+ ossl_unused void *provctx)
+{
+ return settable_ctx_params;
+}
+
+static int ecdsa_get_ctx_md_params(void *vctx, OSSL_PARAM *params)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_get_params(ctx->mdctx, params);
+}
+
+static const OSSL_PARAM *ecdsa_gettable_ctx_md_params(void *vctx)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->md == NULL)
+ return 0;
+
+ return EVP_MD_gettable_ctx_params(ctx->md);
+}
+
+static int ecdsa_set_ctx_md_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_set_params(ctx->mdctx, params);
+}
+
+static const OSSL_PARAM *ecdsa_settable_ctx_md_params(void *vctx)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx->md == NULL)
+ return 0;
+
+ return EVP_MD_settable_ctx_params(ctx->md);
+}
+
+const OSSL_DISPATCH ossl_ecdsa_signature_functions[] = {
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ecdsa_newctx },
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))ecdsa_sign_init },
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ecdsa_sign },
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))ecdsa_verify_init },
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ecdsa_verify },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+ (void (*)(void))ecdsa_digest_sign_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+ (void (*)(void))ecdsa_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+ (void (*)(void))ecdsa_digest_sign_final },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+ (void (*)(void))ecdsa_digest_verify_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+ (void (*)(void))ecdsa_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+ (void (*)(void))ecdsa_digest_verify_final },
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ecdsa_freectx },
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ecdsa_dupctx },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))ecdsa_get_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+ (void (*)(void))ecdsa_gettable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))ecdsa_set_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+ (void (*)(void))ecdsa_settable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+ (void (*)(void))ecdsa_get_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))ecdsa_gettable_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+ (void (*)(void))ecdsa_set_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))ecdsa_settable_ctx_md_params },
+ OSSL_DISPATCH_END
+};
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * So called sigalgs (composite ECDSA+hash) implemented below. They
+ * are pretty much hard coded.
+ */
+
+static OSSL_FUNC_signature_query_key_types_fn ecdsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_settable_ctx_params_fn ecdsa_sigalg_settable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn ecdsa_sigalg_set_ctx_params;
+
+/*
+ * ecdsa_sigalg_signverify_init() is almost like ecdsa_digest_signverify_init(),
+ * just doesn't allow fetching an MD from whatever the user chooses.
+ */
+static int ecdsa_sigalg_signverify_init(void *vctx, void *vec,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[],
+ const char *mdname,
+ int operation, const char *desc)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!ecdsa_signverify_init(vctx, vec, set_ctx_params, params, operation,
+ desc))
+ return 0;
+
+ if (!ecdsa_setup_md(ctx, mdname, NULL, desc))
+ return 0;
+
+ ctx->flag_sigalg = 1;
+ ctx->flag_allow_md = 0;
+
+ if (ctx->mdctx == NULL) {
+ ctx->mdctx = EVP_MD_CTX_new();
+ if (ctx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params))
+ goto error;
+
+ return 1;
+
+ error:
+ EVP_MD_CTX_free(ctx->mdctx);
+ ctx->mdctx = NULL;
+ return 0;
+}
+
+static const char **ecdsa_sigalg_query_key_types(void)
+{
+ static const char *keytypes[] = { "EC", NULL };
+
+ return keytypes;
+}
+
+static const OSSL_PARAM settable_sigalg_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0),
+ ECDSA_COMMON_SETTABLE_CTX_PARAMS
+};
+
+static const OSSL_PARAM *ecdsa_sigalg_settable_ctx_params(void *vctx,
+ ossl_unused void *provctx)
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+
+ if (ctx != NULL && ctx->operation == EVP_PKEY_OP_VERIFYMSG)
+ return settable_sigalg_ctx_params;
+ return NULL;
+}
+
+static int ecdsa_sigalg_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ECDSA_CTX *ctx = (PROV_ECDSA_CTX *)vctx;
+ const OSSL_PARAM *p;
+ int ret;
+
+ if (ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if ((ret = ecdsa_common_set_ctx_params(ctx, params)) <= 0)
+ return ret;
+
+ if (ctx->operation == EVP_PKEY_OP_VERIFYMSG) {
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE);
+ if (p != NULL) {
+ OPENSSL_free(ctx->sig);
+ ctx->sig = NULL;
+ ctx->siglen = 0;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&ctx->sig,
+ 0, &ctx->siglen))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define IMPL_ECDSA_SIGALG(md, MD) \
+ static OSSL_FUNC_signature_sign_init_fn ecdsa_##md##_sign_init; \
+ static OSSL_FUNC_signature_sign_message_init_fn \
+ ecdsa_##md##_sign_message_init; \
+ static OSSL_FUNC_signature_verify_init_fn ecdsa_##md##_verify_init; \
+ static OSSL_FUNC_signature_verify_message_init_fn \
+ ecdsa_##md##_verify_message_init; \
+ \
+ static int \
+ ecdsa_##md##_sign_init(void *vctx, void *vec, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "ECDSA-" #MD " Sign Init"; \
+ \
+ return ecdsa_sigalg_signverify_init(vctx, vec, \
+ ecdsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGN, \
+ desc); \
+ } \
+ \
+ static int \
+ ecdsa_##md##_sign_message_init(void *vctx, void *vec, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "ECDSA-" #MD " Sign Message Init"; \
+ \
+ return ecdsa_sigalg_signverify_init(vctx, vec, \
+ ecdsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGNMSG, \
+ desc); \
+ } \
+ \
+ static int \
+ ecdsa_##md##_verify_init(void *vctx, void *vec, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "ECDSA-" #MD " Verify Init"; \
+ \
+ return ecdsa_sigalg_signverify_init(vctx, vec, \
+ ecdsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFY, \
+ desc); \
+ } \
+ \
+ static int \
+ ecdsa_##md##_verify_message_init(void *vctx, void *vec, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "ECDSA-" #MD " Verify Message Init"; \
+ \
+ return ecdsa_sigalg_signverify_init(vctx, vec, \
+ ecdsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFYMSG, \
+ desc); \
+ } \
+ \
+ const OSSL_DISPATCH ossl_ecdsa_##md##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ecdsa_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))ecdsa_##md##_sign_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ecdsa_sign }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))ecdsa_##md##_sign_message_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \
+ (void (*)(void))ecdsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \
+ (void (*)(void))ecdsa_sign_message_final }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))ecdsa_##md##_verify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, \
+ (void (*)(void))ecdsa_verify }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))ecdsa_##md##_verify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \
+ (void (*)(void))ecdsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \
+ (void (*)(void))ecdsa_verify_message_final }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ecdsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ecdsa_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \
+ (void (*)(void))ecdsa_sigalg_query_key_types }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))ecdsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ecdsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))ecdsa_sigalg_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ecdsa_sigalg_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+IMPL_ECDSA_SIGALG(sha1, SHA1);
+IMPL_ECDSA_SIGALG(sha224, SHA2-224);
+IMPL_ECDSA_SIGALG(sha256, SHA2-256);
+IMPL_ECDSA_SIGALG(sha384, SHA2-384);
+IMPL_ECDSA_SIGALG(sha512, SHA2-512);
+IMPL_ECDSA_SIGALG(sha3_224, SHA3-224);
+IMPL_ECDSA_SIGALG(sha3_256, SHA3-256);
+IMPL_ECDSA_SIGALG(sha3_384, SHA3-384);
+IMPL_ECDSA_SIGALG(sha3_512, SHA3-512);
diff --git a/crypto/openssl/providers/implementations/signature/eddsa_sig.c b/crypto/openssl/providers/implementations/signature/eddsa_sig.c
new file mode 100644
index 000000000000..28b17eab93f3
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/eddsa_sig.c
@@ -0,0 +1,1146 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/securitycheck.h"
+#include "prov/provider_ctx.h"
+#include "prov/der_ecx.h"
+#include "crypto/ecx.h"
+
+#ifdef S390X_EC_ASM
+# include "s390x_arch.h"
+
+# define S390X_CAN_SIGN(edtype) \
+((OPENSSL_s390xcap_P.pcc[1] & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_##edtype)) \
+&& (OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_SIGN_##edtype)) \
+&& (OPENSSL_s390xcap_P.kdsa[0] & S390X_CAPBIT(S390X_EDDSA_VERIFY_##edtype)))
+
+static int s390x_ed25519_digestsign(const ECX_KEY *edkey, unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen);
+static int s390x_ed448_digestsign(const ECX_KEY *edkey, unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen);
+static int s390x_ed25519_digestverify(const ECX_KEY *edkey,
+ const unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen);
+static int s390x_ed448_digestverify(const ECX_KEY *edkey,
+ const unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen);
+
+#endif /* S390X_EC_ASM */
+
+enum ID_EdDSA_INSTANCE {
+ ID_NOT_SET = 0,
+ ID_Ed25519,
+ ID_Ed25519ctx,
+ ID_Ed25519ph,
+ ID_Ed448,
+ ID_Ed448ph
+};
+
+#define SN_Ed25519 "Ed25519"
+#define SN_Ed25519ph "Ed25519ph"
+#define SN_Ed25519ctx "Ed25519ctx"
+#define SN_Ed448 "Ed448"
+#define SN_Ed448ph "Ed448ph"
+
+#define EDDSA_MAX_CONTEXT_STRING_LEN 255
+#define EDDSA_PREHASH_OUTPUT_LEN 64
+
+static OSSL_FUNC_signature_newctx_fn eddsa_newctx;
+static OSSL_FUNC_signature_sign_message_init_fn ed25519_signverify_message_init;
+static OSSL_FUNC_signature_sign_message_init_fn ed25519ph_signverify_message_init;
+static OSSL_FUNC_signature_sign_message_init_fn ed25519ctx_signverify_message_init;
+static OSSL_FUNC_signature_sign_message_init_fn ed448_signverify_message_init;
+static OSSL_FUNC_signature_sign_message_init_fn ed448ph_signverify_message_init;
+static OSSL_FUNC_signature_sign_fn ed25519_sign;
+static OSSL_FUNC_signature_sign_fn ed448_sign;
+static OSSL_FUNC_signature_verify_fn ed25519_verify;
+static OSSL_FUNC_signature_verify_fn ed448_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn ed25519_digest_signverify_init;
+static OSSL_FUNC_signature_digest_sign_init_fn ed448_digest_signverify_init;
+static OSSL_FUNC_signature_digest_sign_fn ed25519_digest_sign;
+static OSSL_FUNC_signature_digest_sign_fn ed448_digest_sign;
+static OSSL_FUNC_signature_digest_verify_fn ed25519_digest_verify;
+static OSSL_FUNC_signature_digest_verify_fn ed448_digest_verify;
+static OSSL_FUNC_signature_freectx_fn eddsa_freectx;
+static OSSL_FUNC_signature_dupctx_fn eddsa_dupctx;
+static OSSL_FUNC_signature_query_key_types_fn ed25519_sigalg_query_key_types;
+static OSSL_FUNC_signature_query_key_types_fn ed448_sigalg_query_key_types;
+static OSSL_FUNC_signature_get_ctx_params_fn eddsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn eddsa_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn eddsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn eddsa_settable_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn eddsa_settable_variant_ctx_params;
+
+/* there are five EdDSA instances:
+
+ Ed25519
+ Ed25519ph
+ Ed25519ctx
+ Ed448
+ Ed448ph
+
+ Quoting from RFC 8032, Section 5.1:
+
+ For Ed25519, dom2(f,c) is the empty string. The phflag value is
+ irrelevant. The context (if present at all) MUST be empty. This
+ causes the scheme to be one and the same with the Ed25519 scheme
+ published earlier.
+
+ For Ed25519ctx, phflag=0. The context input SHOULD NOT be empty.
+
+ For Ed25519ph, phflag=1 and PH is SHA512 instead. That is, the input
+ is hashed using SHA-512 before signing with Ed25519.
+
+ Quoting from RFC 8032, Section 5.2:
+
+ Ed448ph is the same but with PH being SHAKE256(x, 64) and phflag
+ being 1, i.e., the input is hashed before signing with Ed448 with a
+ hash constant modified.
+
+ Value of context is set by signer and verifier (maximum of 255
+ octets; the default is empty string) and has to match octet by octet
+ for verification to be successful.
+
+ Quoting from RFC 8032, Section 2:
+
+ dom2(x, y) The blank octet string when signing or verifying
+ Ed25519. Otherwise, the octet string: "SigEd25519 no
+ Ed25519 collisions" || octet(x) || octet(OLEN(y)) ||
+ y, where x is in range 0-255 and y is an octet string
+ of at most 255 octets. "SigEd25519 no Ed25519
+ collisions" is in ASCII (32 octets).
+
+ dom4(x, y) The octet string "SigEd448" || octet(x) ||
+ octet(OLEN(y)) || y, where x is in range 0-255 and y
+ is an octet string of at most 255 octets. "SigEd448"
+ is in ASCII (8 octets).
+
+ Note above that x is the pre-hash flag, and y is the context string.
+*/
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ ECX_KEY *key;
+
+ /* The Algorithm Identifier of the signature algorithm */
+ unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+
+ /* id indicating the EdDSA instance */
+ int instance_id;
+ /* indicates that instance_id and associated flags are preset / hardcoded */
+ unsigned int instance_id_preset_flag : 1;
+ /* for ph instances, this indicates whether the caller is expected to prehash */
+ unsigned int prehash_by_caller_flag : 1;
+
+ unsigned int dom2_flag : 1;
+ unsigned int prehash_flag : 1;
+
+ /* indicates that a non-empty context string is required, as in Ed25519ctx */
+ unsigned int context_string_flag : 1;
+
+ unsigned char context_string[EDDSA_MAX_CONTEXT_STRING_LEN];
+ size_t context_string_len;
+
+} PROV_EDDSA_CTX;
+
+static void *eddsa_newctx(void *provctx, const char *propq_unused)
+{
+ PROV_EDDSA_CTX *peddsactx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ peddsactx = OPENSSL_zalloc(sizeof(PROV_EDDSA_CTX));
+ if (peddsactx == NULL)
+ return NULL;
+
+ peddsactx->libctx = PROV_LIBCTX_OF(provctx);
+
+ return peddsactx;
+}
+
+static int eddsa_setup_instance(void *vpeddsactx, int instance_id,
+ unsigned int instance_id_preset,
+ unsigned int prehash_by_caller)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+
+ switch (instance_id) {
+ case ID_Ed25519:
+ if (peddsactx->key->type != ECX_KEY_TYPE_ED25519)
+ return 0;
+ peddsactx->dom2_flag = 0;
+ peddsactx->prehash_flag = 0;
+ peddsactx->context_string_flag = 0;
+ break;
+ case ID_Ed25519ctx:
+ if (peddsactx->key->type != ECX_KEY_TYPE_ED25519)
+ return 0;
+ peddsactx->dom2_flag = 1;
+ peddsactx->prehash_flag = 0;
+ peddsactx->context_string_flag = 1;
+ break;
+ case ID_Ed25519ph:
+ if (peddsactx->key->type != ECX_KEY_TYPE_ED25519)
+ return 0;
+ peddsactx->dom2_flag = 1;
+ peddsactx->prehash_flag = 1;
+ peddsactx->context_string_flag = 0;
+ break;
+ case ID_Ed448:
+ if (peddsactx->key->type != ECX_KEY_TYPE_ED448)
+ return 0;
+ peddsactx->prehash_flag = 0;
+ peddsactx->context_string_flag = 0;
+ break;
+ case ID_Ed448ph:
+ if (peddsactx->key->type != ECX_KEY_TYPE_ED448)
+ return 0;
+ peddsactx->prehash_flag = 1;
+ peddsactx->context_string_flag = 0;
+ break;
+ default:
+ /* we did not recognize the instance */
+ return 0;
+ }
+ peddsactx->instance_id = instance_id;
+ peddsactx->instance_id_preset_flag = instance_id_preset;
+ peddsactx->prehash_by_caller_flag = prehash_by_caller;
+ return 1;
+}
+
+static int eddsa_signverify_init(void *vpeddsactx, void *vedkey)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ ECX_KEY *edkey = (ECX_KEY *)vedkey;
+ WPACKET pkt;
+ int ret;
+ unsigned char *aid = NULL;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (edkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (!ossl_ecx_key_up_ref(edkey)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ peddsactx->instance_id_preset_flag = 0;
+ peddsactx->dom2_flag = 0;
+ peddsactx->prehash_flag = 0;
+ peddsactx->context_string_flag = 0;
+ peddsactx->context_string_len = 0;
+
+ peddsactx->key = edkey;
+
+ /*
+ * We do not care about DER writing errors.
+ * All it really means is that for some reason, there's no
+ * AlgorithmIdentifier to be had, but the operation itself is
+ * still valid, just as long as it's not used to construct
+ * anything that needs an AlgorithmIdentifier.
+ */
+ peddsactx->aid_len = 0;
+ ret = WPACKET_init_der(&pkt, peddsactx->aid_buf, sizeof(peddsactx->aid_buf));
+ switch (edkey->type) {
+ case ECX_KEY_TYPE_ED25519:
+ ret = ret && ossl_DER_w_algorithmIdentifier_ED25519(&pkt, -1, edkey);
+ break;
+ case ECX_KEY_TYPE_ED448:
+ ret = ret && ossl_DER_w_algorithmIdentifier_ED448(&pkt, -1, edkey);
+ break;
+ default:
+ /* Should never happen */
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ ossl_ecx_key_free(edkey);
+ peddsactx->key = NULL;
+ WPACKET_cleanup(&pkt);
+ return 0;
+ }
+ if (ret && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &peddsactx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && peddsactx->aid_len != 0)
+ memmove(peddsactx->aid_buf, aid, peddsactx->aid_len);
+
+ return 1;
+}
+
+static int ed25519_signverify_message_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 1, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed25519ph_signverify_message_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519ph, 1, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed25519ph_signverify_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519ph, 1, 1)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+/*
+ * This supports using ED25519 with EVP_PKEY_{sign,verify}_init_ex() and
+ * EVP_PKEY_{sign,verify}_init_ex2(), under the condition that the caller
+ * explicitly sets the Ed25519ph instance (this is verified by ed25519_sign()
+ * and ed25519_verify())
+ */
+static int ed25519_signverify_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 0, 1)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed25519ctx_signverify_message_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519ctx, 1, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed448_signverify_message_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed448, 1, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed448ph_signverify_message_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed448ph, 1, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed448ph_signverify_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed448ph, 1, 1)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+/*
+ * This supports using ED448 with EVP_PKEY_{sign,verify}_init_ex() and
+ * EVP_PKEY_{sign,verify}_init_ex2(), under the condition that the caller
+ * explicitly sets the Ed448ph instance (this is verified by ed448_sign()
+ * and ed448_verify())
+ */
+static int ed448_signverify_init(void *vpeddsactx, void *vedkey,
+ const OSSL_PARAM params[])
+{
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed448, 0, 1)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+/*
+ * This is used directly for OSSL_FUNC_SIGNATURE_SIGN and indirectly
+ * for OSSL_FUNC_SIGNATURE_DIGEST_SIGN
+ */
+static int ed25519_sign(void *vpeddsactx,
+ unsigned char *sigret, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ const ECX_KEY *edkey = peddsactx->key;
+ uint8_t md[EVP_MAX_MD_SIZE];
+ size_t mdlen;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sigret == NULL) {
+ *siglen = ED25519_SIGSIZE;
+ return 1;
+ }
+ if (sigsize < ED25519_SIGSIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (edkey->privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+#ifdef S390X_EC_ASM
+ /*
+ * s390x_ed25519_digestsign() does not yet support dom2 or context-strings.
+ * fall back to non-accelerated sign if those options are set, or pre-hasing
+ * is provided.
+ */
+ if (S390X_CAN_SIGN(ED25519)
+ && !peddsactx->dom2_flag
+ && !peddsactx->context_string_flag
+ && peddsactx->context_string_len == 0
+ && !peddsactx->prehash_flag
+ && !peddsactx->prehash_by_caller_flag) {
+ if (s390x_ed25519_digestsign(edkey, sigret, tbs, tbslen) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN);
+ return 0;
+ }
+ *siglen = ED25519_SIGSIZE;
+ return 1;
+ }
+#endif /* S390X_EC_ASM */
+
+ if (peddsactx->prehash_flag) {
+ if (!peddsactx->prehash_by_caller_flag) {
+ if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL,
+ tbs, tbslen, md, &mdlen)
+ || mdlen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH);
+ return 0;
+ }
+ tbs = md;
+ tbslen = mdlen;
+ } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ } else if (peddsactx->prehash_by_caller_flag) {
+ /* The caller is supposed to set up a ph instance! */
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION);
+ return 0;
+ }
+
+ if (ossl_ed25519_sign(sigret, tbs, tbslen, edkey->pubkey, edkey->privkey,
+ peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag,
+ peddsactx->context_string, peddsactx->context_string_len,
+ peddsactx->libctx, NULL) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN);
+ return 0;
+ }
+ *siglen = ED25519_SIGSIZE;
+ return 1;
+}
+
+/* EVP_Q_digest() does not allow variable output length for XOFs,
+ so we use this function */
+static int ed448_shake256(OSSL_LIB_CTX *libctx,
+ const char *propq,
+ const uint8_t *in, size_t inlen,
+ uint8_t *out, size_t outlen)
+{
+ int ret = 0;
+ EVP_MD_CTX *hash_ctx = EVP_MD_CTX_new();
+ EVP_MD *shake256 = EVP_MD_fetch(libctx, SN_shake256, propq);
+
+ if (hash_ctx == NULL || shake256 == NULL)
+ goto err;
+
+ if (!EVP_DigestInit_ex(hash_ctx, shake256, NULL)
+ || !EVP_DigestUpdate(hash_ctx, in, inlen)
+ || !EVP_DigestFinalXOF(hash_ctx, out, outlen))
+ goto err;
+
+ ret = 1;
+
+ err:
+ EVP_MD_CTX_free(hash_ctx);
+ EVP_MD_free(shake256);
+ return ret;
+}
+
+/*
+ * This is used directly for OSSL_FUNC_SIGNATURE_SIGN and indirectly
+ * for OSSL_FUNC_SIGNATURE_DIGEST_SIGN
+ */
+static int ed448_sign(void *vpeddsactx,
+ unsigned char *sigret, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ const ECX_KEY *edkey = peddsactx->key;
+ uint8_t md[EDDSA_PREHASH_OUTPUT_LEN];
+ size_t mdlen = sizeof(md);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sigret == NULL) {
+ *siglen = ED448_SIGSIZE;
+ return 1;
+ }
+ if (sigsize < ED448_SIGSIZE) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+ if (edkey->privkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NOT_A_PRIVATE_KEY);
+ return 0;
+ }
+#ifdef S390X_EC_ASM
+ /*
+ * s390x_ed448_digestsign() does not yet support context-strings or
+ * pre-hashing. Fall back to non-accelerated sign if a context-string or
+ * pre-hasing is provided.
+ */
+ if (S390X_CAN_SIGN(ED448)
+ && peddsactx->context_string_len == 0
+ && !peddsactx->prehash_flag
+ && !peddsactx->prehash_by_caller_flag) {
+ if (s390x_ed448_digestsign(edkey, sigret, tbs, tbslen) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN);
+ return 0;
+ }
+ *siglen = ED448_SIGSIZE;
+ return 1;
+ }
+#endif /* S390X_EC_ASM */
+
+ if (peddsactx->prehash_flag) {
+ if (!peddsactx->prehash_by_caller_flag) {
+ if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen))
+ return 0;
+ tbs = md;
+ tbslen = mdlen;
+ } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ } else if (peddsactx->prehash_by_caller_flag) {
+ /* The caller is supposed to set up a ph instance! */
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION);
+ return 0;
+ }
+
+ if (ossl_ed448_sign(peddsactx->libctx, sigret, tbs, tbslen,
+ edkey->pubkey, edkey->privkey,
+ peddsactx->context_string, peddsactx->context_string_len,
+ peddsactx->prehash_flag, edkey->propq) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SIGN);
+ return 0;
+ }
+ *siglen = ED448_SIGSIZE;
+ return 1;
+}
+
+/*
+ * This is used directly for OSSL_FUNC_SIGNATURE_VERIFY and indirectly
+ * for OSSL_FUNC_SIGNATURE_DIGEST_VERIFY
+ */
+static int ed25519_verify(void *vpeddsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ const ECX_KEY *edkey = peddsactx->key;
+ uint8_t md[EVP_MAX_MD_SIZE];
+ size_t mdlen;
+
+ if (!ossl_prov_is_running() || siglen != ED25519_SIGSIZE)
+ return 0;
+
+#ifdef S390X_EC_ASM
+ /*
+ * s390x_ed25519_digestverify() does not yet support dom2 or context-strings.
+ * fall back to non-accelerated verify if those options are set, or
+ * pre-hasing is provided.
+ */
+ if (S390X_CAN_SIGN(ED25519)
+ && !peddsactx->dom2_flag
+ && !peddsactx->context_string_flag
+ && peddsactx->context_string_len == 0
+ && !peddsactx->prehash_flag
+ && !peddsactx->prehash_by_caller_flag)
+ return s390x_ed25519_digestverify(edkey, sig, tbs, tbslen);
+#endif /* S390X_EC_ASM */
+
+ if (peddsactx->prehash_flag) {
+ if (!peddsactx->prehash_by_caller_flag) {
+ if (!EVP_Q_digest(peddsactx->libctx, SN_sha512, NULL,
+ tbs, tbslen, md, &mdlen)
+ || mdlen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PREHASHED_DIGEST_LENGTH);
+ return 0;
+ }
+ tbs = md;
+ tbslen = mdlen;
+ } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ } else if (peddsactx->prehash_by_caller_flag) {
+ /* The caller is supposed to set up a ph instance! */
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION);
+ return 0;
+ }
+
+ return ossl_ed25519_verify(tbs, tbslen, sig, edkey->pubkey,
+ peddsactx->dom2_flag, peddsactx->prehash_flag, peddsactx->context_string_flag,
+ peddsactx->context_string, peddsactx->context_string_len,
+ peddsactx->libctx, edkey->propq);
+}
+
+/*
+ * This is used directly for OSSL_FUNC_SIGNATURE_VERIFY and indirectly
+ * for OSSL_FUNC_SIGNATURE_DIGEST_VERIFY
+ */
+static int ed448_verify(void *vpeddsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ const ECX_KEY *edkey = peddsactx->key;
+ uint8_t md[EDDSA_PREHASH_OUTPUT_LEN];
+ size_t mdlen = sizeof(md);
+
+ if (!ossl_prov_is_running() || siglen != ED448_SIGSIZE)
+ return 0;
+
+#ifdef S390X_EC_ASM
+ /*
+ * s390x_ed448_digestverify() does not yet support context-strings or
+ * pre-hashing. Fall back to non-accelerated verify if a context-string or
+ * pre-hasing is provided.
+ */
+ if (S390X_CAN_SIGN(ED448)
+ && peddsactx->context_string_len == 0
+ && !peddsactx->prehash_flag
+ && !peddsactx->prehash_by_caller_flag)
+ return s390x_ed448_digestverify(edkey, sig, tbs, tbslen);
+#endif /* S390X_EC_ASM */
+
+ if (peddsactx->prehash_flag) {
+ if (!peddsactx->prehash_by_caller_flag) {
+ if (!ed448_shake256(peddsactx->libctx, NULL, tbs, tbslen, md, mdlen))
+ return 0;
+ tbs = md;
+ tbslen = mdlen;
+ } else if (tbslen != EDDSA_PREHASH_OUTPUT_LEN) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+ } else if (peddsactx->prehash_by_caller_flag) {
+ /* The caller is supposed to set up a ph instance! */
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_INVALID_EDDSA_INSTANCE_FOR_ATTEMPTED_OPERATION);
+ return 0;
+ }
+
+ return ossl_ed448_verify(peddsactx->libctx, tbs, tbslen, sig, edkey->pubkey,
+ peddsactx->context_string, peddsactx->context_string_len,
+ peddsactx->prehash_flag, edkey->propq);
+}
+
+/* All digest_{sign,verify} are simple wrappers around the functions above */
+
+static int ed25519_digest_signverify_init(void *vpeddsactx, const char *mdname,
+ void *vedkey,
+ const OSSL_PARAM params[])
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+
+ if (mdname != NULL && mdname[0] != '\0') {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "Explicit digest not allowed with EdDSA operations");
+ return 0;
+ }
+
+ if (vedkey == NULL && peddsactx->key != NULL)
+ return eddsa_set_ctx_params(peddsactx, params);
+
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed25519, 0, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed25519_digest_sign(void *vpeddsactx,
+ unsigned char *sigret, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ return ed25519_sign(vpeddsactx, sigret, siglen, sigsize, tbs, tbslen);
+}
+
+static int ed25519_digest_verify(void *vpeddsactx,
+ const unsigned char *sigret, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ return ed25519_verify(vpeddsactx, sigret, siglen, tbs, tbslen);
+}
+
+static int ed448_digest_signverify_init(void *vpeddsactx, const char *mdname,
+ void *vedkey,
+ const OSSL_PARAM params[])
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+
+ if (mdname != NULL && mdname[0] != '\0') {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "Explicit digest not allowed with EdDSA operations");
+ return 0;
+ }
+
+ if (vedkey == NULL && peddsactx->key != NULL)
+ return eddsa_set_ctx_params(peddsactx, params);
+
+ return eddsa_signverify_init(vpeddsactx, vedkey)
+ && eddsa_setup_instance(vpeddsactx, ID_Ed448, 0, 0)
+ && eddsa_set_ctx_params(vpeddsactx, params);
+}
+
+static int ed448_digest_sign(void *vpeddsactx,
+ unsigned char *sigret, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ return ed448_sign(vpeddsactx, sigret, siglen, sigsize, tbs, tbslen);
+}
+
+static int ed448_digest_verify(void *vpeddsactx,
+ const unsigned char *sigret, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ return ed448_verify(vpeddsactx, sigret, siglen, tbs, tbslen);
+}
+
+static void eddsa_freectx(void *vpeddsactx)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+
+ ossl_ecx_key_free(peddsactx->key);
+
+ OPENSSL_free(peddsactx);
+}
+
+static void *eddsa_dupctx(void *vpeddsactx)
+{
+ PROV_EDDSA_CTX *srcctx = (PROV_EDDSA_CTX *)vpeddsactx;
+ PROV_EDDSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->key = NULL;
+
+ if (srcctx->key != NULL && !ossl_ecx_key_up_ref(srcctx->key)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ goto err;
+ }
+ dstctx->key = srcctx->key;
+
+ return dstctx;
+ err:
+ eddsa_freectx(dstctx);
+ return NULL;
+}
+
+static const char **ed25519_sigalg_query_key_types(void)
+{
+ static const char *keytypes[] = { "ED25519", NULL };
+
+ return keytypes;
+}
+
+static const char **ed448_sigalg_query_key_types(void)
+{
+ static const char *keytypes[] = { "ED448", NULL };
+
+ return keytypes;
+}
+
+
+
+static int eddsa_get_ctx_params(void *vpeddsactx, OSSL_PARAM *params)
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ OSSL_PARAM *p;
+
+ if (peddsactx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p,
+ peddsactx->aid_len == 0 ? NULL : peddsactx->aid_buf,
+ peddsactx->aid_len))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *eddsa_gettable_ctx_params(ossl_unused void *vpeddsactx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int eddsa_set_ctx_params(void *vpeddsactx, const OSSL_PARAM params[])
+{
+ PROV_EDDSA_CTX *peddsactx = (PROV_EDDSA_CTX *)vpeddsactx;
+ const OSSL_PARAM *p;
+
+ if (peddsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_INSTANCE);
+ if (p != NULL) {
+ char instance_name[OSSL_MAX_NAME_SIZE] = "";
+ char *pinstance_name = instance_name;
+
+ if (peddsactx->instance_id_preset_flag) {
+ /* When the instance is preset, the caller must no try to set it */
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NO_INSTANCE_ALLOWED,
+ "the EdDSA instance is preset, you may not try to specify it",
+ NULL);
+ return 0;
+ }
+
+ if (!OSSL_PARAM_get_utf8_string(p, &pinstance_name, sizeof(instance_name)))
+ return 0;
+
+ /*
+ * When setting the new instance, we're careful not to change the
+ * prehash_by_caller flag, as that's always preset by the init
+ * functions. The sign functions will determine if the instance
+ * matches this flag.
+ */
+ if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519) == 0) {
+ eddsa_setup_instance(peddsactx, ID_Ed25519, 0,
+ peddsactx->prehash_by_caller_flag);
+ } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ctx) == 0) {
+ eddsa_setup_instance(peddsactx, ID_Ed25519ctx, 0,
+ peddsactx->prehash_by_caller_flag);
+ } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed25519ph) == 0) {
+ eddsa_setup_instance(peddsactx, ID_Ed25519ph, 0,
+ peddsactx->prehash_by_caller_flag);
+ } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448) == 0) {
+ eddsa_setup_instance(peddsactx, ID_Ed448, 0,
+ peddsactx->prehash_by_caller_flag);
+ } else if (OPENSSL_strcasecmp(pinstance_name, SN_Ed448ph) == 0) {
+ eddsa_setup_instance(peddsactx, ID_Ed448ph, 0,
+ peddsactx->prehash_by_caller_flag);
+ } else {
+ /* we did not recognize the instance */
+ return 0;
+ }
+
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING);
+ if (p != NULL) {
+ void *vp_context_string = peddsactx->context_string;
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp_context_string, sizeof(peddsactx->context_string), &(peddsactx->context_string_len))) {
+ peddsactx->context_string_len = 0;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_INSTANCE, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *eddsa_settable_ctx_params(ossl_unused void *vpeddsactx,
+ ossl_unused void *provctx)
+{
+ return settable_ctx_params;
+}
+
+static const OSSL_PARAM settable_variant_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *
+eddsa_settable_variant_ctx_params(ossl_unused void *vpeddsactx,
+ ossl_unused void *provctx)
+{
+ return settable_variant_ctx_params;
+}
+
+/*
+ * Ed25519 can be used with:
+ * - EVP_PKEY_sign_init_ex2() [ instance and prehash assumed done by caller ]
+ * - EVP_PKEY_verify_init_ex2() [ instance and prehash assumed done by caller ]
+ * - EVP_PKEY_sign_message_init()
+ * - EVP_PKEY_verify_message_init()
+ * - EVP_DigestSignInit_ex()
+ * - EVP_DigestVerifyInit_ex()
+ * Ed25519ph can be used with:
+ * - EVP_PKEY_sign_init_ex2() [ prehash assumed done by caller ]
+ * - EVP_PKEY_verify_init_ex2() [ prehash assumed done by caller ]
+ * - EVP_PKEY_sign_message_init()
+ * - EVP_PKEY_verify_message_init()
+ * Ed25519ctx can be used with:
+ * - EVP_PKEY_sign_message_init()
+ * - EVP_PKEY_verify_message_init()
+ * Ed448 can be used with:
+ * - EVP_PKEY_sign_init_ex2() [ instance and prehash assumed done by caller ]
+ * - EVP_PKEY_verify_init_ex2() [ instance and prehash assumed done by caller ]
+ * - EVP_PKEY_sign_message_init()
+ * - EVP_PKEY_verify_message_init()
+ * - EVP_DigestSignInit_ex()
+ * - EVP_DigestVerifyInit_ex()
+ * Ed448ph can be used with:
+ * - EVP_PKEY_sign_init_ex2() [ prehash assumed done by caller ]
+ * - EVP_PKEY_verify_init_ex2() [ prehash assumed done by caller ]
+ * - EVP_PKEY_sign_message_init()
+ * - EVP_PKEY_verify_message_init()
+ */
+
+#define ed25519_DISPATCH_END \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))ed25519_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))ed25519_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \
+ (void (*)(void))ed25519_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \
+ (void (*)(void))ed25519_digest_sign }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \
+ (void (*)(void))ed25519_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \
+ (void (*)(void))ed25519_digest_verify }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))eddsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))eddsa_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_settable_ctx_params }, \
+ OSSL_DISPATCH_END
+
+#define eddsa_variant_DISPATCH_END(v) \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))v##_signverify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))v##_signverify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))eddsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))eddsa_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_settable_variant_ctx_params }, \
+ OSSL_DISPATCH_END
+
+#define ed25519ph_DISPATCH_END \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))ed25519ph_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))ed25519ph_signverify_init }, \
+ eddsa_variant_DISPATCH_END(ed25519ph)
+
+#define ed25519ctx_DISPATCH_END eddsa_variant_DISPATCH_END(ed25519ctx)
+
+#define ed448_DISPATCH_END \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))ed448_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))ed448_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \
+ (void (*)(void))ed448_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \
+ (void (*)(void))ed448_digest_sign }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \
+ (void (*)(void))ed448_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \
+ (void (*)(void))ed448_digest_verify }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))eddsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))eddsa_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))eddsa_settable_ctx_params }, \
+ OSSL_DISPATCH_END
+
+#define ed448ph_DISPATCH_END \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))ed448ph_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))ed448ph_signverify_init }, \
+ eddsa_variant_DISPATCH_END(ed448ph)
+
+/* vn = variant name, bn = base name */
+#define IMPL_EDDSA_DISPATCH(vn,bn) \
+ const OSSL_DISPATCH ossl_##vn##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))eddsa_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))vn##_signverify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, \
+ (void (*)(void))bn##_sign }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))vn##_signverify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, \
+ (void (*)(void))bn##_verify }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))eddsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))eddsa_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \
+ (void (*)(void))bn##_sigalg_query_key_types }, \
+ vn##_DISPATCH_END \
+ }
+
+IMPL_EDDSA_DISPATCH(ed25519,ed25519);
+IMPL_EDDSA_DISPATCH(ed25519ph,ed25519);
+IMPL_EDDSA_DISPATCH(ed25519ctx,ed25519);
+IMPL_EDDSA_DISPATCH(ed448,ed448);
+IMPL_EDDSA_DISPATCH(ed448ph,ed448);
+
+#ifdef S390X_EC_ASM
+
+static int s390x_ed25519_digestsign(const ECX_KEY *edkey, unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen)
+{
+ int rc;
+ union {
+ struct {
+ unsigned char sig[64];
+ unsigned char priv[32];
+ } ed25519;
+ unsigned long long buff[512];
+ } param;
+
+ memset(&param, 0, sizeof(param));
+ memcpy(param.ed25519.priv, edkey->privkey, sizeof(param.ed25519.priv));
+
+ rc = s390x_kdsa(S390X_EDDSA_SIGN_ED25519, &param.ed25519, tbs, tbslen);
+ OPENSSL_cleanse(param.ed25519.priv, sizeof(param.ed25519.priv));
+ if (rc != 0)
+ return 0;
+
+ s390x_flip_endian32(sig, param.ed25519.sig);
+ s390x_flip_endian32(sig + 32, param.ed25519.sig + 32);
+ return 1;
+}
+
+static int s390x_ed448_digestsign(const ECX_KEY *edkey, unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen)
+{
+ int rc;
+ union {
+ struct {
+ unsigned char sig[128];
+ unsigned char priv[64];
+ } ed448;
+ unsigned long long buff[512];
+ } param;
+
+ memset(&param, 0, sizeof(param));
+ memcpy(param.ed448.priv + 64 - 57, edkey->privkey, 57);
+
+ rc = s390x_kdsa(S390X_EDDSA_SIGN_ED448, &param.ed448, tbs, tbslen);
+ OPENSSL_cleanse(param.ed448.priv, sizeof(param.ed448.priv));
+ if (rc != 0)
+ return 0;
+
+ s390x_flip_endian64(param.ed448.sig, param.ed448.sig);
+ s390x_flip_endian64(param.ed448.sig + 64, param.ed448.sig + 64);
+ memcpy(sig, param.ed448.sig, 57);
+ memcpy(sig + 57, param.ed448.sig + 64, 57);
+ return 1;
+}
+
+static int s390x_ed25519_digestverify(const ECX_KEY *edkey,
+ const unsigned char *sig,
+ const unsigned char *tbs, size_t tbslen)
+{
+ union {
+ struct {
+ unsigned char sig[64];
+ unsigned char pub[32];
+ } ed25519;
+ unsigned long long buff[512];
+ } param;
+
+ memset(&param, 0, sizeof(param));
+ s390x_flip_endian32(param.ed25519.sig, sig);
+ s390x_flip_endian32(param.ed25519.sig + 32, sig + 32);
+ s390x_flip_endian32(param.ed25519.pub, edkey->pubkey);
+
+ return s390x_kdsa(S390X_EDDSA_VERIFY_ED25519,
+ &param.ed25519, tbs, tbslen) == 0 ? 1 : 0;
+}
+
+static int s390x_ed448_digestverify(const ECX_KEY *edkey,
+ const unsigned char *sig,
+ const unsigned char *tbs,
+ size_t tbslen)
+{
+ union {
+ struct {
+ unsigned char sig[128];
+ unsigned char pub[64];
+ } ed448;
+ unsigned long long buff[512];
+ } param;
+
+ memset(&param, 0, sizeof(param));
+ memcpy(param.ed448.sig, sig, 57);
+ s390x_flip_endian64(param.ed448.sig, param.ed448.sig);
+ memcpy(param.ed448.sig + 64, sig + 57, 57);
+ s390x_flip_endian64(param.ed448.sig + 64, param.ed448.sig + 64);
+ memcpy(param.ed448.pub, edkey->pubkey, 57);
+ s390x_flip_endian64(param.ed448.pub, param.ed448.pub);
+
+ return s390x_kdsa(S390X_EDDSA_VERIFY_ED448,
+ &param.ed448, tbs, tbslen) == 0 ? 1 : 0;
+}
+
+#endif /* S390X_EC_ASM */
diff --git a/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c b/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c
new file mode 100644
index 000000000000..b25a74506ab0
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/mac_legacy_sig.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright 2019-2023 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* We need to use some engine deprecated APIs */
+#define OPENSSL_SUPPRESS_DEPRECATED
+
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#ifndef FIPS_MODULE
+# include <openssl/engine.h>
+#endif
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/macsignature.h"
+#include "prov/providercommon.h"
+
+static OSSL_FUNC_signature_newctx_fn mac_hmac_newctx;
+static OSSL_FUNC_signature_newctx_fn mac_siphash_newctx;
+static OSSL_FUNC_signature_newctx_fn mac_poly1305_newctx;
+static OSSL_FUNC_signature_newctx_fn mac_cmac_newctx;
+static OSSL_FUNC_signature_digest_sign_init_fn mac_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn mac_digest_sign_update;
+static OSSL_FUNC_signature_digest_sign_final_fn mac_digest_sign_final;
+static OSSL_FUNC_signature_freectx_fn mac_freectx;
+static OSSL_FUNC_signature_dupctx_fn mac_dupctx;
+static OSSL_FUNC_signature_set_ctx_params_fn mac_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn mac_hmac_settable_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn mac_siphash_settable_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn mac_poly1305_settable_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn mac_cmac_settable_ctx_params;
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ MAC_KEY *key;
+ EVP_MAC_CTX *macctx;
+} PROV_MAC_CTX;
+
+static void *mac_newctx(void *provctx, const char *propq, const char *macname)
+{
+ PROV_MAC_CTX *pmacctx;
+ EVP_MAC *mac = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ pmacctx = OPENSSL_zalloc(sizeof(PROV_MAC_CTX));
+ if (pmacctx == NULL)
+ return NULL;
+
+ pmacctx->libctx = PROV_LIBCTX_OF(provctx);
+ if (propq != NULL && (pmacctx->propq = OPENSSL_strdup(propq)) == NULL)
+ goto err;
+
+ mac = EVP_MAC_fetch(pmacctx->libctx, macname, propq);
+ if (mac == NULL)
+ goto err;
+
+ pmacctx->macctx = EVP_MAC_CTX_new(mac);
+ if (pmacctx->macctx == NULL)
+ goto err;
+
+ EVP_MAC_free(mac);
+
+ return pmacctx;
+
+ err:
+ OPENSSL_free(pmacctx->propq);
+ OPENSSL_free(pmacctx);
+ EVP_MAC_free(mac);
+ return NULL;
+}
+
+#define MAC_NEWCTX(funcname, macname) \
+ static void *mac_##funcname##_newctx(void *provctx, const char *propq) \
+ { \
+ return mac_newctx(provctx, propq, macname); \
+ }
+
+MAC_NEWCTX(hmac, "HMAC")
+MAC_NEWCTX(siphash, "SIPHASH")
+MAC_NEWCTX(poly1305, "POLY1305")
+MAC_NEWCTX(cmac, "CMAC")
+
+static int mac_digest_sign_init(void *vpmacctx, const char *mdname, void *vkey,
+ const OSSL_PARAM params[])
+{
+ PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx;
+ const char *ciphername = NULL, *engine = NULL;
+
+ if (!ossl_prov_is_running()
+ || pmacctx == NULL)
+ return 0;
+
+ if (pmacctx->key == NULL && vkey == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (vkey != NULL) {
+ if (!ossl_mac_key_up_ref(vkey))
+ return 0;
+ ossl_mac_key_free(pmacctx->key);
+ pmacctx->key = vkey;
+ }
+
+ if (pmacctx->key->cipher.cipher != NULL)
+ ciphername = (char *)EVP_CIPHER_get0_name(pmacctx->key->cipher.cipher);
+#if !defined(OPENSSL_NO_ENGINE) && !defined(FIPS_MODULE)
+ if (pmacctx->key->cipher.engine != NULL)
+ engine = (char *)ENGINE_get_id(pmacctx->key->cipher.engine);
+#endif
+
+ if (!ossl_prov_set_macctx(pmacctx->macctx, NULL,
+ (char *)ciphername,
+ (char *)mdname,
+ (char *)engine,
+ pmacctx->key->properties,
+ NULL, 0))
+ return 0;
+
+ if (!EVP_MAC_init(pmacctx->macctx, pmacctx->key->priv_key,
+ pmacctx->key->priv_key_len, params))
+ return 0;
+
+ return 1;
+}
+
+int mac_digest_sign_update(void *vpmacctx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx;
+
+ if (pmacctx == NULL || pmacctx->macctx == NULL)
+ return 0;
+
+ return EVP_MAC_update(pmacctx->macctx, data, datalen);
+}
+
+int mac_digest_sign_final(void *vpmacctx, unsigned char *mac, size_t *maclen,
+ size_t macsize)
+{
+ PROV_MAC_CTX *pmacctx = (PROV_MAC_CTX *)vpmacctx;
+
+ if (!ossl_prov_is_running() || pmacctx == NULL || pmacctx->macctx == NULL)
+ return 0;
+
+ return EVP_MAC_final(pmacctx->macctx, mac, maclen, macsize);
+}
+
+static void mac_freectx(void *vpmacctx)
+{
+ PROV_MAC_CTX *ctx = (PROV_MAC_CTX *)vpmacctx;
+
+ OPENSSL_free(ctx->propq);
+ EVP_MAC_CTX_free(ctx->macctx);
+ ossl_mac_key_free(ctx->key);
+ OPENSSL_free(ctx);
+}
+
+static void *mac_dupctx(void *vpmacctx)
+{
+ PROV_MAC_CTX *srcctx = (PROV_MAC_CTX *)vpmacctx;
+ PROV_MAC_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->propq = NULL;
+ dstctx->key = NULL;
+ dstctx->macctx = NULL;
+
+ if (srcctx->propq != NULL && (dstctx->propq = OPENSSL_strdup(srcctx->propq)) == NULL)
+ goto err;
+
+ if (srcctx->key != NULL && !ossl_mac_key_up_ref(srcctx->key))
+ goto err;
+ dstctx->key = srcctx->key;
+
+ if (srcctx->macctx != NULL) {
+ dstctx->macctx = EVP_MAC_CTX_dup(srcctx->macctx);
+ if (dstctx->macctx == NULL)
+ goto err;
+ }
+
+ return dstctx;
+ err:
+ mac_freectx(dstctx);
+ return NULL;
+}
+
+static int mac_set_ctx_params(void *vpmacctx, const OSSL_PARAM params[])
+{
+ PROV_MAC_CTX *ctx = (PROV_MAC_CTX *)vpmacctx;
+
+ return EVP_MAC_CTX_set_params(ctx->macctx, params);
+}
+
+static const OSSL_PARAM *mac_settable_ctx_params(ossl_unused void *ctx,
+ void *provctx,
+ const char *macname)
+{
+ EVP_MAC *mac = EVP_MAC_fetch(PROV_LIBCTX_OF(provctx), macname,
+ NULL);
+ const OSSL_PARAM *params;
+
+ if (mac == NULL)
+ return NULL;
+
+ params = EVP_MAC_settable_ctx_params(mac);
+ EVP_MAC_free(mac);
+
+ return params;
+}
+
+#define MAC_SETTABLE_CTX_PARAMS(funcname, macname) \
+ static const OSSL_PARAM *mac_##funcname##_settable_ctx_params(void *ctx, \
+ void *provctx) \
+ { \
+ return mac_settable_ctx_params(ctx, provctx, macname); \
+ }
+
+MAC_SETTABLE_CTX_PARAMS(hmac, "HMAC")
+MAC_SETTABLE_CTX_PARAMS(siphash, "SIPHASH")
+MAC_SETTABLE_CTX_PARAMS(poly1305, "POLY1305")
+MAC_SETTABLE_CTX_PARAMS(cmac, "CMAC")
+
+#define MAC_SIGNATURE_FUNCTIONS(funcname) \
+ const OSSL_DISPATCH ossl_mac_legacy_##funcname##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))mac_##funcname##_newctx }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \
+ (void (*)(void))mac_digest_sign_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE, \
+ (void (*)(void))mac_digest_sign_update }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL, \
+ (void (*)(void))mac_digest_sign_final }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))mac_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))mac_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))mac_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))mac_##funcname##_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ };
+
+MAC_SIGNATURE_FUNCTIONS(hmac)
+MAC_SIGNATURE_FUNCTIONS(siphash)
+MAC_SIGNATURE_FUNCTIONS(poly1305)
+MAC_SIGNATURE_FUNCTIONS(cmac)
diff --git a/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c b/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c
new file mode 100644
index 000000000000..e235e31752eb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/ml_dsa_sig.c
@@ -0,0 +1,368 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include "internal/deprecated.h"
+
+#include <assert.h>
+#include <string.h> /* memset */
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/der_ml_dsa.h"
+#include "crypto/ml_dsa.h"
+#include "internal/packet.h"
+#include "internal/sizes.h"
+
+#define ML_DSA_MESSAGE_ENCODE_RAW 0
+#define ML_DSA_MESSAGE_ENCODE_PURE 1
+
+static OSSL_FUNC_signature_sign_message_init_fn ml_dsa_sign_msg_init;
+static OSSL_FUNC_signature_sign_fn ml_dsa_sign;
+static OSSL_FUNC_signature_verify_message_init_fn ml_dsa_verify_msg_init;
+static OSSL_FUNC_signature_verify_fn ml_dsa_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn ml_dsa_digest_signverify_init;
+static OSSL_FUNC_signature_digest_sign_fn ml_dsa_digest_sign;
+static OSSL_FUNC_signature_digest_verify_fn ml_dsa_digest_verify;
+static OSSL_FUNC_signature_freectx_fn ml_dsa_freectx;
+static OSSL_FUNC_signature_set_ctx_params_fn ml_dsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn ml_dsa_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_params_fn ml_dsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn ml_dsa_gettable_ctx_params;
+static OSSL_FUNC_signature_dupctx_fn ml_dsa_dupctx;
+
+typedef struct {
+ ML_DSA_KEY *key;
+ OSSL_LIB_CTX *libctx;
+ uint8_t context_string[ML_DSA_MAX_CONTEXT_STRING_LEN];
+ size_t context_string_len;
+ uint8_t test_entropy[ML_DSA_ENTROPY_LEN];
+ size_t test_entropy_len;
+ int msg_encode;
+ int deterministic;
+ int evp_type;
+ /* The Algorithm Identifier of the signature algorithm */
+ uint8_t aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+ int mu; /* Flag indicating we should begin from \mu, not the message */
+} PROV_ML_DSA_CTX;
+
+static void ml_dsa_freectx(void *vctx)
+{
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+
+ OPENSSL_cleanse(ctx->test_entropy, ctx->test_entropy_len);
+ OPENSSL_free(ctx);
+}
+
+static void *ml_dsa_newctx(void *provctx, int evp_type, const char *propq)
+{
+ PROV_ML_DSA_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(PROV_ML_DSA_CTX));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ ctx->msg_encode = ML_DSA_MESSAGE_ENCODE_PURE;
+ ctx->evp_type = evp_type;
+ return ctx;
+}
+
+static void *ml_dsa_dupctx(void *vctx)
+{
+ PROV_ML_DSA_CTX *srcctx = (PROV_ML_DSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ /*
+ * Note that the ML_DSA_KEY is ref counted via EVP_PKEY so we can just copy
+ * the key here.
+ */
+ return OPENSSL_memdup(srcctx, sizeof(*srcctx));
+}
+
+static int set_alg_id_buffer(PROV_ML_DSA_CTX *ctx)
+{
+ int ret;
+ WPACKET pkt;
+ uint8_t *aid = NULL;
+
+ /*
+ * We do not care about DER writing errors.
+ * All it really means is that for some reason, there's no
+ * AlgorithmIdentifier to be had, but the operation itself is
+ * still valid, just as long as it's not used to construct
+ * anything that needs an AlgorithmIdentifier.
+ */
+ ctx->aid_len = 0;
+ ret = WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf));
+ ret = ret && ossl_DER_w_algorithmIdentifier_ML_DSA(&pkt, -1, ctx->key);
+ if (ret && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &ctx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && ctx->aid_len != 0)
+ memmove(ctx->aid_buf, aid, ctx->aid_len);
+ return 1;
+}
+
+static int ml_dsa_signverify_msg_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+ ML_DSA_KEY *key = vkey;
+
+ if (!ossl_prov_is_running()
+ || ctx == NULL)
+ return 0;
+
+ if (vkey == NULL && ctx->key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (key != NULL)
+ ctx->key = vkey;
+ if (!ossl_ml_dsa_key_matches(ctx->key, ctx->evp_type))
+ return 0;
+
+ set_alg_id_buffer(ctx);
+ ctx->mu = 0;
+
+ return ml_dsa_set_ctx_params(ctx, params);
+}
+
+static int ml_dsa_sign_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ return ml_dsa_signverify_msg_init(vctx, vkey, params,
+ EVP_PKEY_OP_SIGN, "ML_DSA Sign Init");
+}
+
+static int ml_dsa_digest_signverify_init(void *vctx, const char *mdname,
+ void *vkey, const OSSL_PARAM params[])
+{
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+
+ if (mdname != NULL && mdname[0] != '\0') {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "Explicit digest not supported for ML-DSA operations");
+ return 0;
+ }
+
+ ctx->mu = 0;
+
+ if (vkey == NULL && ctx->key != NULL)
+ return ml_dsa_set_ctx_params(ctx, params);
+
+ return ml_dsa_signverify_msg_init(vctx, vkey, params,
+ EVP_PKEY_OP_SIGN, "ML_DSA Sign Init");
+}
+
+static int ml_dsa_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize,
+ const uint8_t *msg, size_t msg_len)
+{
+ int ret = 0;
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+ uint8_t rand_tmp[ML_DSA_ENTROPY_LEN], *rnd = NULL;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sig != NULL) {
+ if (ctx->test_entropy_len != 0) {
+ rnd = ctx->test_entropy;
+ } else {
+ rnd = rand_tmp;
+
+ if (ctx->deterministic == 1)
+ memset(rnd, 0, sizeof(rand_tmp));
+ else if (RAND_priv_bytes_ex(ctx->libctx, rnd, sizeof(rand_tmp), 0) <= 0)
+ return 0;
+ }
+ }
+ ret = ossl_ml_dsa_sign(ctx->key, ctx->mu, msg, msg_len,
+ ctx->context_string, ctx->context_string_len,
+ rnd, sizeof(rand_tmp), ctx->msg_encode,
+ sig, siglen, sigsize);
+ if (rnd != ctx->test_entropy)
+ OPENSSL_cleanse(rand_tmp, sizeof(rand_tmp));
+ return ret;
+}
+
+static int ml_dsa_digest_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize,
+ const uint8_t *tbs, size_t tbslen)
+{
+ return ml_dsa_sign(vctx, sig, siglen, sigsize, tbs, tbslen);
+}
+
+static int ml_dsa_verify_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ return ml_dsa_signverify_msg_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY,
+ "ML_DSA Verify Init");
+}
+
+static int ml_dsa_verify(void *vctx, const uint8_t *sig, size_t siglen,
+ const uint8_t *msg, size_t msg_len)
+{
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_ml_dsa_verify(ctx->key, ctx->mu, msg, msg_len,
+ ctx->context_string, ctx->context_string_len,
+ ctx->msg_encode, sig, siglen);
+}
+static int ml_dsa_digest_verify(void *vctx,
+ const uint8_t *sig, size_t siglen,
+ const uint8_t *tbs, size_t tbslen)
+{
+ return ml_dsa_verify(vctx, sig, siglen, tbs, tbslen);
+}
+
+static int ml_dsa_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_ML_DSA_CTX *pctx = (PROV_ML_DSA_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (pctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING);
+ if (p != NULL) {
+ void *vp = pctx->context_string;
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->context_string),
+ &(pctx->context_string_len))) {
+ pctx->context_string_len = 0;
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_TEST_ENTROPY);
+ if (p != NULL) {
+ void *vp = pctx->test_entropy;
+
+ pctx->test_entropy_len = 0;
+ if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->test_entropy),
+ &(pctx->test_entropy_len)))
+ return 0;
+ if (pctx->test_entropy_len != sizeof(pctx->test_entropy)) {
+ pctx->test_entropy_len = 0;
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SEED_LENGTH);
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DETERMINISTIC);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->deterministic))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->msg_encode))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MU);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->mu))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *ml_dsa_settable_ctx_params(void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, NULL, 0),
+ OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 0),
+ OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MU, 0),
+ OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 0),
+ OSSL_PARAM_END
+ };
+
+ return settable_ctx_params;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *ml_dsa_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int ml_dsa_get_ctx_params(void *vctx, OSSL_PARAM *params)
+{
+ PROV_ML_DSA_CTX *ctx = (PROV_ML_DSA_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (ctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p,
+ ctx->aid_len == 0 ? NULL : ctx->aid_buf,
+ ctx->aid_len))
+ return 0;
+
+ return 1;
+}
+
+#define MAKE_SIGNATURE_FUNCTIONS(alg) \
+ static OSSL_FUNC_signature_newctx_fn ml_dsa_##alg##_newctx; \
+ static void *ml_dsa_##alg##_newctx(void *provctx, const char *propq) \
+ { \
+ return ml_dsa_newctx(provctx, EVP_PKEY_ML_DSA_##alg, propq); \
+ } \
+ const OSSL_DISPATCH ossl_ml_dsa_##alg##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))ml_dsa_##alg##_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))ml_dsa_sign_msg_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))ml_dsa_sign }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))ml_dsa_verify_msg_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))ml_dsa_verify }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \
+ (void (*)(void))ml_dsa_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \
+ (void (*)(void))ml_dsa_digest_sign }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \
+ (void (*)(void))ml_dsa_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \
+ (void (*)(void))ml_dsa_digest_verify }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))ml_dsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))ml_dsa_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))ml_dsa_settable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))ml_dsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))ml_dsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))ml_dsa_dupctx }, \
+ OSSL_DISPATCH_END \
+ }
+
+MAKE_SIGNATURE_FUNCTIONS(44);
+MAKE_SIGNATURE_FUNCTIONS(65);
+MAKE_SIGNATURE_FUNCTIONS(87);
diff --git a/crypto/openssl/providers/implementations/signature/rsa_sig.c b/crypto/openssl/providers/implementations/signature/rsa_sig.c
new file mode 100644
index 000000000000..d8357cfe1578
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/rsa_sig.c
@@ -0,0 +1,2144 @@
+/*
+ * Copyright 2019-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * RSA low level APIs are deprecated for public use, but still ok for
+ * internal use.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h>
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/proverr.h>
+#include "internal/cryptlib.h"
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "crypto/rsa.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "prov/provider_ctx.h"
+#include "prov/der_rsa.h"
+#include "prov/securitycheck.h"
+
+#define RSA_DEFAULT_DIGEST_NAME OSSL_DIGEST_NAME_SHA1
+
+static OSSL_FUNC_signature_newctx_fn rsa_newctx;
+static OSSL_FUNC_signature_sign_init_fn rsa_sign_init;
+static OSSL_FUNC_signature_verify_init_fn rsa_verify_init;
+static OSSL_FUNC_signature_verify_recover_init_fn rsa_verify_recover_init;
+static OSSL_FUNC_signature_sign_fn rsa_sign;
+static OSSL_FUNC_signature_sign_message_update_fn rsa_signverify_message_update;
+static OSSL_FUNC_signature_sign_message_final_fn rsa_sign_message_final;
+static OSSL_FUNC_signature_verify_fn rsa_verify;
+static OSSL_FUNC_signature_verify_recover_fn rsa_verify_recover;
+static OSSL_FUNC_signature_verify_message_update_fn rsa_signverify_message_update;
+static OSSL_FUNC_signature_verify_message_final_fn rsa_verify_message_final;
+static OSSL_FUNC_signature_digest_sign_init_fn rsa_digest_sign_init;
+static OSSL_FUNC_signature_digest_sign_update_fn rsa_digest_sign_update;
+static OSSL_FUNC_signature_digest_sign_final_fn rsa_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn rsa_digest_verify_init;
+static OSSL_FUNC_signature_digest_verify_update_fn rsa_digest_verify_update;
+static OSSL_FUNC_signature_digest_verify_final_fn rsa_digest_verify_final;
+static OSSL_FUNC_signature_freectx_fn rsa_freectx;
+static OSSL_FUNC_signature_dupctx_fn rsa_dupctx;
+static OSSL_FUNC_signature_query_key_types_fn rsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_get_ctx_params_fn rsa_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn rsa_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn rsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn rsa_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn rsa_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn rsa_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn rsa_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn rsa_settable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_params_fn rsa_sigalg_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn rsa_sigalg_settable_ctx_params;
+
+static OSSL_ITEM padding_item[] = {
+ { RSA_PKCS1_PADDING, OSSL_PKEY_RSA_PAD_MODE_PKCSV15 },
+ { RSA_NO_PADDING, OSSL_PKEY_RSA_PAD_MODE_NONE },
+ { RSA_X931_PADDING, OSSL_PKEY_RSA_PAD_MODE_X931 },
+ { RSA_PKCS1_PSS_PADDING, OSSL_PKEY_RSA_PAD_MODE_PSS },
+ { 0, NULL }
+};
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes RSA structures, so
+ * we use that here too.
+ */
+
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ RSA *rsa;
+ int operation;
+
+ /*
+ * Flag to determine if a full sigalg is run (1) or if a composable
+ * signature algorithm is run (0).
+ *
+ * When a full sigalg is run (1), this currently affects the following
+ * other flags, which are to remain untouched after their initialization:
+ *
+ * - flag_allow_md (initialized to 0)
+ */
+ unsigned int flag_sigalg : 1;
+ /*
+ * Flag to determine if the hash function can be changed (1) or not (0)
+ * Because it's dangerous to change during a DigestSign or DigestVerify
+ * operation, this flag is cleared by their Init function, and set again
+ * by their Final function.
+ * Implementations of full sigalgs (such as RSA-SHA256) hard-code this
+ * flag to not allow changes (0).
+ */
+ unsigned int flag_allow_md : 1;
+ unsigned int mgf1_md_set : 1;
+ /*
+ * Flags to say what are the possible next external calls in what
+ * consitutes the life cycle of an algorithm. The relevant calls are:
+ * - init
+ * - update
+ * - final
+ * - oneshot
+ * All other external calls are regarded as utilitarian and are allowed
+ * at any time (they may be affected by other flags, like flag_allow_md,
+ * though).
+ */
+ unsigned int flag_allow_update : 1;
+ unsigned int flag_allow_final : 1;
+ unsigned int flag_allow_oneshot : 1;
+
+ /* main digest */
+ EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+ int mdnid;
+ char mdname[OSSL_MAX_NAME_SIZE]; /* Purely informational */
+
+ /* RSA padding mode */
+ int pad_mode;
+ /* message digest for MGF1 */
+ EVP_MD *mgf1_md;
+ int mgf1_mdnid;
+ char mgf1_mdname[OSSL_MAX_NAME_SIZE]; /* Purely informational */
+ /* PSS salt length */
+ int saltlen;
+ /* Minimum salt length or -1 if no PSS parameter restriction */
+ int min_saltlen;
+
+ /* Signature, for verification */
+ unsigned char *sig;
+ size_t siglen;
+
+#ifdef FIPS_MODULE
+ /*
+ * FIPS 140-3 IG 2.4.B mandates that verification based on a digest of a
+ * message is not permitted. However, signing based on a digest is still
+ * permitted.
+ */
+ int verify_message;
+#endif
+
+ /* Temp buffer */
+ unsigned char *tbuf;
+
+ OSSL_FIPS_IND_DECLARE
+} PROV_RSA_CTX;
+
+/* True if PSS parameters are restricted */
+#define rsa_pss_restricted(prsactx) (prsactx->min_saltlen != -1)
+
+static size_t rsa_get_md_size(const PROV_RSA_CTX *prsactx)
+{
+ int md_size;
+
+ if (prsactx->md != NULL) {
+ md_size = EVP_MD_get_size(prsactx->md);
+ if (md_size <= 0)
+ return 0;
+ return md_size;
+ }
+ return 0;
+}
+
+static int rsa_check_padding(const PROV_RSA_CTX *prsactx,
+ const char *mdname, const char *mgf1_mdname,
+ int mdnid)
+{
+ switch (prsactx->pad_mode) {
+ case RSA_NO_PADDING:
+ if (mdname != NULL || mdnid != NID_undef) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE);
+ return 0;
+ }
+ break;
+ case RSA_X931_PADDING:
+ if (RSA_X931_hash_id(mdnid) == -1) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_X931_DIGEST);
+ return 0;
+ }
+ break;
+ case RSA_PKCS1_PSS_PADDING:
+ if (rsa_pss_restricted(prsactx))
+ if ((mdname != NULL && !EVP_MD_is_a(prsactx->md, mdname))
+ || (mgf1_mdname != NULL
+ && !EVP_MD_is_a(prsactx->mgf1_md, mgf1_mdname))) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED);
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+static int rsa_check_parameters(PROV_RSA_CTX *prsactx, int min_saltlen)
+{
+ if (prsactx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+ int max_saltlen;
+
+ /* See if minimum salt length exceeds maximum possible */
+ max_saltlen = RSA_size(prsactx->rsa) - EVP_MD_get_size(prsactx->md);
+ if ((RSA_bits(prsactx->rsa) & 0x7) == 1)
+ max_saltlen--;
+ if (min_saltlen < 0 || min_saltlen > max_saltlen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+ return 0;
+ }
+ prsactx->min_saltlen = min_saltlen;
+ }
+ return 1;
+}
+
+static void *rsa_newctx(void *provctx, const char *propq)
+{
+ PROV_RSA_CTX *prsactx = NULL;
+ char *propq_copy = NULL;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX))) == NULL
+ || (propq != NULL
+ && (propq_copy = OPENSSL_strdup(propq)) == NULL)) {
+ OPENSSL_free(prsactx);
+ return NULL;
+ }
+
+ OSSL_FIPS_IND_INIT(prsactx)
+ prsactx->libctx = PROV_LIBCTX_OF(provctx);
+ prsactx->flag_allow_md = 1;
+#ifdef FIPS_MODULE
+ prsactx->verify_message = 1;
+#endif
+ prsactx->propq = propq_copy;
+ /* Maximum up to digest length for sign, auto for verify */
+ prsactx->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
+ prsactx->min_saltlen = -1;
+ return prsactx;
+}
+
+static int rsa_pss_compute_saltlen(PROV_RSA_CTX *ctx)
+{
+ int saltlen = ctx->saltlen;
+ int saltlenMax = -1;
+
+ /* FIPS 186-4 section 5 "The RSA Digital Signature Algorithm", subsection
+ * 5.5 "PKCS #1" says: "For RSASSA-PSS […] the length (in bytes) of the
+ * salt (sLen) shall satisfy 0 <= sLen <= hLen, where hLen is the length of
+ * the hash function output block (in bytes)."
+ *
+ * Provide a way to use at most the digest length, so that the default does
+ * not violate FIPS 186-4. */
+ if (saltlen == RSA_PSS_SALTLEN_DIGEST) {
+ if ((saltlen = EVP_MD_get_size(ctx->md)) <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
+ return -1;
+ }
+ } else if (saltlen == RSA_PSS_SALTLEN_AUTO_DIGEST_MAX) {
+ saltlen = RSA_PSS_SALTLEN_MAX;
+ if ((saltlenMax = EVP_MD_get_size(ctx->md)) <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
+ return -1;
+ }
+ }
+ if (saltlen == RSA_PSS_SALTLEN_MAX || saltlen == RSA_PSS_SALTLEN_AUTO) {
+ int mdsize, rsasize;
+
+ if ((mdsize = EVP_MD_get_size(ctx->md)) <= 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST);
+ return -1;
+ }
+ if ((rsasize = RSA_size(ctx->rsa)) <= 2 || rsasize - 2 < mdsize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY);
+ return -1;
+ }
+ saltlen = rsasize - mdsize - 2;
+ if ((RSA_bits(ctx->rsa) & 0x7) == 1)
+ saltlen--;
+ if (saltlenMax >= 0 && saltlen > saltlenMax)
+ saltlen = saltlenMax;
+ }
+ if (saltlen < 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ return -1;
+ } else if (saltlen < ctx->min_saltlen) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_PSS_SALTLEN_TOO_SMALL,
+ "minimum salt length: %d, actual salt length: %d",
+ ctx->min_saltlen, saltlen);
+ return -1;
+ }
+ return saltlen;
+}
+
+static unsigned char *rsa_generate_signature_aid(PROV_RSA_CTX *ctx,
+ unsigned char *aid_buf,
+ size_t buf_len,
+ size_t *aid_len)
+{
+ WPACKET pkt;
+ unsigned char *aid = NULL;
+ int saltlen;
+ RSA_PSS_PARAMS_30 pss_params;
+ int ret;
+
+ if (!WPACKET_init_der(&pkt, aid_buf, buf_len)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_CRYPTO_LIB);
+ return NULL;
+ }
+
+ switch (ctx->pad_mode) {
+ case RSA_PKCS1_PADDING:
+ ret = ossl_DER_w_algorithmIdentifier_MDWithRSAEncryption(&pkt, -1,
+ ctx->mdnid);
+
+ if (ret > 0) {
+ break;
+ } else if (ret == 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ goto cleanup;
+ }
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_UNSUPPORTED,
+ "Algorithm ID generation - md NID: %d",
+ ctx->mdnid);
+ goto cleanup;
+ case RSA_PKCS1_PSS_PADDING:
+ saltlen = rsa_pss_compute_saltlen(ctx);
+ if (saltlen < 0)
+ goto cleanup;
+ if (!ossl_rsa_pss_params_30_set_defaults(&pss_params)
+ || !ossl_rsa_pss_params_30_set_hashalg(&pss_params, ctx->mdnid)
+ || !ossl_rsa_pss_params_30_set_maskgenhashalg(&pss_params,
+ ctx->mgf1_mdnid)
+ || !ossl_rsa_pss_params_30_set_saltlen(&pss_params, saltlen)
+ || !ossl_DER_w_algorithmIdentifier_RSA_PSS(&pkt, -1,
+ RSA_FLAG_TYPE_RSASSAPSS,
+ &pss_params)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ goto cleanup;
+ }
+ break;
+ default:
+ ERR_raise_data(ERR_LIB_PROV, ERR_R_UNSUPPORTED,
+ "Algorithm ID generation - pad mode: %d",
+ ctx->pad_mode);
+ goto cleanup;
+ }
+ if (WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ cleanup:
+ WPACKET_cleanup(&pkt);
+ return aid;
+}
+
+static int rsa_setup_md(PROV_RSA_CTX *ctx, const char *mdname,
+ const char *mdprops, const char *desc)
+{
+ EVP_MD *md = NULL;
+
+ if (mdprops == NULL)
+ mdprops = ctx->propq;
+
+ if (mdname != NULL) {
+ int md_nid;
+ size_t mdname_len = strlen(mdname);
+
+ md = EVP_MD_fetch(ctx->libctx, mdname, mdprops);
+
+ if (md == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s could not be fetched", mdname);
+ goto err;
+ }
+ md_nid = ossl_digest_rsa_sign_get_md_nid(md);
+ if (md_nid == NID_undef) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest=%s", mdname);
+ goto err;
+ }
+ /*
+ * XOF digests are not allowed except for RSA PSS.
+ * We don't support XOF digests with RSA PSS (yet), so just fail.
+ * When we do support them, uncomment the second clause.
+ */
+ if (EVP_MD_xof(md)
+ /* && ctx->pad_mode != RSA_PKCS1_PSS_PADDING */) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ goto err;
+ }
+#ifdef FIPS_MODULE
+ {
+ int sha1_allowed
+ = ((ctx->operation
+ & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG)) == 0);
+
+ if (!ossl_fips_ind_digest_sign_check(OSSL_FIPS_IND_GET(ctx),
+ OSSL_FIPS_IND_SETTABLE1,
+ ctx->libctx,
+ md_nid, sha1_allowed, 1, desc,
+ ossl_fips_config_signature_digest_check))
+ goto err;
+ }
+#endif
+
+ if (!rsa_check_padding(ctx, mdname, NULL, md_nid))
+ goto err;
+ if (mdname_len >= sizeof(ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s exceeds name buffer length", mdname);
+ goto err;
+ }
+
+ if (!ctx->flag_allow_md) {
+ if (ctx->mdname[0] != '\0' && !EVP_MD_is_a(md, ctx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest %s != %s", mdname, ctx->mdname);
+ goto err;
+ }
+ EVP_MD_free(md);
+ return 1;
+ }
+
+ if (!ctx->mgf1_md_set) {
+ if (!EVP_MD_up_ref(md)) {
+ goto err;
+ }
+ EVP_MD_free(ctx->mgf1_md);
+ ctx->mgf1_md = md;
+ ctx->mgf1_mdnid = md_nid;
+ OPENSSL_strlcpy(ctx->mgf1_mdname, mdname, sizeof(ctx->mgf1_mdname));
+ }
+
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+
+ ctx->mdctx = NULL;
+ ctx->md = md;
+ ctx->mdnid = md_nid;
+ OPENSSL_strlcpy(ctx->mdname, mdname, sizeof(ctx->mdname));
+ }
+
+ return 1;
+err:
+ EVP_MD_free(md);
+ return 0;
+}
+
+static int rsa_setup_mgf1_md(PROV_RSA_CTX *ctx, const char *mdname,
+ const char *mdprops)
+{
+ size_t len;
+ EVP_MD *md = NULL;
+ int mdnid;
+
+ if (mdprops == NULL)
+ mdprops = ctx->propq;
+
+ if ((md = EVP_MD_fetch(ctx->libctx, mdname, mdprops)) == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s could not be fetched", mdname);
+ return 0;
+ }
+ /* The default for mgf1 is SHA1 - so allow SHA1 */
+ if ((mdnid = ossl_digest_rsa_sign_get_md_nid(md)) <= 0
+ || !rsa_check_padding(ctx, NULL, mdname, mdnid)) {
+ if (mdnid <= 0)
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_DIGEST_NOT_ALLOWED,
+ "digest=%s", mdname);
+ EVP_MD_free(md);
+ return 0;
+ }
+ len = OPENSSL_strlcpy(ctx->mgf1_mdname, mdname, sizeof(ctx->mgf1_mdname));
+ if (len >= sizeof(ctx->mgf1_mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "%s exceeds name buffer length", mdname);
+ EVP_MD_free(md);
+ return 0;
+ }
+
+ EVP_MD_free(ctx->mgf1_md);
+ ctx->mgf1_md = md;
+ ctx->mgf1_mdnid = mdnid;
+ ctx->mgf1_md_set = 1;
+ return 1;
+}
+
+static int
+rsa_signverify_init(PROV_RSA_CTX *prsactx, void *vrsa,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ int protect;
+
+ if (!ossl_prov_is_running() || prsactx == NULL)
+ return 0;
+
+ if (vrsa == NULL && prsactx->rsa == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (vrsa != NULL) {
+ if (!RSA_up_ref(vrsa))
+ return 0;
+ RSA_free(prsactx->rsa);
+ prsactx->rsa = vrsa;
+ }
+ if (!ossl_rsa_key_op_get_protect(prsactx->rsa, operation, &protect))
+ return 0;
+
+ prsactx->operation = operation;
+ prsactx->flag_allow_update = 1;
+ prsactx->flag_allow_final = 1;
+ prsactx->flag_allow_oneshot = 1;
+
+ /* Maximize up to digest length for sign, auto for verify */
+ prsactx->saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
+ prsactx->min_saltlen = -1;
+
+ switch (RSA_test_flags(prsactx->rsa, RSA_FLAG_TYPE_MASK)) {
+ case RSA_FLAG_TYPE_RSA:
+ prsactx->pad_mode = RSA_PKCS1_PADDING;
+ break;
+ case RSA_FLAG_TYPE_RSASSAPSS:
+ prsactx->pad_mode = RSA_PKCS1_PSS_PADDING;
+
+ {
+ const RSA_PSS_PARAMS_30 *pss =
+ ossl_rsa_get0_pss_params_30(prsactx->rsa);
+
+ if (!ossl_rsa_pss_params_30_is_unrestricted(pss)) {
+ int md_nid = ossl_rsa_pss_params_30_hashalg(pss);
+ int mgf1md_nid = ossl_rsa_pss_params_30_maskgenhashalg(pss);
+ int min_saltlen = ossl_rsa_pss_params_30_saltlen(pss);
+ const char *mdname, *mgf1mdname;
+ size_t len;
+
+ mdname = ossl_rsa_oaeppss_nid2name(md_nid);
+ mgf1mdname = ossl_rsa_oaeppss_nid2name(mgf1md_nid);
+
+ if (mdname == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "PSS restrictions lack hash algorithm");
+ return 0;
+ }
+ if (mgf1mdname == NULL) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "PSS restrictions lack MGF1 hash algorithm");
+ return 0;
+ }
+
+ len = OPENSSL_strlcpy(prsactx->mdname, mdname,
+ sizeof(prsactx->mdname));
+ if (len >= sizeof(prsactx->mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "hash algorithm name too long");
+ return 0;
+ }
+ len = OPENSSL_strlcpy(prsactx->mgf1_mdname, mgf1mdname,
+ sizeof(prsactx->mgf1_mdname));
+ if (len >= sizeof(prsactx->mgf1_mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "MGF1 hash algorithm name too long");
+ return 0;
+ }
+ prsactx->saltlen = min_saltlen;
+
+ /* call rsa_setup_mgf1_md before rsa_setup_md to avoid duplication */
+ if (!rsa_setup_mgf1_md(prsactx, mgf1mdname, prsactx->propq)
+ || !rsa_setup_md(prsactx, mdname, prsactx->propq, desc)
+ || !rsa_check_parameters(prsactx, min_saltlen))
+ return 0;
+ }
+ }
+
+ break;
+ default:
+ ERR_raise(ERR_LIB_RSA, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+
+ OSSL_FIPS_IND_SET_APPROVED(prsactx)
+ if (!set_ctx_params(prsactx, params))
+ return 0;
+#ifdef FIPS_MODULE
+ if (!ossl_fips_ind_rsa_key_check(OSSL_FIPS_IND_GET(prsactx),
+ OSSL_FIPS_IND_SETTABLE0, prsactx->libctx,
+ prsactx->rsa, desc, protect))
+ return 0;
+#endif
+ return 1;
+}
+
+static int setup_tbuf(PROV_RSA_CTX *ctx)
+{
+ if (ctx->tbuf != NULL)
+ return 1;
+ if ((ctx->tbuf = OPENSSL_malloc(RSA_size(ctx->rsa))) == NULL)
+ return 0;
+ return 1;
+}
+
+static void clean_tbuf(PROV_RSA_CTX *ctx)
+{
+ if (ctx->tbuf != NULL)
+ OPENSSL_cleanse(ctx->tbuf, RSA_size(ctx->rsa));
+}
+
+static void free_tbuf(PROV_RSA_CTX *ctx)
+{
+ clean_tbuf(ctx);
+ OPENSSL_free(ctx->tbuf);
+ ctx->tbuf = NULL;
+}
+
+#ifdef FIPS_MODULE
+static int rsa_pss_saltlen_check_passed(PROV_RSA_CTX *ctx, const char *algoname, int saltlen)
+{
+ int mdsize = rsa_get_md_size(ctx);
+ /*
+ * Perform the check if the salt length is compliant to FIPS 186-5.
+ *
+ * According to FIPS 186-5 5.4 (g), the salt length shall be between zero
+ * and the output block length of the digest function (inclusive).
+ */
+ int approved = (saltlen >= 0 && saltlen <= mdsize);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE3,
+ ctx->libctx,
+ algoname, "PSS Salt Length",
+ ossl_fips_config_rsa_pss_saltlen_check)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+#endif
+
+static int rsa_sign_init(void *vprsactx, void *vrsa, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+#ifdef FIPS_MODULE
+ if (prsactx != NULL)
+ prsactx->verify_message = 1;
+#endif
+
+ return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params,
+ EVP_PKEY_OP_SIGN, "RSA Sign Init");
+}
+
+/*
+ * Sign tbs without digesting it first. This is suitable for "primitive"
+ * signing and signing the digest of a message, i.e. should be used with
+ * implementations of the keytype related algorithms.
+ */
+static int rsa_sign_directly(PROV_RSA_CTX *prsactx,
+ unsigned char *sig, size_t *siglen, size_t sigsize,
+ const unsigned char *tbs, size_t tbslen)
+{
+ int ret;
+ size_t rsasize = RSA_size(prsactx->rsa);
+ size_t mdsize = rsa_get_md_size(prsactx);
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sig == NULL) {
+ *siglen = rsasize;
+ return 1;
+ }
+
+ if (sigsize < rsasize) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SIGNATURE_SIZE,
+ "is %zu, should be at least %zu", sigsize, rsasize);
+ return 0;
+ }
+
+ if (mdsize != 0) {
+ if (tbslen != mdsize) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH);
+ return 0;
+ }
+
+#ifndef FIPS_MODULE
+ if (EVP_MD_is_a(prsactx->md, OSSL_DIGEST_NAME_MDC2)) {
+ unsigned int sltmp;
+
+ if (prsactx->pad_mode != RSA_PKCS1_PADDING) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE,
+ "only PKCS#1 padding supported with MDC2");
+ return 0;
+ }
+ ret = RSA_sign_ASN1_OCTET_STRING(0, tbs, tbslen, sig, &sltmp,
+ prsactx->rsa);
+
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ ret = sltmp;
+ goto end;
+ }
+#endif
+ switch (prsactx->pad_mode) {
+ case RSA_X931_PADDING:
+ if ((size_t)RSA_size(prsactx->rsa) < tbslen + 1) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_KEY_SIZE_TOO_SMALL,
+ "RSA key size = %d, expected minimum = %d",
+ RSA_size(prsactx->rsa), tbslen + 1);
+ return 0;
+ }
+ if (!setup_tbuf(prsactx)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);
+ return 0;
+ }
+ memcpy(prsactx->tbuf, tbs, tbslen);
+ prsactx->tbuf[tbslen] = RSA_X931_hash_id(prsactx->mdnid);
+ ret = RSA_private_encrypt(tbslen + 1, prsactx->tbuf,
+ sig, prsactx->rsa, RSA_X931_PADDING);
+ clean_tbuf(prsactx);
+ break;
+ case RSA_PKCS1_PADDING:
+ {
+ unsigned int sltmp;
+
+ ret = RSA_sign(prsactx->mdnid, tbs, tbslen, sig, &sltmp,
+ prsactx->rsa);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ ret = sltmp;
+ }
+ break;
+
+ case RSA_PKCS1_PSS_PADDING:
+ {
+ int saltlen;
+
+ /* Check PSS restrictions */
+ if (rsa_pss_restricted(prsactx)) {
+ switch (prsactx->saltlen) {
+ case RSA_PSS_SALTLEN_DIGEST:
+ if (prsactx->min_saltlen > EVP_MD_get_size(prsactx->md)) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_PSS_SALTLEN_TOO_SMALL,
+ "minimum salt length set to %d, "
+ "but the digest only gives %d",
+ prsactx->min_saltlen,
+ EVP_MD_get_size(prsactx->md));
+ return 0;
+ }
+ /* FALLTHRU */
+ default:
+ if (prsactx->saltlen >= 0
+ && prsactx->saltlen < prsactx->min_saltlen) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_PSS_SALTLEN_TOO_SMALL,
+ "minimum salt length set to %d, but the"
+ "actual salt length is only set to %d",
+ prsactx->min_saltlen,
+ prsactx->saltlen);
+ return 0;
+ }
+ break;
+ }
+ }
+ if (!setup_tbuf(prsactx))
+ return 0;
+ saltlen = prsactx->saltlen;
+ if (!ossl_rsa_padding_add_PKCS1_PSS_mgf1(prsactx->rsa,
+ prsactx->tbuf, tbs,
+ prsactx->md, prsactx->mgf1_md,
+ &saltlen)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ if (!rsa_pss_saltlen_check_passed(prsactx, "RSA Sign", saltlen))
+ return 0;
+#endif
+ ret = RSA_private_encrypt(RSA_size(prsactx->rsa), prsactx->tbuf,
+ sig, prsactx->rsa, RSA_NO_PADDING);
+ clean_tbuf(prsactx);
+ }
+ break;
+
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE,
+ "Only X.931, PKCS#1 v1.5 or PSS padding allowed");
+ return 0;
+ }
+ } else {
+ ret = RSA_private_encrypt(tbslen, tbs, sig, prsactx->rsa,
+ prsactx->pad_mode);
+ }
+
+#ifndef FIPS_MODULE
+ end:
+#endif
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+
+ *siglen = ret;
+ return 1;
+}
+
+static int rsa_signverify_message_update(void *vprsactx,
+ const unsigned char *data,
+ size_t datalen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx == NULL || prsactx->mdctx == NULL)
+ return 0;
+
+ if (!prsactx->flag_allow_update) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_UPDATE_CALL_OUT_OF_ORDER);
+ return 0;
+ }
+ prsactx->flag_allow_oneshot = 0;
+
+ return EVP_DigestUpdate(prsactx->mdctx, data, datalen);
+}
+
+static int rsa_sign_message_final(void *vprsactx, unsigned char *sig,
+ size_t *siglen, size_t sigsize)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running() || prsactx == NULL)
+ return 0;
+ if (prsactx->mdctx == NULL)
+ return 0;
+ if (!prsactx->flag_allow_final) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER);
+ return 0;
+ }
+
+ /*
+ * If sig is NULL then we're just finding out the sig size. Other fields
+ * are ignored. Defer to rsa_sign.
+ */
+ if (sig != NULL) {
+ /*
+ * The digests used here are all known (see rsa_get_md_nid()), so they
+ * should not exceed the internal buffer size of EVP_MAX_MD_SIZE.
+ */
+ if (!EVP_DigestFinal_ex(prsactx->mdctx, digest, &dlen))
+ return 0;
+
+ prsactx->flag_allow_update = 0;
+ prsactx->flag_allow_oneshot = 0;
+ prsactx->flag_allow_final = 0;
+ }
+
+ return rsa_sign_directly(prsactx, sig, siglen, sigsize, digest, dlen);
+}
+
+/*
+ * If signing a message, digest tbs and sign the result.
+ * Otherwise, sign tbs directly.
+ */
+static int rsa_sign(void *vprsactx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *tbs, size_t tbslen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (!ossl_prov_is_running() || prsactx == NULL)
+ return 0;
+ if (!prsactx->flag_allow_oneshot) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER);
+ return 0;
+ }
+
+ if (prsactx->operation == EVP_PKEY_OP_SIGNMSG) {
+ /*
+ * If |sig| is NULL, the caller is only looking for the sig length.
+ * DO NOT update the input in this case.
+ */
+ if (sig == NULL)
+ return rsa_sign_message_final(prsactx, sig, siglen, sigsize);
+
+ return rsa_signverify_message_update(prsactx, tbs, tbslen)
+ && rsa_sign_message_final(prsactx, sig, siglen, sigsize);
+ }
+ return rsa_sign_directly(prsactx, sig, siglen, sigsize, tbs, tbslen);
+}
+
+static int rsa_verify_recover_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+#ifdef FIPS_MODULE
+ if (prsactx != NULL)
+ prsactx->verify_message = 0;
+#endif
+
+ return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params,
+ EVP_PKEY_OP_VERIFYRECOVER, "RSA VerifyRecover Init");
+}
+
+/*
+ * There is no message variant of verify recover, so no need for
+ * 'rsa_verify_recover_directly', just use this function, er, directly.
+ */
+static int rsa_verify_recover(void *vprsactx,
+ unsigned char *rout, size_t *routlen,
+ size_t routsize,
+ const unsigned char *sig, size_t siglen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int ret;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (rout == NULL) {
+ *routlen = RSA_size(prsactx->rsa);
+ return 1;
+ }
+
+ if (prsactx->md != NULL) {
+ switch (prsactx->pad_mode) {
+ case RSA_X931_PADDING:
+ if (!setup_tbuf(prsactx))
+ return 0;
+ ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf, prsactx->rsa,
+ RSA_X931_PADDING);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ ret--;
+ if (prsactx->tbuf[ret] != RSA_X931_hash_id(prsactx->mdnid)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ALGORITHM_MISMATCH);
+ return 0;
+ }
+ if (ret != EVP_MD_get_size(prsactx->md)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH,
+ "Should be %d, but got %d",
+ EVP_MD_get_size(prsactx->md), ret);
+ return 0;
+ }
+
+ *routlen = ret;
+ if (rout != prsactx->tbuf) {
+ if (routsize < (size_t)ret) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL,
+ "buffer size is %d, should be %d",
+ routsize, ret);
+ return 0;
+ }
+ memcpy(rout, prsactx->tbuf, ret);
+ }
+ break;
+
+ case RSA_PKCS1_PADDING:
+ {
+ size_t sltmp;
+
+ ret = ossl_rsa_verify(prsactx->mdnid, NULL, 0, rout, &sltmp,
+ sig, siglen, prsactx->rsa);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ ret = sltmp;
+ }
+ break;
+
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE,
+ "Only X.931 or PKCS#1 v1.5 padding allowed");
+ return 0;
+ }
+ } else {
+ ret = RSA_public_decrypt(siglen, sig, rout, prsactx->rsa,
+ prsactx->pad_mode);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ }
+ *routlen = ret;
+ return 1;
+}
+
+static int rsa_verify_init(void *vprsactx, void *vrsa,
+ const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+#ifdef FIPS_MODULE
+ if (prsactx != NULL)
+ prsactx->verify_message = 0;
+#endif
+
+ return rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params,
+ EVP_PKEY_OP_VERIFY, "RSA Verify Init");
+}
+
+static int rsa_verify_directly(PROV_RSA_CTX *prsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ size_t rslen;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ if (prsactx->md != NULL) {
+ switch (prsactx->pad_mode) {
+ case RSA_PKCS1_PADDING:
+ if (!RSA_verify(prsactx->mdnid, tbs, tbslen, sig, siglen,
+ prsactx->rsa)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ return 1;
+ case RSA_X931_PADDING:
+ if (!setup_tbuf(prsactx))
+ return 0;
+ if (rsa_verify_recover(prsactx, prsactx->tbuf, &rslen, 0,
+ sig, siglen) <= 0)
+ return 0;
+ break;
+ case RSA_PKCS1_PSS_PADDING:
+ {
+ int ret;
+ int saltlen;
+ size_t mdsize;
+
+ /*
+ * We need to check this for the RSA_verify_PKCS1_PSS_mgf1()
+ * call
+ */
+ mdsize = rsa_get_md_size(prsactx);
+ if (tbslen != mdsize) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST_LENGTH,
+ "Should be %d, but got %d",
+ mdsize, tbslen);
+ return 0;
+ }
+
+ if (!setup_tbuf(prsactx))
+ return 0;
+ ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf,
+ prsactx->rsa, RSA_NO_PADDING);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ saltlen = prsactx->saltlen;
+ ret = ossl_rsa_verify_PKCS1_PSS_mgf1(prsactx->rsa, tbs,
+ prsactx->md, prsactx->mgf1_md,
+ prsactx->tbuf,
+ &saltlen);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+#ifdef FIPS_MODULE
+ if (!rsa_pss_saltlen_check_passed(prsactx, "RSA Verify", saltlen))
+ return 0;
+#endif
+ return 1;
+ }
+ default:
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_PADDING_MODE,
+ "Only X.931, PKCS#1 v1.5 or PSS padding allowed");
+ return 0;
+ }
+ } else {
+ int ret;
+
+ if (!setup_tbuf(prsactx))
+ return 0;
+ ret = RSA_public_decrypt(siglen, sig, prsactx->tbuf, prsactx->rsa,
+ prsactx->pad_mode);
+ if (ret <= 0) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_RSA_LIB);
+ return 0;
+ }
+ rslen = (size_t)ret;
+ }
+
+ if ((rslen != tbslen) || memcmp(tbs, prsactx->tbuf, rslen))
+ return 0;
+
+ return 1;
+}
+
+static int rsa_verify_set_sig(void *vprsactx,
+ const unsigned char *sig, size_t siglen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ OSSL_PARAM params[2];
+
+ params[0] =
+ OSSL_PARAM_construct_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE,
+ (unsigned char *)sig, siglen);
+ params[1] = OSSL_PARAM_construct_end();
+ return rsa_sigalg_set_ctx_params(prsactx, params);
+}
+
+static int rsa_verify_message_final(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (!ossl_prov_is_running() || prsactx == NULL)
+ return 0;
+ if (prsactx->mdctx == NULL)
+ return 0;
+ if (!prsactx->flag_allow_final) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FINAL_CALL_OUT_OF_ORDER);
+ return 0;
+ }
+
+ /*
+ * The digests used here are all known (see rsa_get_md_nid()), so they
+ * should not exceed the internal buffer size of EVP_MAX_MD_SIZE.
+ */
+ if (!EVP_DigestFinal_ex(prsactx->mdctx, digest, &dlen))
+ return 0;
+
+ prsactx->flag_allow_update = 0;
+ prsactx->flag_allow_final = 0;
+ prsactx->flag_allow_oneshot = 0;
+
+ return rsa_verify_directly(prsactx, prsactx->sig, prsactx->siglen,
+ digest, dlen);
+}
+
+/*
+ * If verifying a message, digest tbs and verify the result.
+ * Otherwise, verify tbs directly.
+ */
+static int rsa_verify(void *vprsactx,
+ const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (!ossl_prov_is_running() || prsactx == NULL)
+ return 0;
+ if (!prsactx->flag_allow_oneshot) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_ONESHOT_CALL_OUT_OF_ORDER);
+ return 0;
+ }
+
+ if (prsactx->operation == EVP_PKEY_OP_VERIFYMSG)
+ return rsa_verify_set_sig(prsactx, sig, siglen)
+ && rsa_signverify_message_update(prsactx, tbs, tbslen)
+ && rsa_verify_message_final(prsactx);
+ return rsa_verify_directly(prsactx, sig, siglen, tbs, tbslen);
+}
+
+/* DigestSign/DigestVerify wrappers */
+
+static int rsa_digest_signverify_init(void *vprsactx, const char *mdname,
+ void *vrsa, const OSSL_PARAM params[],
+ int operation, const char *desc)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+#ifdef FIPS_MODULE
+ if (prsactx != NULL)
+ prsactx->verify_message = 1;
+#endif
+
+ if (!rsa_signverify_init(prsactx, vrsa, rsa_set_ctx_params, params,
+ operation, desc))
+ return 0;
+
+ if (mdname != NULL
+ /* was rsa_setup_md already called in rsa_signverify_init()? */
+ && (mdname[0] == '\0' || OPENSSL_strcasecmp(prsactx->mdname, mdname) != 0)
+ && !rsa_setup_md(prsactx, mdname, prsactx->propq, desc))
+ return 0;
+
+ prsactx->flag_allow_md = 0;
+
+ if (prsactx->mdctx == NULL) {
+ prsactx->mdctx = EVP_MD_CTX_new();
+ if (prsactx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(prsactx->mdctx, prsactx->md, params))
+ goto error;
+
+ return 1;
+
+ error:
+ EVP_MD_CTX_free(prsactx->mdctx);
+ prsactx->mdctx = NULL;
+ return 0;
+}
+
+static int rsa_digest_sign_init(void *vprsactx, const char *mdname,
+ void *vrsa, const OSSL_PARAM params[])
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return rsa_digest_signverify_init(vprsactx, mdname, vrsa,
+ params, EVP_PKEY_OP_SIGNMSG,
+ "RSA Digest Sign Init");
+}
+
+static int rsa_digest_sign_update(void *vprsactx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (prsactx->flag_sigalg)
+ return 0;
+
+ return rsa_signverify_message_update(prsactx, data, datalen);
+}
+
+static int rsa_digest_sign_final(void *vprsactx, unsigned char *sig,
+ size_t *siglen, size_t sigsize)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int ok = 0;
+
+ if (prsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (prsactx->flag_sigalg)
+ return 0;
+
+ if (rsa_sign_message_final(prsactx, sig, siglen, sigsize))
+ ok = 1;
+
+ prsactx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static int rsa_digest_verify_init(void *vprsactx, const char *mdname,
+ void *vrsa, const OSSL_PARAM params[])
+{
+ if (!ossl_prov_is_running())
+ return 0;
+ return rsa_digest_signverify_init(vprsactx, mdname, vrsa,
+ params, EVP_PKEY_OP_VERIFYMSG,
+ "RSA Digest Verify Init");
+}
+
+static int rsa_digest_verify_update(void *vprsactx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_sign */
+ if (prsactx->flag_sigalg)
+ return 0;
+
+ return rsa_signverify_message_update(prsactx, data, datalen);
+}
+
+int rsa_digest_verify_final(void *vprsactx, const unsigned char *sig,
+ size_t siglen)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ int ok = 0;
+
+ if (prsactx == NULL)
+ return 0;
+ /* Sigalg implementations shouldn't do digest_verify */
+ if (prsactx->flag_sigalg)
+ return 0;
+
+ if (rsa_verify_set_sig(prsactx, sig, siglen)
+ && rsa_verify_message_final(vprsactx))
+ ok = 1;
+
+ prsactx->flag_allow_md = 1;
+
+ return ok;
+}
+
+static void rsa_freectx(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx == NULL)
+ return;
+
+ EVP_MD_CTX_free(prsactx->mdctx);
+ EVP_MD_free(prsactx->md);
+ EVP_MD_free(prsactx->mgf1_md);
+ OPENSSL_free(prsactx->sig);
+ OPENSSL_free(prsactx->propq);
+ free_tbuf(prsactx);
+ RSA_free(prsactx->rsa);
+
+ OPENSSL_clear_free(prsactx, sizeof(*prsactx));
+}
+
+static void *rsa_dupctx(void *vprsactx)
+{
+ PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx;
+ PROV_RSA_CTX *dstctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->rsa = NULL;
+ dstctx->md = NULL;
+ dstctx->mgf1_md = NULL;
+ dstctx->mdctx = NULL;
+ dstctx->tbuf = NULL;
+ dstctx->propq = NULL;
+
+ if (srcctx->rsa != NULL && !RSA_up_ref(srcctx->rsa))
+ goto err;
+ dstctx->rsa = srcctx->rsa;
+
+ if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md))
+ goto err;
+ dstctx->md = srcctx->md;
+
+ if (srcctx->mgf1_md != NULL && !EVP_MD_up_ref(srcctx->mgf1_md))
+ goto err;
+ dstctx->mgf1_md = srcctx->mgf1_md;
+
+ if (srcctx->mdctx != NULL) {
+ dstctx->mdctx = EVP_MD_CTX_new();
+ if (dstctx->mdctx == NULL
+ || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx))
+ goto err;
+ }
+
+ if (srcctx->propq != NULL) {
+ dstctx->propq = OPENSSL_strdup(srcctx->propq);
+ if (dstctx->propq == NULL)
+ goto err;
+ }
+
+ return dstctx;
+ err:
+ rsa_freectx(dstctx);
+ return NULL;
+}
+
+static int rsa_get_ctx_params(void *vprsactx, OSSL_PARAM *params)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ OSSL_PARAM *p;
+
+ if (prsactx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL) {
+ /* The Algorithm Identifier of the combined signature algorithm */
+ unsigned char aid_buf[128];
+ unsigned char *aid;
+ size_t aid_len;
+
+ aid = rsa_generate_signature_aid(prsactx, aid_buf,
+ sizeof(aid_buf), &aid_len);
+ if (aid == NULL || !OSSL_PARAM_set_octet_string(p, aid, aid_len))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PAD_MODE);
+ if (p != NULL)
+ switch (p->data_type) {
+ case OSSL_PARAM_INTEGER:
+ if (!OSSL_PARAM_set_int(p, prsactx->pad_mode))
+ return 0;
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ {
+ int i;
+ const char *word = NULL;
+
+ for (i = 0; padding_item[i].id != 0; i++) {
+ if (prsactx->pad_mode == (int)padding_item[i].id) {
+ word = padding_item[i].ptr;
+ break;
+ }
+ }
+
+ if (word != NULL) {
+ if (!OSSL_PARAM_set_utf8_string(p, word))
+ return 0;
+ } else {
+ ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->mdname))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_MGF1_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, prsactx->mgf1_mdname))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+ if (p != NULL) {
+ if (p->data_type == OSSL_PARAM_INTEGER) {
+ if (!OSSL_PARAM_set_int(p, prsactx->saltlen))
+ return 0;
+ } else if (p->data_type == OSSL_PARAM_UTF8_STRING) {
+ const char *value = NULL;
+
+ switch (prsactx->saltlen) {
+ case RSA_PSS_SALTLEN_DIGEST:
+ value = OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST;
+ break;
+ case RSA_PSS_SALTLEN_MAX:
+ value = OSSL_PKEY_RSA_PSS_SALT_LEN_MAX;
+ break;
+ case RSA_PSS_SALTLEN_AUTO:
+ value = OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO;
+ break;
+ case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX:
+ value = OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX;
+ break;
+ default:
+ {
+ int len = BIO_snprintf(p->data, p->data_size, "%d",
+ prsactx->saltlen);
+
+ if (len <= 0)
+ return 0;
+ p->return_size = len;
+ break;
+ }
+ }
+ if (value != NULL
+ && !OSSL_PARAM_set_utf8_string(p, value))
+ return 0;
+ }
+ }
+
+#ifdef FIPS_MODULE
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE);
+ if (p != NULL && !OSSL_PARAM_set_uint(p, prsactx->verify_message))
+ return 0;
+#endif
+
+ if (!OSSL_FIPS_IND_GET_CTX_PARAM(prsactx, params))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+#ifdef FIPS_MODULE
+ OSSL_PARAM_uint(OSSL_SIGNATURE_PARAM_FIPS_VERIFY_MESSAGE, NULL),
+#endif
+ OSSL_FIPS_IND_GETTABLE_CTX_PARAM()
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_gettable_ctx_params(ossl_unused void *vprsactx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+#ifdef FIPS_MODULE
+static int rsa_x931_padding_allowed(PROV_RSA_CTX *ctx)
+{
+ int approved = ((ctx->operation & EVP_PKEY_OP_SIGN) == 0);
+
+ if (!approved) {
+ if (!OSSL_FIPS_IND_ON_UNAPPROVED(ctx, OSSL_FIPS_IND_SETTABLE2,
+ ctx->libctx,
+ "RSA Sign set ctx", "X931 Padding",
+ ossl_fips_config_rsa_sign_x931_disallowed)) {
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif
+
+static int rsa_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ const OSSL_PARAM *p;
+ int pad_mode;
+ int saltlen;
+ char mdname[OSSL_MAX_NAME_SIZE] = "", *pmdname = NULL;
+ char mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmdprops = NULL;
+ char mgf1mdname[OSSL_MAX_NAME_SIZE] = "", *pmgf1mdname = NULL;
+ char mgf1mdprops[OSSL_MAX_PROPQUERY_SIZE] = "", *pmgf1mdprops = NULL;
+
+ if (prsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE0, params,
+ OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK))
+ return 0;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE1, params,
+ OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK))
+ return 0;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE2, params,
+ OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK))
+ return 0;
+
+ if (!OSSL_FIPS_IND_SET_CTX_PARAM(prsactx, OSSL_FIPS_IND_SETTABLE3, params,
+ OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK))
+ return 0;
+
+ pad_mode = prsactx->pad_mode;
+ saltlen = prsactx->saltlen;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL) {
+ const OSSL_PARAM *propsp =
+ OSSL_PARAM_locate_const(params,
+ OSSL_SIGNATURE_PARAM_PROPERTIES);
+
+ pmdname = mdname;
+ if (!OSSL_PARAM_get_utf8_string(p, &pmdname, sizeof(mdname)))
+ return 0;
+
+ if (propsp != NULL) {
+ pmdprops = mdprops;
+ if (!OSSL_PARAM_get_utf8_string(propsp,
+ &pmdprops, sizeof(mdprops)))
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PAD_MODE);
+ if (p != NULL) {
+ const char *err_extra_text = NULL;
+
+ switch (p->data_type) {
+ case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */
+ if (!OSSL_PARAM_get_int(p, &pad_mode))
+ return 0;
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ {
+ int i;
+
+ if (p->data == NULL)
+ return 0;
+
+ for (i = 0; padding_item[i].id != 0; i++) {
+ if (strcmp(p->data, padding_item[i].ptr) == 0) {
+ pad_mode = padding_item[i].id;
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return 0;
+ }
+
+ switch (pad_mode) {
+ case RSA_PKCS1_OAEP_PADDING:
+ /*
+ * OAEP padding is for asymmetric cipher only so is not compatible
+ * with signature use.
+ */
+ err_extra_text = "OAEP padding not allowed for signing / verifying";
+ goto bad_pad;
+ case RSA_PKCS1_PSS_PADDING:
+ if ((prsactx->operation
+ & (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_SIGNMSG
+ | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYMSG)) == 0) {
+ err_extra_text =
+ "PSS padding only allowed for sign and verify operations";
+ goto bad_pad;
+ }
+ break;
+ case RSA_PKCS1_PADDING:
+ err_extra_text = "PKCS#1 padding not allowed with RSA-PSS";
+ goto cont;
+ case RSA_NO_PADDING:
+ err_extra_text = "No padding not allowed with RSA-PSS";
+ goto cont;
+ case RSA_X931_PADDING:
+#ifdef FIPS_MODULE
+ /* X9.31 only allows sizes of 1024 + 256 * s (bits) */
+ if ((RSA_bits(prsactx->rsa) & 0xFF) != 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+ /* RSA Signing with X9.31 padding is not allowed in FIPS 140-3 */
+ if (!rsa_x931_padding_allowed(prsactx))
+ return 0;
+#endif
+ err_extra_text = "X.931 padding not allowed with RSA-PSS";
+ cont:
+ if (RSA_test_flags(prsactx->rsa,
+ RSA_FLAG_TYPE_MASK) == RSA_FLAG_TYPE_RSA)
+ break;
+ /* FALLTHRU */
+ default:
+ bad_pad:
+ if (err_extra_text == NULL)
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE);
+ else
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE,
+ err_extra_text);
+ return 0;
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_PSS_SALTLEN);
+ if (p != NULL) {
+ if (pad_mode != RSA_PKCS1_PSS_PADDING) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_NOT_SUPPORTED,
+ "PSS saltlen can only be specified if "
+ "PSS padding has been specified first");
+ return 0;
+ }
+
+ switch (p->data_type) {
+ case OSSL_PARAM_INTEGER: /* Support for legacy pad mode number */
+ if (!OSSL_PARAM_get_int(p, &saltlen))
+ return 0;
+ break;
+ case OSSL_PARAM_UTF8_STRING:
+ if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_DIGEST) == 0)
+ saltlen = RSA_PSS_SALTLEN_DIGEST;
+ else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_MAX) == 0)
+ saltlen = RSA_PSS_SALTLEN_MAX;
+ else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO) == 0)
+ saltlen = RSA_PSS_SALTLEN_AUTO;
+ else if (strcmp(p->data, OSSL_PKEY_RSA_PSS_SALT_LEN_AUTO_DIGEST_MAX) == 0)
+ saltlen = RSA_PSS_SALTLEN_AUTO_DIGEST_MAX;
+ else
+ saltlen = atoi(p->data);
+ break;
+ default:
+ return 0;
+ }
+
+ /*
+ * RSA_PSS_SALTLEN_AUTO_DIGEST_MAX seems curiously named in this check.
+ * Contrary to what it's name suggests, it's the currently lowest
+ * saltlen number possible.
+ */
+ if (saltlen < RSA_PSS_SALTLEN_AUTO_DIGEST_MAX) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH);
+ return 0;
+ }
+
+ if (rsa_pss_restricted(prsactx)) {
+ switch (saltlen) {
+ case RSA_PSS_SALTLEN_AUTO:
+ case RSA_PSS_SALTLEN_AUTO_DIGEST_MAX:
+ if ((prsactx->operation
+ & (EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYMSG)) == 0) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_SALT_LENGTH,
+ "Cannot use autodetected salt length");
+ return 0;
+ }
+ break;
+ case RSA_PSS_SALTLEN_DIGEST:
+ if (prsactx->min_saltlen > EVP_MD_get_size(prsactx->md)) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_PSS_SALTLEN_TOO_SMALL,
+ "Should be more than %d, but would be "
+ "set to match digest size (%d)",
+ prsactx->min_saltlen,
+ EVP_MD_get_size(prsactx->md));
+ return 0;
+ }
+ break;
+ default:
+ if (saltlen >= 0 && saltlen < prsactx->min_saltlen) {
+ ERR_raise_data(ERR_LIB_PROV,
+ PROV_R_PSS_SALTLEN_TOO_SMALL,
+ "Should be more than %d, "
+ "but would be set to %d",
+ prsactx->min_saltlen, saltlen);
+ return 0;
+ }
+ }
+ }
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MGF1_DIGEST);
+ if (p != NULL) {
+ const OSSL_PARAM *propsp =
+ OSSL_PARAM_locate_const(params,
+ OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES);
+
+ pmgf1mdname = mgf1mdname;
+ if (!OSSL_PARAM_get_utf8_string(p, &pmgf1mdname, sizeof(mgf1mdname)))
+ return 0;
+
+ if (propsp != NULL) {
+ pmgf1mdprops = mgf1mdprops;
+ if (!OSSL_PARAM_get_utf8_string(propsp,
+ &pmgf1mdprops, sizeof(mgf1mdprops)))
+ return 0;
+ }
+
+ if (pad_mode != RSA_PKCS1_PSS_PADDING) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_MGF1_MD);
+ return 0;
+ }
+ }
+
+ prsactx->saltlen = saltlen;
+ prsactx->pad_mode = pad_mode;
+
+ if (prsactx->md == NULL && pmdname == NULL
+ && pad_mode == RSA_PKCS1_PSS_PADDING)
+ pmdname = RSA_DEFAULT_DIGEST_NAME;
+
+ if (pmgf1mdname != NULL
+ && !rsa_setup_mgf1_md(prsactx, pmgf1mdname, pmgf1mdprops))
+ return 0;
+
+ if (pmdname != NULL) {
+ if (!rsa_setup_md(prsactx, pmdname, pmdprops, "RSA Sign Set Ctx"))
+ return 0;
+ } else {
+ if (!rsa_check_padding(prsactx, NULL, NULL, prsactx->mdnid))
+ return 0;
+ }
+ return 1;
+}
+
+static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK)
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM settable_ctx_params_no_digest[] = {
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_PROPERTIES, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, NULL, 0),
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_KEY_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_DIGEST_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_RSA_PSS_SALTLEN_CHECK)
+ OSSL_FIPS_IND_SETTABLE_CTX_PARAM(OSSL_SIGNATURE_PARAM_FIPS_SIGN_X931_PAD_CHECK)
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_settable_ctx_params(void *vprsactx,
+ ossl_unused void *provctx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx != NULL && !prsactx->flag_allow_md)
+ return settable_ctx_params_no_digest;
+ return settable_ctx_params;
+}
+
+static int rsa_get_ctx_md_params(void *vprsactx, OSSL_PARAM *params)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_get_params(prsactx->mdctx, params);
+}
+
+static const OSSL_PARAM *rsa_gettable_ctx_md_params(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx->md == NULL)
+ return 0;
+
+ return EVP_MD_gettable_ctx_params(prsactx->md);
+}
+
+static int rsa_set_ctx_md_params(void *vprsactx, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_set_params(prsactx->mdctx, params);
+}
+
+static const OSSL_PARAM *rsa_settable_ctx_md_params(void *vprsactx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx->md == NULL)
+ return 0;
+
+ return EVP_MD_settable_ctx_params(prsactx->md);
+}
+
+const OSSL_DISPATCH ossl_rsa_signature_functions[] = {
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))rsa_newctx },
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))rsa_sign_init },
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))rsa_sign },
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))rsa_verify_init },
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))rsa_verify },
+ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT,
+ (void (*)(void))rsa_verify_recover_init },
+ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER,
+ (void (*)(void))rsa_verify_recover },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+ (void (*)(void))rsa_digest_sign_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+ (void (*)(void))rsa_digest_sign_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+ (void (*)(void))rsa_digest_sign_final },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+ (void (*)(void))rsa_digest_verify_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+ (void (*)(void))rsa_digest_verify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+ (void (*)(void))rsa_digest_verify_final },
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))rsa_freectx },
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))rsa_dupctx },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))rsa_get_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+ (void (*)(void))rsa_gettable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))rsa_set_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+ (void (*)(void))rsa_settable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+ (void (*)(void))rsa_get_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))rsa_gettable_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+ (void (*)(void))rsa_set_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))rsa_settable_ctx_md_params },
+ OSSL_DISPATCH_END
+};
+
+/* ------------------------------------------------------------------ */
+
+/*
+ * So called sigalgs (composite RSA+hash) implemented below. They
+ * are pretty much hard coded, and rely on the hash implementation
+ * being available as per what OPENSSL_NO_ macros allow.
+ */
+
+static OSSL_FUNC_signature_query_key_types_fn rsa_sigalg_query_key_types;
+static OSSL_FUNC_signature_settable_ctx_params_fn rsa_sigalg_settable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn rsa_sigalg_set_ctx_params;
+
+/*
+ * rsa_sigalg_signverify_init() is almost like rsa_digest_signverify_init(),
+ * just doesn't allow fetching an MD from whatever the user chooses.
+ */
+static int rsa_sigalg_signverify_init(void *vprsactx, void *vrsa,
+ OSSL_FUNC_signature_set_ctx_params_fn *set_ctx_params,
+ const OSSL_PARAM params[],
+ const char *mdname,
+ int operation, int pad_mode,
+ const char *desc)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (!rsa_signverify_init(prsactx, vrsa, set_ctx_params, params, operation,
+ desc))
+ return 0;
+
+ /* PSS is currently not supported as a sigalg */
+ if (prsactx->pad_mode == RSA_PKCS1_PSS_PADDING) {
+ ERR_raise(ERR_LIB_RSA, PROV_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+ return 0;
+ }
+
+ if (!rsa_setup_md(prsactx, mdname, NULL, desc))
+ return 0;
+
+ prsactx->pad_mode = pad_mode;
+ prsactx->flag_sigalg = 1;
+ prsactx->flag_allow_md = 0;
+
+ if (prsactx->mdctx == NULL) {
+ prsactx->mdctx = EVP_MD_CTX_new();
+ if (prsactx->mdctx == NULL)
+ goto error;
+ }
+
+ if (!EVP_DigestInit_ex2(prsactx->mdctx, prsactx->md, params))
+ goto error;
+
+ return 1;
+
+ error:
+ EVP_MD_CTX_free(prsactx->mdctx);
+ prsactx->mdctx = NULL;
+ return 0;
+}
+
+static const char **rsa_sigalg_query_key_types(void)
+{
+ static const char *keytypes[] = { "RSA", NULL };
+
+ return keytypes;
+}
+
+static const OSSL_PARAM settable_sigalg_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_SIGNATURE, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *rsa_sigalg_settable_ctx_params(void *vprsactx,
+ ossl_unused void *provctx)
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+
+ if (prsactx != NULL && prsactx->operation == EVP_PKEY_OP_VERIFYMSG)
+ return settable_sigalg_ctx_params;
+ return NULL;
+}
+
+static int rsa_sigalg_set_ctx_params(void *vprsactx, const OSSL_PARAM params[])
+{
+ PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx;
+ const OSSL_PARAM *p;
+
+ if (prsactx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (prsactx->operation == EVP_PKEY_OP_VERIFYMSG) {
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_SIGNATURE);
+ if (p != NULL) {
+ OPENSSL_free(prsactx->sig);
+ prsactx->sig = NULL;
+ prsactx->siglen = 0;
+ if (!OSSL_PARAM_get_octet_string(p, (void **)&prsactx->sig,
+ 0, &prsactx->siglen))
+ return 0;
+ }
+ }
+ return 1;
+}
+
+#define IMPL_RSA_SIGALG(md, MD) \
+ static OSSL_FUNC_signature_sign_init_fn rsa_##md##_sign_init; \
+ static OSSL_FUNC_signature_sign_message_init_fn \
+ rsa_##md##_sign_message_init; \
+ static OSSL_FUNC_signature_verify_init_fn rsa_##md##_verify_init; \
+ static OSSL_FUNC_signature_verify_message_init_fn \
+ rsa_##md##_verify_message_init; \
+ \
+ static int \
+ rsa_##md##_sign_init(void *vprsactx, void *vrsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "RSA Sigalg Sign Init"; \
+ \
+ return rsa_sigalg_signverify_init(vprsactx, vrsa, \
+ rsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGN, \
+ RSA_PKCS1_PADDING, \
+ desc); \
+ } \
+ \
+ static int \
+ rsa_##md##_sign_message_init(void *vprsactx, void *vrsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "RSA Sigalg Sign Message Init"; \
+ \
+ return rsa_sigalg_signverify_init(vprsactx, vrsa, \
+ rsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_SIGNMSG, \
+ RSA_PKCS1_PADDING, \
+ desc); \
+ } \
+ \
+ static int \
+ rsa_##md##_verify_init(void *vprsactx, void *vrsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "RSA Sigalg Verify Init"; \
+ \
+ return rsa_sigalg_signverify_init(vprsactx, vrsa, \
+ rsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFY, \
+ RSA_PKCS1_PADDING, \
+ desc); \
+ } \
+ \
+ static int \
+ rsa_##md##_verify_recover_init(void *vprsactx, void *vrsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "RSA Sigalg Verify Recover Init"; \
+ \
+ return rsa_sigalg_signverify_init(vprsactx, vrsa, \
+ rsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFYRECOVER, \
+ RSA_PKCS1_PADDING, \
+ desc); \
+ } \
+ \
+ static int \
+ rsa_##md##_verify_message_init(void *vprsactx, void *vrsa, \
+ const OSSL_PARAM params[]) \
+ { \
+ static const char desc[] = "RSA Sigalg Verify Message Init"; \
+ \
+ return rsa_sigalg_signverify_init(vprsactx, vrsa, \
+ rsa_sigalg_set_ctx_params, \
+ params, #MD, \
+ EVP_PKEY_OP_VERIFYMSG, \
+ RSA_PKCS1_PADDING, \
+ desc); \
+ } \
+ \
+ const OSSL_DISPATCH ossl_rsa_##md##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))rsa_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, \
+ (void (*)(void))rsa_##md##_sign_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))rsa_sign }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))rsa_##md##_sign_message_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_UPDATE, \
+ (void (*)(void))rsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_FINAL, \
+ (void (*)(void))rsa_sign_message_final }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, \
+ (void (*)(void))rsa_##md##_verify_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, \
+ (void (*)(void))rsa_verify }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))rsa_##md##_verify_message_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_UPDATE, \
+ (void (*)(void))rsa_signverify_message_update }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_FINAL, \
+ (void (*)(void))rsa_verify_message_final }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER_INIT, \
+ (void (*)(void))rsa_##md##_verify_recover_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_RECOVER, \
+ (void (*)(void))rsa_verify_recover }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))rsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))rsa_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_QUERY_KEY_TYPES, \
+ (void (*)(void))rsa_sigalg_query_key_types }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))rsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))rsa_gettable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, \
+ (void (*)(void))rsa_sigalg_set_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))rsa_sigalg_settable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+#if !defined(OPENSSL_NO_RMD160) && !defined(FIPS_MODULE)
+IMPL_RSA_SIGALG(ripemd160, RIPEMD160);
+#endif
+IMPL_RSA_SIGALG(sha1, SHA1);
+IMPL_RSA_SIGALG(sha224, SHA2-224);
+IMPL_RSA_SIGALG(sha256, SHA2-256);
+IMPL_RSA_SIGALG(sha384, SHA2-384);
+IMPL_RSA_SIGALG(sha512, SHA2-512);
+IMPL_RSA_SIGALG(sha512_224, SHA2-512/224);
+IMPL_RSA_SIGALG(sha512_256, SHA2-512/256);
+IMPL_RSA_SIGALG(sha3_224, SHA3-224);
+IMPL_RSA_SIGALG(sha3_256, SHA3-256);
+IMPL_RSA_SIGALG(sha3_384, SHA3-384);
+IMPL_RSA_SIGALG(sha3_512, SHA3-512);
+#if !defined(OPENSSL_NO_SM3) && !defined(FIPS_MODULE)
+IMPL_RSA_SIGALG(sm3, SM3);
+#endif
diff --git a/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c b/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c
new file mode 100644
index 000000000000..40fc6846e2ad
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/slh_dsa_sig.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_names.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/proverr.h>
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "prov/der_slh_dsa.h"
+#include "crypto/slh_dsa.h"
+#include "internal/sizes.h"
+
+#define SLH_DSA_MAX_ADD_RANDOM_LEN 32
+
+#define SLH_DSA_MESSAGE_ENCODE_RAW 0
+#define SLH_DSA_MESSAGE_ENCODE_PURE 1
+
+static OSSL_FUNC_signature_sign_message_init_fn slh_dsa_sign_msg_init;
+static OSSL_FUNC_signature_sign_fn slh_dsa_sign;
+static OSSL_FUNC_signature_verify_message_init_fn slh_dsa_verify_msg_init;
+static OSSL_FUNC_signature_verify_fn slh_dsa_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn slh_dsa_digest_signverify_init;
+static OSSL_FUNC_signature_digest_sign_fn slh_dsa_digest_sign;
+static OSSL_FUNC_signature_digest_verify_fn slh_dsa_digest_verify;
+static OSSL_FUNC_signature_freectx_fn slh_dsa_freectx;
+static OSSL_FUNC_signature_dupctx_fn slh_dsa_dupctx;
+static OSSL_FUNC_signature_set_ctx_params_fn slh_dsa_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn slh_dsa_settable_ctx_params;
+
+/*
+ * NOTE: Any changes to this structure may require updating slh_dsa_dupctx().
+ */
+typedef struct {
+ SLH_DSA_KEY *key; /* Note that the key is not owned by this object */
+ SLH_DSA_HASH_CTX *hash_ctx;
+ uint8_t context_string[SLH_DSA_MAX_CONTEXT_STRING_LEN];
+ size_t context_string_len;
+ uint8_t add_random[SLH_DSA_MAX_ADD_RANDOM_LEN];
+ size_t add_random_len;
+ int msg_encode;
+ int deterministic;
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ const char *alg;
+ /* The Algorithm Identifier of the signature algorithm */
+ uint8_t aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+} PROV_SLH_DSA_CTX;
+
+static void slh_dsa_freectx(void *vctx)
+{
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+
+ ossl_slh_dsa_hash_ctx_free(ctx->hash_ctx);
+ OPENSSL_free(ctx->propq);
+ OPENSSL_cleanse(ctx->add_random, ctx->add_random_len);
+ OPENSSL_free(ctx);
+}
+
+static void *slh_dsa_newctx(void *provctx, const char *alg, const char *propq)
+{
+ PROV_SLH_DSA_CTX *ctx;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(PROV_SLH_DSA_CTX));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL)
+ goto err;
+ ctx->alg = alg;
+ ctx->msg_encode = SLH_DSA_MESSAGE_ENCODE_PURE;
+ return ctx;
+ err:
+ slh_dsa_freectx(ctx);
+ return NULL;
+}
+
+static void *slh_dsa_dupctx(void *vctx)
+{
+ PROV_SLH_DSA_CTX *src = (PROV_SLH_DSA_CTX *)vctx;
+ PROV_SLH_DSA_CTX *ret;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ /*
+ * Note that the SLH_DSA_KEY is ref counted via EVP_PKEY so we can just copy
+ * the key here.
+ */
+ ret = OPENSSL_memdup(src, sizeof(*src));
+ if (ret == NULL)
+ return NULL;
+ ret->propq = NULL;
+ ret->hash_ctx = NULL;
+ if (src->propq != NULL && (ret->propq = OPENSSL_strdup(src->propq)) == NULL)
+ goto err;
+ ret->hash_ctx = ossl_slh_dsa_hash_ctx_dup(src->hash_ctx);
+ if (ret->hash_ctx == NULL)
+ goto err;
+
+ return ret;
+ err:
+ slh_dsa_freectx(ret);
+ return NULL;
+}
+
+static int slh_dsa_set_alg_id_buffer(PROV_SLH_DSA_CTX *ctx)
+{
+ int ret;
+ WPACKET pkt;
+ uint8_t *aid = NULL;
+
+ /*
+ * We do not care about DER writing errors.
+ * All it really means is that for some reason, there's no
+ * AlgorithmIdentifier to be had, but the operation itself is
+ * still valid, just as long as it's not used to construct
+ * anything that needs an AlgorithmIdentifier.
+ */
+ ctx->aid_len = 0;
+ ret = WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf));
+ ret = ret && ossl_DER_w_algorithmIdentifier_SLH_DSA(&pkt, -1, ctx->key);
+ if (ret && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &ctx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && ctx->aid_len != 0)
+ memmove(ctx->aid_buf, aid, ctx->aid_len);
+ return 1;
+}
+
+static int slh_dsa_signverify_msg_init(void *vctx, void *vkey,
+ const OSSL_PARAM params[], int operation,
+ const char *desc)
+{
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+ SLH_DSA_KEY *key = vkey;
+
+ if (!ossl_prov_is_running()
+ || ctx == NULL)
+ return 0;
+
+ if (vkey == NULL && ctx->key == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (key != NULL) {
+ if (!ossl_slh_dsa_key_type_matches(key, ctx->alg))
+ return 0;
+ ctx->hash_ctx = ossl_slh_dsa_hash_ctx_new(key);
+ if (ctx->hash_ctx == NULL)
+ return 0;
+ ctx->key = vkey;
+ }
+
+ slh_dsa_set_alg_id_buffer(ctx);
+ if (!slh_dsa_set_ctx_params(ctx, params))
+ return 0;
+ return 1;
+}
+
+static int slh_dsa_sign_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ return slh_dsa_signverify_msg_init(vctx, vkey, params,
+ EVP_PKEY_OP_SIGN, "SLH_DSA Sign Init");
+}
+
+static int slh_dsa_digest_signverify_init(void *vctx, const char *mdname,
+ void *vkey, const OSSL_PARAM params[])
+{
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+
+ if (mdname != NULL && mdname[0] != '\0') {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST,
+ "Explicit digest not supported for SLH-DSA operations");
+ return 0;
+ }
+
+ if (vkey == NULL && ctx->key != NULL)
+ return slh_dsa_set_ctx_params(ctx, params);
+
+ return slh_dsa_signverify_msg_init(vctx, vkey, params,
+ EVP_PKEY_OP_SIGN, "SLH_DSA Sign Init");
+}
+
+static int slh_dsa_sign(void *vctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *msg, size_t msg_len)
+{
+ int ret = 0;
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+ uint8_t add_rand[SLH_DSA_MAX_ADD_RANDOM_LEN], *opt_rand = NULL;
+ size_t n = 0;
+
+ if (!ossl_prov_is_running())
+ return 0;
+
+ if (sig != NULL) {
+ if (ctx->add_random_len != 0) {
+ opt_rand = ctx->add_random;
+ } else if (ctx->deterministic == 0) {
+ n = ossl_slh_dsa_key_get_n(ctx->key);
+ if (RAND_priv_bytes_ex(ctx->libctx, add_rand, n, 0) <= 0)
+ return 0;
+ opt_rand = add_rand;
+ }
+ }
+ ret = ossl_slh_dsa_sign(ctx->hash_ctx, msg, msg_len,
+ ctx->context_string, ctx->context_string_len,
+ opt_rand, ctx->msg_encode,
+ sig, siglen, sigsize);
+ if (opt_rand != add_rand)
+ OPENSSL_cleanse(opt_rand, n);
+ return ret;
+}
+
+static int slh_dsa_digest_sign(void *vctx, uint8_t *sig, size_t *siglen, size_t sigsize,
+ const uint8_t *tbs, size_t tbslen)
+{
+ return slh_dsa_sign(vctx, sig, siglen, sigsize, tbs, tbslen);
+}
+
+static int slh_dsa_verify_msg_init(void *vctx, void *vkey, const OSSL_PARAM params[])
+{
+ return slh_dsa_signverify_msg_init(vctx, vkey, params, EVP_PKEY_OP_VERIFY,
+ "SLH_DSA Verify Init");
+}
+
+static int slh_dsa_verify(void *vctx, const uint8_t *sig, size_t siglen,
+ const uint8_t *msg, size_t msg_len)
+{
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+
+ if (!ossl_prov_is_running())
+ return 0;
+ return ossl_slh_dsa_verify(ctx->hash_ctx, msg, msg_len,
+ ctx->context_string, ctx->context_string_len,
+ ctx->msg_encode, sig, siglen);
+}
+static int slh_dsa_digest_verify(void *vctx, const uint8_t *sig, size_t siglen,
+ const uint8_t *tbs, size_t tbslen)
+{
+ return slh_dsa_verify(vctx, sig, siglen, tbs, tbslen);
+}
+
+static int slh_dsa_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ PROV_SLH_DSA_CTX *pctx = (PROV_SLH_DSA_CTX *)vctx;
+ const OSSL_PARAM *p;
+
+ if (pctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_CONTEXT_STRING);
+ if (p != NULL) {
+ void *vp = pctx->context_string;
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp, sizeof(pctx->context_string),
+ &(pctx->context_string_len))) {
+ pctx->context_string_len = 0;
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_TEST_ENTROPY);
+ if (p != NULL) {
+ void *vp = pctx->add_random;
+ size_t n = ossl_slh_dsa_key_get_n(pctx->key);
+
+ if (!OSSL_PARAM_get_octet_string(p, &vp, n, &(pctx->add_random_len))
+ || pctx->add_random_len != n) {
+ pctx->add_random_len = 0;
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DETERMINISTIC);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->deterministic))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &pctx->msg_encode))
+ return 0;
+ return 1;
+}
+
+static const OSSL_PARAM *slh_dsa_settable_ctx_params(void *vctx,
+ ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_CONTEXT_STRING, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_TEST_ENTROPY, NULL, 0),
+ OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_DETERMINISTIC, 0),
+ OSSL_PARAM_int(OSSL_SIGNATURE_PARAM_MESSAGE_ENCODING, 0),
+ OSSL_PARAM_END
+ };
+
+ return settable_ctx_params;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *slh_dsa_gettable_ctx_params(ossl_unused void *vctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int slh_dsa_get_ctx_params(void *vctx, OSSL_PARAM *params)
+{
+ PROV_SLH_DSA_CTX *ctx = (PROV_SLH_DSA_CTX *)vctx;
+ OSSL_PARAM *p;
+
+ if (ctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p,
+ ctx->aid_len == 0 ? NULL : ctx->aid_buf,
+ ctx->aid_len))
+ return 0;
+
+ return 1;
+}
+
+#define MAKE_SIGNATURE_FUNCTIONS(alg, fn) \
+ static OSSL_FUNC_signature_newctx_fn slh_dsa_##fn##_newctx; \
+ static void *slh_dsa_##fn##_newctx(void *provctx, const char *propq) \
+ { \
+ return slh_dsa_newctx(provctx, alg, propq); \
+ } \
+ const OSSL_DISPATCH ossl_slh_dsa_##fn##_signature_functions[] = { \
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))slh_dsa_##fn##_newctx }, \
+ { OSSL_FUNC_SIGNATURE_SIGN_MESSAGE_INIT, \
+ (void (*)(void))slh_dsa_sign_msg_init }, \
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))slh_dsa_sign }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY_MESSAGE_INIT, \
+ (void (*)(void))slh_dsa_verify_msg_init }, \
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))slh_dsa_verify }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, \
+ (void (*)(void))slh_dsa_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, \
+ (void (*)(void))slh_dsa_digest_sign }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, \
+ (void (*)(void))slh_dsa_digest_signverify_init }, \
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, \
+ (void (*)(void))slh_dsa_digest_verify }, \
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))slh_dsa_freectx }, \
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))slh_dsa_dupctx }, \
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))slh_dsa_set_ctx_params },\
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))slh_dsa_settable_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, \
+ (void (*)(void))slh_dsa_get_ctx_params }, \
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, \
+ (void (*)(void))slh_dsa_gettable_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-128s", sha2_128s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-128f", sha2_128f);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-192s", sha2_192s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-192f", sha2_192f);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-256s", sha2_256s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHA2-256f", sha2_256f);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-128s", shake_128s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-128f", shake_128f);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-192s", shake_192s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-192f", shake_192f);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-256s", shake_256s);
+MAKE_SIGNATURE_FUNCTIONS("SLH-DSA-SHAKE-256f", shake_256f);
diff --git a/crypto/openssl/providers/implementations/signature/sm2_sig.c b/crypto/openssl/providers/implementations/signature/sm2_sig.c
new file mode 100644
index 000000000000..bcbbd1e24503
--- /dev/null
+++ b/crypto/openssl/providers/implementations/signature/sm2_sig.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2020-2024 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * ECDSA low level APIs are deprecated for public use, but still ok for
+ * internal use - SM2 implementation uses ECDSA_size() function.
+ */
+#include "internal/deprecated.h"
+
+#include <string.h> /* memcpy */
+#include <openssl/crypto.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/dsa.h>
+#include <openssl/params.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "internal/nelem.h"
+#include "internal/sizes.h"
+#include "internal/cryptlib.h"
+#include "internal/sm3.h"
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/provider_ctx.h"
+#include "crypto/ec.h"
+#include "crypto/sm2.h"
+#include "prov/der_sm2.h"
+
+static OSSL_FUNC_signature_newctx_fn sm2sig_newctx;
+static OSSL_FUNC_signature_sign_init_fn sm2sig_signature_init;
+static OSSL_FUNC_signature_verify_init_fn sm2sig_signature_init;
+static OSSL_FUNC_signature_sign_fn sm2sig_sign;
+static OSSL_FUNC_signature_verify_fn sm2sig_verify;
+static OSSL_FUNC_signature_digest_sign_init_fn sm2sig_digest_signverify_init;
+static OSSL_FUNC_signature_digest_sign_update_fn sm2sig_digest_signverify_update;
+static OSSL_FUNC_signature_digest_sign_final_fn sm2sig_digest_sign_final;
+static OSSL_FUNC_signature_digest_verify_init_fn sm2sig_digest_signverify_init;
+static OSSL_FUNC_signature_digest_verify_update_fn sm2sig_digest_signverify_update;
+static OSSL_FUNC_signature_digest_verify_final_fn sm2sig_digest_verify_final;
+static OSSL_FUNC_signature_freectx_fn sm2sig_freectx;
+static OSSL_FUNC_signature_dupctx_fn sm2sig_dupctx;
+static OSSL_FUNC_signature_get_ctx_params_fn sm2sig_get_ctx_params;
+static OSSL_FUNC_signature_gettable_ctx_params_fn sm2sig_gettable_ctx_params;
+static OSSL_FUNC_signature_set_ctx_params_fn sm2sig_set_ctx_params;
+static OSSL_FUNC_signature_settable_ctx_params_fn sm2sig_settable_ctx_params;
+static OSSL_FUNC_signature_get_ctx_md_params_fn sm2sig_get_ctx_md_params;
+static OSSL_FUNC_signature_gettable_ctx_md_params_fn sm2sig_gettable_ctx_md_params;
+static OSSL_FUNC_signature_set_ctx_md_params_fn sm2sig_set_ctx_md_params;
+static OSSL_FUNC_signature_settable_ctx_md_params_fn sm2sig_settable_ctx_md_params;
+
+/*
+ * What's passed as an actual key is defined by the KEYMGMT interface.
+ * We happen to know that our KEYMGMT simply passes EC structures, so
+ * we use that here too.
+ */
+typedef struct {
+ OSSL_LIB_CTX *libctx;
+ char *propq;
+ EC_KEY *ec;
+
+ /*
+ * Flag to determine if the 'z' digest needs to be computed and fed to the
+ * hash function.
+ * This flag should be set on initialization and the computation should
+ * be performed only once, on first update.
+ */
+ unsigned int flag_compute_z_digest : 1;
+
+ char mdname[OSSL_MAX_NAME_SIZE];
+
+ /* The Algorithm Identifier of the combined signature algorithm */
+ unsigned char aid_buf[OSSL_MAX_ALGORITHM_ID_SIZE];
+ size_t aid_len;
+
+ /* main digest */
+ EVP_MD *md;
+ EVP_MD_CTX *mdctx;
+ size_t mdsize;
+
+ /* SM2 ID used for calculating the Z value */
+ unsigned char *id;
+ size_t id_len;
+} PROV_SM2_CTX;
+
+static int sm2sig_set_mdname(PROV_SM2_CTX *psm2ctx, const char *mdname)
+{
+ if (psm2ctx->md == NULL) /* We need an SM3 md to compare with */
+ psm2ctx->md = EVP_MD_fetch(psm2ctx->libctx, psm2ctx->mdname,
+ psm2ctx->propq);
+ if (psm2ctx->md == NULL)
+ return 0;
+
+ /* XOF digests don't work */
+ if (EVP_MD_xof(psm2ctx->md)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_XOF_DIGESTS_NOT_ALLOWED);
+ return 0;
+ }
+
+ if (mdname == NULL)
+ return 1;
+
+ if (strlen(mdname) >= sizeof(psm2ctx->mdname)
+ || !EVP_MD_is_a(psm2ctx->md, mdname)) {
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_DIGEST, "digest=%s",
+ mdname);
+ return 0;
+ }
+
+ OPENSSL_strlcpy(psm2ctx->mdname, mdname, sizeof(psm2ctx->mdname));
+ return 1;
+}
+
+static void *sm2sig_newctx(void *provctx, const char *propq)
+{
+ PROV_SM2_CTX *ctx = OPENSSL_zalloc(sizeof(PROV_SM2_CTX));
+
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->libctx = PROV_LIBCTX_OF(provctx);
+ if (propq != NULL && (ctx->propq = OPENSSL_strdup(propq)) == NULL) {
+ OPENSSL_free(ctx);
+ return NULL;
+ }
+ ctx->mdsize = SM3_DIGEST_LENGTH;
+ strcpy(ctx->mdname, OSSL_DIGEST_NAME_SM3);
+ return ctx;
+}
+
+static int sm2sig_signature_init(void *vpsm2ctx, void *ec,
+ const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (!ossl_prov_is_running()
+ || psm2ctx == NULL)
+ return 0;
+
+ if (ec == NULL && psm2ctx->ec == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_NO_KEY_SET);
+ return 0;
+ }
+
+ if (ec != NULL) {
+ if (!EC_KEY_up_ref(ec))
+ return 0;
+ EC_KEY_free(psm2ctx->ec);
+ psm2ctx->ec = ec;
+ }
+
+ return sm2sig_set_ctx_params(psm2ctx, params);
+}
+
+static int sm2sig_sign(void *vpsm2ctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize, const unsigned char *tbs, size_t tbslen)
+{
+ PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ int ret;
+ unsigned int sltmp;
+ /* SM2 uses ECDSA_size as well */
+ size_t ecsize = ECDSA_size(ctx->ec);
+
+ if (sig == NULL) {
+ *siglen = ecsize;
+ return 1;
+ }
+
+ if (sigsize < (size_t)ecsize)
+ return 0;
+
+ if (ctx->mdsize != 0 && tbslen != ctx->mdsize)
+ return 0;
+
+ ret = ossl_sm2_internal_sign(tbs, tbslen, sig, &sltmp, ctx->ec);
+ if (ret <= 0)
+ return 0;
+
+ *siglen = sltmp;
+ return 1;
+}
+
+static int sm2sig_verify(void *vpsm2ctx, const unsigned char *sig, size_t siglen,
+ const unsigned char *tbs, size_t tbslen)
+{
+ PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (ctx->mdsize != 0 && tbslen != ctx->mdsize)
+ return 0;
+
+ return ossl_sm2_internal_verify(tbs, tbslen, sig, siglen, ctx->ec);
+}
+
+static void free_md(PROV_SM2_CTX *ctx)
+{
+ EVP_MD_CTX_free(ctx->mdctx);
+ EVP_MD_free(ctx->md);
+ ctx->mdctx = NULL;
+ ctx->md = NULL;
+}
+
+static int sm2sig_digest_signverify_init(void *vpsm2ctx, const char *mdname,
+ void *ec, const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ int md_nid;
+ WPACKET pkt;
+ int ret = 0;
+ unsigned char *aid = NULL;
+
+ if (!sm2sig_signature_init(vpsm2ctx, ec, params)
+ || !sm2sig_set_mdname(ctx, mdname))
+ return ret;
+
+ if (ctx->mdctx == NULL) {
+ ctx->mdctx = EVP_MD_CTX_new();
+ if (ctx->mdctx == NULL)
+ goto error;
+ }
+
+ md_nid = EVP_MD_get_type(ctx->md);
+
+ /*
+ * We do not care about DER writing errors.
+ * All it really means is that for some reason, there's no
+ * AlgorithmIdentifier to be had, but the operation itself is
+ * still valid, just as long as it's not used to construct
+ * anything that needs an AlgorithmIdentifier.
+ */
+ ctx->aid_len = 0;
+ if (WPACKET_init_der(&pkt, ctx->aid_buf, sizeof(ctx->aid_buf))
+ && ossl_DER_w_algorithmIdentifier_SM2_with_MD(&pkt, -1, ctx->ec, md_nid)
+ && WPACKET_finish(&pkt)) {
+ WPACKET_get_total_written(&pkt, &ctx->aid_len);
+ aid = WPACKET_get_curr(&pkt);
+ }
+ WPACKET_cleanup(&pkt);
+ if (aid != NULL && ctx->aid_len != 0)
+ memmove(ctx->aid_buf, aid, ctx->aid_len);
+
+ if (!EVP_DigestInit_ex2(ctx->mdctx, ctx->md, params))
+ goto error;
+
+ ctx->flag_compute_z_digest = 1;
+
+ ret = 1;
+
+ error:
+ return ret;
+}
+
+static int sm2sig_compute_z_digest(PROV_SM2_CTX *ctx)
+{
+ uint8_t *z = NULL;
+ int ret = 1;
+
+ if (ctx->flag_compute_z_digest) {
+ /* Only do this once */
+ ctx->flag_compute_z_digest = 0;
+
+ if ((z = OPENSSL_zalloc(ctx->mdsize)) == NULL
+ /* get hashed prefix 'z' of tbs message */
+ || !ossl_sm2_compute_z_digest(z, ctx->md, ctx->id, ctx->id_len,
+ ctx->ec)
+ || !EVP_DigestUpdate(ctx->mdctx, z, ctx->mdsize))
+ ret = 0;
+ OPENSSL_free(z);
+ }
+
+ return ret;
+}
+
+int sm2sig_digest_signverify_update(void *vpsm2ctx, const unsigned char *data,
+ size_t datalen)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx == NULL || psm2ctx->mdctx == NULL)
+ return 0;
+
+ return sm2sig_compute_z_digest(psm2ctx)
+ && EVP_DigestUpdate(psm2ctx->mdctx, data, datalen);
+}
+
+int sm2sig_digest_sign_final(void *vpsm2ctx, unsigned char *sig, size_t *siglen,
+ size_t sigsize)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+
+ if (psm2ctx == NULL || psm2ctx->mdctx == NULL)
+ return 0;
+
+ /*
+ * If sig is NULL then we're just finding out the sig size. Other fields
+ * are ignored. Defer to sm2sig_sign.
+ */
+ if (sig != NULL) {
+ if (!(sm2sig_compute_z_digest(psm2ctx)
+ && EVP_DigestFinal_ex(psm2ctx->mdctx, digest, &dlen)))
+ return 0;
+ }
+
+ return sm2sig_sign(vpsm2ctx, sig, siglen, sigsize, digest, (size_t)dlen);
+}
+
+
+int sm2sig_digest_verify_final(void *vpsm2ctx, const unsigned char *sig,
+ size_t siglen)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ unsigned char digest[EVP_MAX_MD_SIZE];
+ unsigned int dlen = 0;
+ int md_size;
+
+ if (psm2ctx == NULL || psm2ctx->mdctx == NULL)
+ return 0;
+
+ md_size = EVP_MD_get_size(psm2ctx->md);
+ if (md_size <= 0 || md_size > (int)sizeof(digest))
+ return 0;
+
+ if (!(sm2sig_compute_z_digest(psm2ctx)
+ && EVP_DigestFinal_ex(psm2ctx->mdctx, digest, &dlen)))
+ return 0;
+
+ return sm2sig_verify(vpsm2ctx, sig, siglen, digest, (size_t)dlen);
+}
+
+static void sm2sig_freectx(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ free_md(ctx);
+ EC_KEY_free(ctx->ec);
+ OPENSSL_free(ctx->propq);
+ OPENSSL_free(ctx->id);
+ OPENSSL_free(ctx);
+}
+
+static void *sm2sig_dupctx(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *srcctx = (PROV_SM2_CTX *)vpsm2ctx;
+ PROV_SM2_CTX *dstctx;
+
+ dstctx = OPENSSL_zalloc(sizeof(*srcctx));
+ if (dstctx == NULL)
+ return NULL;
+
+ *dstctx = *srcctx;
+ dstctx->ec = NULL;
+ dstctx->propq = NULL;
+ dstctx->md = NULL;
+ dstctx->mdctx = NULL;
+ dstctx->id = NULL;
+
+ if (srcctx->ec != NULL && !EC_KEY_up_ref(srcctx->ec))
+ goto err;
+ dstctx->ec = srcctx->ec;
+
+ if (srcctx->propq != NULL) {
+ dstctx->propq = OPENSSL_strdup(srcctx->propq);
+ if (dstctx->propq == NULL)
+ goto err;
+ }
+
+ if (srcctx->md != NULL && !EVP_MD_up_ref(srcctx->md))
+ goto err;
+ dstctx->md = srcctx->md;
+
+ if (srcctx->mdctx != NULL) {
+ dstctx->mdctx = EVP_MD_CTX_new();
+ if (dstctx->mdctx == NULL
+ || !EVP_MD_CTX_copy_ex(dstctx->mdctx, srcctx->mdctx))
+ goto err;
+ }
+
+ if (srcctx->id != NULL) {
+ dstctx->id = OPENSSL_malloc(srcctx->id_len);
+ if (dstctx->id == NULL)
+ goto err;
+ dstctx->id_len = srcctx->id_len;
+ memcpy(dstctx->id, srcctx->id, srcctx->id_len);
+ }
+
+ return dstctx;
+ err:
+ sm2sig_freectx(dstctx);
+ return NULL;
+}
+
+static int sm2sig_get_ctx_params(void *vpsm2ctx, OSSL_PARAM *params)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ OSSL_PARAM *p;
+
+ if (psm2ctx == NULL)
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
+ if (p != NULL
+ && !OSSL_PARAM_set_octet_string(p,
+ psm2ctx->aid_len == 0 ? NULL : psm2ctx->aid_buf,
+ psm2ctx->aid_len))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE);
+ if (p != NULL && !OSSL_PARAM_set_size_t(p, psm2ctx->mdsize))
+ return 0;
+
+ p = OSSL_PARAM_locate(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL && !OSSL_PARAM_set_utf8_string(p, psm2ctx->md == NULL
+ ? psm2ctx->mdname
+ : EVP_MD_get0_name(psm2ctx->md)))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM known_gettable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID, NULL, 0),
+ OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *sm2sig_gettable_ctx_params(ossl_unused void *vpsm2ctx,
+ ossl_unused void *provctx)
+{
+ return known_gettable_ctx_params;
+}
+
+static int sm2sig_set_ctx_params(void *vpsm2ctx, const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+ const OSSL_PARAM *p;
+ size_t mdsize;
+
+ if (psm2ctx == NULL)
+ return 0;
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_PKEY_PARAM_DIST_ID);
+ if (p != NULL) {
+ void *tmp_id = NULL;
+ size_t tmp_idlen = 0;
+
+ /*
+ * If the 'z' digest has already been computed, the ID is set too late
+ */
+ if (!psm2ctx->flag_compute_z_digest)
+ return 0;
+
+ if (p->data_size != 0
+ && !OSSL_PARAM_get_octet_string(p, &tmp_id, 0, &tmp_idlen))
+ return 0;
+ OPENSSL_free(psm2ctx->id);
+ psm2ctx->id = tmp_id;
+ psm2ctx->id_len = tmp_idlen;
+ }
+
+ /*
+ * The following code checks that the size is the same as the SM3 digest
+ * size returning an error otherwise.
+ * If there is ever any different digest algorithm allowed with SM2
+ * this needs to be adjusted accordingly.
+ */
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST_SIZE);
+ if (p != NULL && (!OSSL_PARAM_get_size_t(p, &mdsize)
+ || mdsize != psm2ctx->mdsize))
+ return 0;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_SIGNATURE_PARAM_DIGEST);
+ if (p != NULL) {
+ char *mdname = NULL;
+
+ if (!OSSL_PARAM_get_utf8_string(p, &mdname, 0))
+ return 0;
+ if (!sm2sig_set_mdname(psm2ctx, mdname)) {
+ OPENSSL_free(mdname);
+ return 0;
+ }
+ OPENSSL_free(mdname);
+ }
+
+ return 1;
+}
+
+static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_size_t(OSSL_SIGNATURE_PARAM_DIGEST_SIZE, NULL),
+ OSSL_PARAM_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, NULL, 0),
+ OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DIST_ID, NULL, 0),
+ OSSL_PARAM_END
+};
+
+static const OSSL_PARAM *sm2sig_settable_ctx_params(ossl_unused void *vpsm2ctx,
+ ossl_unused void *provctx)
+{
+ return known_settable_ctx_params;
+}
+
+static int sm2sig_get_ctx_md_params(void *vpsm2ctx, OSSL_PARAM *params)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_get_params(psm2ctx->mdctx, params);
+}
+
+static const OSSL_PARAM *sm2sig_gettable_ctx_md_params(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx->md == NULL)
+ return 0;
+
+ return EVP_MD_gettable_ctx_params(psm2ctx->md);
+}
+
+static int sm2sig_set_ctx_md_params(void *vpsm2ctx, const OSSL_PARAM params[])
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx->mdctx == NULL)
+ return 0;
+
+ return EVP_MD_CTX_set_params(psm2ctx->mdctx, params);
+}
+
+static const OSSL_PARAM *sm2sig_settable_ctx_md_params(void *vpsm2ctx)
+{
+ PROV_SM2_CTX *psm2ctx = (PROV_SM2_CTX *)vpsm2ctx;
+
+ if (psm2ctx->md == NULL)
+ return 0;
+
+ return EVP_MD_settable_ctx_params(psm2ctx->md);
+}
+
+const OSSL_DISPATCH ossl_sm2_signature_functions[] = {
+ { OSSL_FUNC_SIGNATURE_NEWCTX, (void (*)(void))sm2sig_newctx },
+ { OSSL_FUNC_SIGNATURE_SIGN_INIT, (void (*)(void))sm2sig_signature_init },
+ { OSSL_FUNC_SIGNATURE_SIGN, (void (*)(void))sm2sig_sign },
+ { OSSL_FUNC_SIGNATURE_VERIFY_INIT, (void (*)(void))sm2sig_signature_init },
+ { OSSL_FUNC_SIGNATURE_VERIFY, (void (*)(void))sm2sig_verify },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT,
+ (void (*)(void))sm2sig_digest_signverify_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_UPDATE,
+ (void (*)(void))sm2sig_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_FINAL,
+ (void (*)(void))sm2sig_digest_sign_final },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT,
+ (void (*)(void))sm2sig_digest_signverify_init },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_UPDATE,
+ (void (*)(void))sm2sig_digest_signverify_update },
+ { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_FINAL,
+ (void (*)(void))sm2sig_digest_verify_final },
+ { OSSL_FUNC_SIGNATURE_FREECTX, (void (*)(void))sm2sig_freectx },
+ { OSSL_FUNC_SIGNATURE_DUPCTX, (void (*)(void))sm2sig_dupctx },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void (*)(void))sm2sig_get_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS,
+ (void (*)(void))sm2sig_gettable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_PARAMS, (void (*)(void))sm2sig_set_ctx_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_PARAMS,
+ (void (*)(void))sm2sig_settable_ctx_params },
+ { OSSL_FUNC_SIGNATURE_GET_CTX_MD_PARAMS,
+ (void (*)(void))sm2sig_get_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))sm2sig_gettable_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SET_CTX_MD_PARAMS,
+ (void (*)(void))sm2sig_set_ctx_md_params },
+ { OSSL_FUNC_SIGNATURE_SETTABLE_CTX_MD_PARAMS,
+ (void (*)(void))sm2sig_settable_ctx_md_params },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c b/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c
new file mode 100644
index 000000000000..6d3b5f377fb8
--- /dev/null
+++ b/crypto/openssl/providers/implementations/skeymgmt/aes_skmgmt.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include "crypto/types.h"
+#include "skeymgmt_lcl.h"
+#include "internal/skey.h"
+#include "prov/implementations.h"
+
+static OSSL_FUNC_skeymgmt_import_fn aes_import;
+static OSSL_FUNC_skeymgmt_export_fn aes_export;
+
+static void *aes_import(void *provctx, int selection,
+ const OSSL_PARAM params[])
+{
+ PROV_SKEY *aes = generic_import(provctx, selection, params);
+
+ if (aes == NULL)
+ return NULL;
+
+ if (aes->length != 16 && aes->length != 24 && aes->length != 32) {
+ generic_free(aes);
+ return NULL;
+ }
+ aes->type = SKEY_TYPE_AES;
+
+ return aes;
+}
+
+static int aes_export(void *keydata, int selection,
+ OSSL_CALLBACK *param_callback, void *cbarg)
+{
+ PROV_SKEY *aes = keydata;
+
+ if (aes->type != SKEY_TYPE_AES)
+ return 0;
+
+ return generic_export(keydata, selection, param_callback, cbarg);
+}
+
+const OSSL_DISPATCH ossl_aes_skeymgmt_functions[] = {
+ { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))generic_free },
+ { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))aes_import },
+ { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))aes_export },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/skeymgmt/build.info b/crypto/openssl/providers/implementations/skeymgmt/build.info
new file mode 100644
index 000000000000..8bccdd72f6c6
--- /dev/null
+++ b/crypto/openssl/providers/implementations/skeymgmt/build.info
@@ -0,0 +1,8 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$AES_GOAL=../../libdefault.a ../../libfips.a
+$GENERIC_GOAL=../../libdefault.a ../../libfips.a
+
+SOURCE[$AES_GOAL]=aes_skmgmt.c
+SOURCE[$GENERIC_GOAL]=generic.c
diff --git a/crypto/openssl/providers/implementations/skeymgmt/generic.c b/crypto/openssl/providers/implementations/skeymgmt/generic.c
new file mode 100644
index 000000000000..b41bf8e12dcb
--- /dev/null
+++ b/crypto/openssl/providers/implementations/skeymgmt/generic.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include "crypto/types.h"
+#include "internal/skey.h"
+#include "prov/provider_ctx.h"
+#include "prov/providercommon.h"
+#include "prov/implementations.h"
+#include "skeymgmt_lcl.h"
+
+void generic_free(void *keydata)
+{
+ PROV_SKEY *generic = keydata;
+
+ if (generic == NULL)
+ return;
+
+ OPENSSL_free(generic->data);
+ OPENSSL_free(generic);
+}
+
+void *generic_import(void *provctx, int selection, const OSSL_PARAM params[])
+{
+ OSSL_LIB_CTX *libctx = PROV_LIBCTX_OF(provctx);
+ const OSSL_PARAM *raw_bytes;
+ PROV_SKEY *generic = NULL;
+ int ok = 0;
+
+ if (!ossl_prov_is_running())
+ return NULL;
+
+ if ((selection & OSSL_SKEYMGMT_SELECT_SECRET_KEY) == 0)
+ return NULL;
+
+ raw_bytes = OSSL_PARAM_locate_const(params, OSSL_SKEY_PARAM_RAW_BYTES);
+ if (raw_bytes == NULL)
+ return NULL;
+
+ generic = OPENSSL_zalloc(sizeof(PROV_SKEY));
+ if (generic == NULL)
+ return NULL;
+
+ generic->libctx = libctx;
+
+ generic->type = SKEY_TYPE_GENERIC;
+
+ if ((generic->data = OPENSSL_memdup(raw_bytes->data, raw_bytes->data_size)) == NULL)
+ goto end;
+ generic->length = raw_bytes->data_size;
+ ok = 1;
+
+end:
+ if (ok == 0) {
+ generic_free(generic);
+ generic = NULL;
+ }
+ return generic;
+}
+
+int generic_export(void *keydata, int selection,
+ OSSL_CALLBACK *param_callback, void *cbarg)
+{
+ PROV_SKEY *gen = keydata;
+ OSSL_PARAM params[2];
+
+ if (!ossl_prov_is_running() || gen == NULL)
+ return 0;
+
+ /* If we use generic SKEYMGMT as a "base class", we shouldn't check the type */
+ if ((selection & OSSL_SKEYMGMT_SELECT_SECRET_KEY) == 0)
+ return 0;
+
+ params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES,
+ gen->data, gen->length);
+ params[1] = OSSL_PARAM_construct_end();
+
+ return param_callback(params, cbarg);
+}
+
+const OSSL_DISPATCH ossl_generic_skeymgmt_functions[] = {
+ { OSSL_FUNC_SKEYMGMT_FREE, (void (*)(void))generic_free },
+ { OSSL_FUNC_SKEYMGMT_IMPORT, (void (*)(void))generic_import },
+ { OSSL_FUNC_SKEYMGMT_EXPORT, (void (*)(void))generic_export },
+ OSSL_DISPATCH_END
+};
diff --git a/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h b/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h
new file mode 100644
index 000000000000..c180c1d30359
--- /dev/null
+++ b/crypto/openssl/providers/implementations/skeymgmt/skeymgmt_lcl.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#ifndef OSSL_PROVIDERS_SKEYMGMT_LCL_H
+# define OSSL_PROVIDERS_SKEYMGMT_LCL_H
+# pragma once
+# include <openssl/core_dispatch.h>
+
+OSSL_FUNC_skeymgmt_import_fn generic_import;
+OSSL_FUNC_skeymgmt_export_fn generic_export;
+OSSL_FUNC_skeymgmt_free_fn generic_free;
+
+#endif
diff --git a/crypto/openssl/providers/implementations/storemgmt/build.info b/crypto/openssl/providers/implementations/storemgmt/build.info
new file mode 100644
index 000000000000..d0b117492660
--- /dev/null
+++ b/crypto/openssl/providers/implementations/storemgmt/build.info
@@ -0,0 +1,9 @@
+# We make separate GOAL variables for each algorithm, to make it easy to
+# switch each to the Legacy provider when needed.
+
+$STORE_GOAL=../../libdefault.a
+
+SOURCE[$STORE_GOAL]=file_store.c file_store_any2obj.c
+IF[{- !$disabled{winstore} -}]
+ SOURCE[$STORE_GOAL]=winstore_store.c
+ENDIF
diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store.c b/crypto/openssl/providers/implementations/storemgmt/file_store.c
new file mode 100644
index 000000000000..134b8efefbb2
--- /dev/null
+++ b/crypto/openssl/providers/implementations/storemgmt/file_store.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/* This file has quite some overlap with engines/e_loader_attic.c */
+
+#include <string.h>
+#include <sys/stat.h>
+#include <ctype.h> /* isdigit */
+#include <assert.h>
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/decoder.h>
+#include <openssl/proverr.h>
+#include <openssl/store.h> /* The OSSL_STORE_INFO type numbers */
+#include "internal/cryptlib.h"
+#include "internal/o_dir.h"
+#include "crypto/decoder.h"
+#include "crypto/ctype.h" /* ossl_isdigit() */
+#include "prov/implementations.h"
+#include "prov/bio.h"
+#include "prov/providercommon.h"
+#include "file_store_local.h"
+
+DEFINE_STACK_OF(OSSL_STORE_INFO)
+
+#ifdef _WIN32
+# define stat _stat
+#endif
+
+#ifndef S_ISDIR
+# define S_ISDIR(a) (((a) & S_IFMT) == S_IFDIR)
+#endif
+
+static OSSL_FUNC_store_open_fn file_open;
+static OSSL_FUNC_store_attach_fn file_attach;
+static OSSL_FUNC_store_settable_ctx_params_fn file_settable_ctx_params;
+static OSSL_FUNC_store_set_ctx_params_fn file_set_ctx_params;
+static OSSL_FUNC_store_load_fn file_load;
+static OSSL_FUNC_store_eof_fn file_eof;
+static OSSL_FUNC_store_close_fn file_close;
+
+/*
+ * This implementation makes full use of OSSL_DECODER, and then some.
+ * It uses its own internal decoder implementation that reads DER and
+ * passes that on to the data callback; this decoder is created with
+ * internal OpenSSL functions, thereby bypassing the need for a surrounding
+ * provider. This is ok, since this is a local decoder, not meant for
+ * public consumption.
+ * Finally, it sets up its own construct and cleanup functions.
+ *
+ * Essentially, that makes this implementation a kind of glorified decoder.
+ */
+
+struct file_ctx_st {
+ void *provctx;
+ char *uri; /* The URI we currently try to load */
+ enum {
+ IS_FILE = 0, /* Read file and pass results */
+ IS_DIR /* Pass directory entry names */
+ } type;
+
+ union {
+ /* Used with |IS_FILE| */
+ struct {
+ BIO *file;
+
+ OSSL_DECODER_CTX *decoderctx;
+ char *input_type;
+ char *propq; /* The properties we got as a parameter */
+ } file;
+
+ /* Used with |IS_DIR| */
+ struct {
+ OPENSSL_DIR_CTX *ctx;
+ int end_reached;
+
+ /*
+ * When a search expression is given, these are filled in.
+ * |search_name| contains the file basename to look for.
+ * The string is exactly 8 characters long.
+ */
+ char search_name[9];
+
+ /*
+ * The directory reading utility we have combines opening with
+ * reading the first name. To make sure we can detect the end
+ * at the right time, we read early and cache the name.
+ */
+ const char *last_entry;
+ int last_errno;
+ } dir;
+ } _;
+
+ /* Expected object type. May be unspecified */
+ int expected_type;
+};
+
+static void free_file_ctx(struct file_ctx_st *ctx)
+{
+ if (ctx == NULL)
+ return;
+
+ OPENSSL_free(ctx->uri);
+ if (ctx->type != IS_DIR) {
+ OSSL_DECODER_CTX_free(ctx->_.file.decoderctx);
+ OPENSSL_free(ctx->_.file.propq);
+ OPENSSL_free(ctx->_.file.input_type);
+ }
+ OPENSSL_free(ctx);
+}
+
+static struct file_ctx_st *new_file_ctx(int type, const char *uri,
+ void *provctx)
+{
+ struct file_ctx_st *ctx = NULL;
+
+ if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) != NULL
+ && (uri == NULL || (ctx->uri = OPENSSL_strdup(uri)) != NULL)) {
+ ctx->type = type;
+ ctx->provctx = provctx;
+ return ctx;
+ }
+ free_file_ctx(ctx);
+ return NULL;
+}
+
+static OSSL_DECODER_CONSTRUCT file_load_construct;
+static OSSL_DECODER_CLEANUP file_load_cleanup;
+
+/*-
+ * Opening / attaching streams and directories
+ * -------------------------------------------
+ */
+
+/*
+ * Function to service both file_open() and file_attach()
+ *
+ *
+ */
+static struct file_ctx_st *file_open_stream(BIO *source, const char *uri,
+ void *provctx)
+{
+ struct file_ctx_st *ctx;
+
+ if ((ctx = new_file_ctx(IS_FILE, uri, provctx)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);
+ goto err;
+ }
+
+ ctx->_.file.file = source;
+
+ return ctx;
+ err:
+ free_file_ctx(ctx);
+ return NULL;
+}
+
+static void *file_open_dir(const char *path, const char *uri, void *provctx)
+{
+ struct file_ctx_st *ctx;
+
+ if ((ctx = new_file_ctx(IS_DIR, uri, provctx)) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_PROV_LIB);
+ return NULL;
+ }
+
+ ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, path);
+ ctx->_.dir.last_errno = errno;
+ if (ctx->_.dir.last_entry == NULL) {
+ if (ctx->_.dir.last_errno != 0) {
+ ERR_raise_data(ERR_LIB_SYS, ctx->_.dir.last_errno,
+ "Calling OPENSSL_DIR_read(\"%s\")", path);
+ goto err;
+ }
+ ctx->_.dir.end_reached = 1;
+ }
+ return ctx;
+ err:
+ file_close(ctx);
+ return NULL;
+}
+
+static void *file_open(void *provctx, const char *uri)
+{
+ struct file_ctx_st *ctx = NULL;
+ struct stat st;
+ struct {
+ const char *path;
+ unsigned int check_absolute:1;
+ } path_data[2];
+ size_t path_data_n = 0, i;
+ const char *path, *p = uri, *q;
+ BIO *bio;
+
+ ERR_set_mark();
+
+ /*
+ * First step, just take the URI as is.
+ */
+ path_data[path_data_n].check_absolute = 0;
+ path_data[path_data_n++].path = uri;
+
+ /*
+ * Second step, if the URI appears to start with the "file" scheme,
+ * extract the path and make that the second path to check.
+ * There's a special case if the URI also contains an authority, then
+ * the full URI shouldn't be used as a path anywhere.
+ */
+ if (CHECK_AND_SKIP_CASE_PREFIX(p, "file:")) {
+ q = p;
+ if (CHECK_AND_SKIP_CASE_PREFIX(q, "//")) {
+ path_data_n--; /* Invalidate using the full URI */
+ if (CHECK_AND_SKIP_CASE_PREFIX(q, "localhost/")
+ || CHECK_AND_SKIP_CASE_PREFIX(q, "/")) {
+ p = q - 1;
+ } else {
+ ERR_clear_last_mark();
+ ERR_raise(ERR_LIB_PROV, PROV_R_URI_AUTHORITY_UNSUPPORTED);
+ return NULL;
+ }
+ }
+
+ path_data[path_data_n].check_absolute = 1;
+#ifdef _WIN32
+ /* Windows "file:" URIs with a drive letter start with a '/' */
+ if (p[0] == '/' && p[2] == ':' && p[3] == '/') {
+ char c = tolower((unsigned char)p[1]);
+
+ if (c >= 'a' && c <= 'z') {
+ p++;
+ /* We know it's absolute, so no need to check */
+ path_data[path_data_n].check_absolute = 0;
+ }
+ }
+#endif
+ path_data[path_data_n++].path = p;
+ }
+
+
+ for (i = 0, path = NULL; path == NULL && i < path_data_n; i++) {
+ /*
+ * If the scheme "file" was an explicit part of the URI, the path must
+ * be absolute. So says RFC 8089
+ */
+ if (path_data[i].check_absolute && path_data[i].path[0] != '/') {
+ ERR_clear_last_mark();
+ ERR_raise_data(ERR_LIB_PROV, PROV_R_PATH_MUST_BE_ABSOLUTE,
+ "Given path=%s", path_data[i].path);
+ return NULL;
+ }
+
+ if (stat(path_data[i].path, &st) < 0) {
+ ERR_raise_data(ERR_LIB_SYS, errno,
+ "calling stat(%s)",
+ path_data[i].path);
+ } else {
+ path = path_data[i].path;
+ }
+ }
+ if (path == NULL) {
+ ERR_clear_last_mark();
+ return NULL;
+ }
+
+ /* Successfully found a working path, clear possible collected errors */
+ ERR_pop_to_mark();
+
+ if (S_ISDIR(st.st_mode))
+ ctx = file_open_dir(path, uri, provctx);
+ else if ((bio = BIO_new_file(path, "rb")) == NULL
+ || (ctx = file_open_stream(bio, uri, provctx)) == NULL)
+ BIO_free_all(bio);
+
+ return ctx;
+}
+
+void *file_attach(void *provctx, OSSL_CORE_BIO *cin)
+{
+ struct file_ctx_st *ctx;
+ BIO *new_bio = ossl_bio_new_from_core_bio(provctx, cin);
+
+ if (new_bio == NULL)
+ return NULL;
+
+ ctx = file_open_stream(new_bio, NULL, provctx);
+ if (ctx == NULL)
+ BIO_free(new_bio);
+ return ctx;
+}
+
+/*-
+ * Setting parameters
+ * ------------------
+ */
+
+static const OSSL_PARAM *file_settable_ctx_params(void *provctx)
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_int(OSSL_STORE_PARAM_EXPECT, NULL),
+ OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_INPUT_TYPE, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int file_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])
+{
+ struct file_ctx_st *ctx = loaderctx;
+ const OSSL_PARAM *p;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ if (ctx->type != IS_DIR) {
+ /* these parameters are ignored for directories */
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);
+ if (p != NULL) {
+ OPENSSL_free(ctx->_.file.propq);
+ ctx->_.file.propq = NULL;
+ if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.propq, 0))
+ return 0;
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_INPUT_TYPE);
+ if (p != NULL) {
+ OPENSSL_free(ctx->_.file.input_type);
+ ctx->_.file.input_type = NULL;
+ if (!OSSL_PARAM_get_utf8_string(p, &ctx->_.file.input_type, 0))
+ return 0;
+ }
+ }
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_EXPECT);
+ if (p != NULL && !OSSL_PARAM_get_int(p, &ctx->expected_type))
+ return 0;
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT);
+ if (p != NULL) {
+ const unsigned char *der = NULL;
+ size_t der_len = 0;
+ X509_NAME *x509_name;
+ unsigned long hash;
+ int ok;
+
+ if (ctx->type != IS_DIR) {
+ ERR_raise(ERR_LIB_PROV,
+ PROV_R_SEARCH_ONLY_SUPPORTED_FOR_DIRECTORIES);
+ return 0;
+ }
+
+ if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len)
+ || (x509_name = d2i_X509_NAME(NULL, &der, der_len)) == NULL)
+ return 0;
+ hash = X509_NAME_hash_ex(x509_name,
+ ossl_prov_ctx_get0_libctx(ctx->provctx), NULL,
+ &ok);
+ BIO_snprintf(ctx->_.dir.search_name, sizeof(ctx->_.dir.search_name),
+ "%08lx", hash);
+ X509_NAME_free(x509_name);
+ if (ok == 0)
+ return 0;
+ }
+ return 1;
+}
+
+/*-
+ * Loading an object from a stream
+ * -------------------------------
+ */
+
+struct file_load_data_st {
+ OSSL_CALLBACK *object_cb;
+ void *object_cbarg;
+};
+
+static int file_load_construct(OSSL_DECODER_INSTANCE *decoder_inst,
+ const OSSL_PARAM *params, void *construct_data)
+{
+ struct file_load_data_st *data = construct_data;
+
+ /*
+ * At some point, we may find it justifiable to recognise PKCS#12 and
+ * handle it specially here, making |file_load()| return pass its
+ * contents one piece at ta time, like |e_loader_attic.c| does.
+ *
+ * However, that currently means parsing them out, which converts the
+ * DER encoded PKCS#12 into a bunch of EVP_PKEYs and X509s, just to
+ * have to re-encode them into DER to create an object abstraction for
+ * each of them.
+ * It's much simpler (less churn) to pass on the object abstraction we
+ * get to the load_result callback and leave it to that one to do the
+ * work. If that's libcrypto code, we know that it has much better
+ * possibilities to handle the EVP_PKEYs and X509s without the extra
+ * churn.
+ */
+
+ return data->object_cb(params, data->object_cbarg);
+}
+
+void file_load_cleanup(void *construct_data)
+{
+ /* Nothing to do */
+}
+
+static int file_setup_decoders(struct file_ctx_st *ctx)
+{
+ OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);
+ const OSSL_ALGORITHM *to_algo = NULL;
+ const char *input_structure = NULL;
+ int ok = 0;
+
+ /* Setup for this session, so only if not already done */
+ if (ctx->_.file.decoderctx == NULL) {
+ if ((ctx->_.file.decoderctx = OSSL_DECODER_CTX_new()) == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ /* Make sure the input type is set */
+ if (!OSSL_DECODER_CTX_set_input_type(ctx->_.file.decoderctx,
+ ctx->_.file.input_type)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ /*
+ * Where applicable, set the outermost structure name.
+ * The goal is to avoid the STORE object types that are
+ * potentially password protected but aren't interesting
+ * for this load.
+ */
+ switch (ctx->expected_type) {
+ case OSSL_STORE_INFO_PUBKEY:
+ input_structure = "SubjectPublicKeyInfo";
+ if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,
+ input_structure)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ break;
+ case OSSL_STORE_INFO_PKEY:
+ /*
+ * The user's OSSL_STORE_INFO_PKEY covers PKCS#8, whether encrypted
+ * or not. The decoder will figure out whether decryption is
+ * applicable and fall back as necessary. We just need to indicate
+ * that it is OK to try and encrypt, which may involve a password
+ * prompt, so not done unless the data type is explicit, as we
+ * might then get a password prompt for a key when reading only
+ * certs from a file.
+ */
+ input_structure = "EncryptedPrivateKeyInfo";
+ if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,
+ input_structure)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ break;
+ case OSSL_STORE_INFO_CERT:
+ input_structure = "Certificate";
+ if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,
+ input_structure)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ break;
+ case OSSL_STORE_INFO_CRL:
+ input_structure = "CertificateList";
+ if (!OSSL_DECODER_CTX_set_input_structure(ctx->_.file.decoderctx,
+ input_structure)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (to_algo = ossl_any_to_obj_algorithm;
+ to_algo->algorithm_names != NULL;
+ to_algo++) {
+ OSSL_DECODER *to_obj = NULL;
+ OSSL_DECODER_INSTANCE *to_obj_inst = NULL;
+ const char *input_type;
+
+ /*
+ * Create the internal last resort decoder implementation
+ * together with a "decoder instance".
+ * The decoder doesn't need any identification or to be
+ * attached to any provider, since it's only used locally.
+ */
+ to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL);
+ if (to_obj != NULL)
+ to_obj_inst =
+ ossl_decoder_instance_new_forprov(to_obj, ctx->provctx,
+ input_structure);
+ OSSL_DECODER_free(to_obj);
+ if (to_obj_inst == NULL)
+ goto err;
+ /*
+ * The input type has to match unless, the input type is PEM
+ * and the decoder input type is DER, in which case we'll pick
+ * up additional decoders.
+ */
+ input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst);
+ if (ctx->_.file.input_type != NULL
+ && OPENSSL_strcasecmp(input_type, ctx->_.file.input_type) != 0
+ && (OPENSSL_strcasecmp(ctx->_.file.input_type, "PEM") != 0
+ || OPENSSL_strcasecmp(input_type, "der") != 0)) {
+ ossl_decoder_instance_free(to_obj_inst);
+ continue;
+ }
+
+ if (!ossl_decoder_ctx_add_decoder_inst(ctx->_.file.decoderctx,
+ to_obj_inst)) {
+ ossl_decoder_instance_free(to_obj_inst);
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ }
+ /* Add on the usual extra decoders */
+ if (!OSSL_DECODER_CTX_add_extra(ctx->_.file.decoderctx,
+ libctx, ctx->_.file.propq)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ /*
+ * Then install our constructor hooks, which just passes decoded
+ * data to the load callback
+ */
+ if (!OSSL_DECODER_CTX_set_construct(ctx->_.file.decoderctx,
+ file_load_construct)
+ || !OSSL_DECODER_CTX_set_cleanup(ctx->_.file.decoderctx,
+ file_load_cleanup)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ }
+
+ ok = 1;
+ err:
+ return ok;
+}
+
+static int file_load_file(struct file_ctx_st *ctx,
+ OSSL_CALLBACK *object_cb, void *object_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct file_load_data_st data;
+ int ret, err;
+
+ /* Setup the decoders (one time shot per session */
+
+ if (!file_setup_decoders(ctx))
+ return 0;
+
+ /* Setup for this object */
+
+ data.object_cb = object_cb;
+ data.object_cbarg = object_cbarg;
+ OSSL_DECODER_CTX_set_construct_data(ctx->_.file.decoderctx, &data);
+ OSSL_DECODER_CTX_set_passphrase_cb(ctx->_.file.decoderctx, pw_cb, pw_cbarg);
+
+ /* Launch */
+
+ ERR_set_mark();
+ ret = OSSL_DECODER_from_bio(ctx->_.file.decoderctx, ctx->_.file.file);
+ if (BIO_eof(ctx->_.file.file)
+ && ((err = ERR_peek_last_error()) != 0)
+ && ERR_GET_LIB(err) == ERR_LIB_OSSL_DECODER
+ && ERR_GET_REASON(err) == ERR_R_UNSUPPORTED)
+ ERR_pop_to_mark();
+ else
+ ERR_clear_last_mark();
+ return ret;
+}
+
+/*-
+ * Loading a name object from a directory
+ * --------------------------------------
+ */
+
+static char *file_name_to_uri(struct file_ctx_st *ctx, const char *name)
+{
+ char *data = NULL;
+
+ assert(name != NULL);
+ {
+ const char *pathsep = ossl_ends_with_dirsep(ctx->uri) ? "" : "/";
+ long calculated_length = strlen(ctx->uri) + strlen(pathsep)
+ + strlen(name) + 1 /* \0 */;
+
+ data = OPENSSL_zalloc(calculated_length);
+ if (data == NULL)
+ return NULL;
+
+ OPENSSL_strlcat(data, ctx->uri, calculated_length);
+ OPENSSL_strlcat(data, pathsep, calculated_length);
+ OPENSSL_strlcat(data, name, calculated_length);
+ }
+ return data;
+}
+
+static int file_name_check(struct file_ctx_st *ctx, const char *name)
+{
+ const char *p = NULL;
+ size_t len = strlen(ctx->_.dir.search_name);
+
+ /* If there are no search criteria, all names are accepted */
+ if (ctx->_.dir.search_name[0] == '\0')
+ return 1;
+
+ /* If the expected type isn't supported, no name is accepted */
+ if (ctx->expected_type != 0
+ && ctx->expected_type != OSSL_STORE_INFO_CERT
+ && ctx->expected_type != OSSL_STORE_INFO_CRL)
+ return 0;
+
+ /*
+ * First, check the basename
+ */
+ if (OPENSSL_strncasecmp(name, ctx->_.dir.search_name, len) != 0
+ || name[len] != '.')
+ return 0;
+ p = &name[len + 1];
+
+ /*
+ * Then, if the expected type is a CRL, check that the extension starts
+ * with 'r'
+ */
+ if (*p == 'r') {
+ p++;
+ if (ctx->expected_type != 0
+ && ctx->expected_type != OSSL_STORE_INFO_CRL)
+ return 0;
+ } else if (ctx->expected_type == OSSL_STORE_INFO_CRL) {
+ return 0;
+ }
+
+ /*
+ * Last, check that the rest of the extension is a decimal number, at
+ * least one digit long.
+ */
+ if (!isdigit((unsigned char)*p))
+ return 0;
+ while (isdigit((unsigned char)*p))
+ p++;
+
+#ifdef __VMS
+ /*
+ * One extra step here, check for a possible generation number.
+ */
+ if (*p == ';')
+ for (p++; *p != '\0'; p++)
+ if (!ossl_isdigit((unsigned char)*p))
+ break;
+#endif
+
+ /*
+ * If we've reached the end of the string at this point, we've successfully
+ * found a fitting file name.
+ */
+ return *p == '\0';
+}
+
+static int file_load_dir_entry(struct file_ctx_st *ctx,
+ OSSL_CALLBACK *object_cb, void *object_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ /* Prepare as much as possible in advance */
+ static const int object_type = OSSL_OBJECT_NAME;
+ OSSL_PARAM object[] = {
+ OSSL_PARAM_int(OSSL_OBJECT_PARAM_TYPE, (int *)&object_type),
+ OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA, NULL, 0),
+ OSSL_PARAM_END
+ };
+ char *newname = NULL;
+ int ok;
+
+ /* Loop until we get an error or until we have a suitable name */
+ do {
+ if (ctx->_.dir.last_entry == NULL) {
+ if (!ctx->_.dir.end_reached) {
+ assert(ctx->_.dir.last_errno != 0);
+ ERR_raise(ERR_LIB_SYS, ctx->_.dir.last_errno);
+ }
+ /* file_eof() will tell if EOF was reached */
+ return 0;
+ }
+
+ /* flag acceptable names */
+ if (ctx->_.dir.last_entry[0] != '.'
+ && file_name_check(ctx, ctx->_.dir.last_entry)) {
+
+ /* If we can't allocate the new name, we fail */
+ if ((newname =
+ file_name_to_uri(ctx, ctx->_.dir.last_entry)) == NULL)
+ return 0;
+ }
+
+ /*
+ * On the first call (with a NULL context), OPENSSL_DIR_read()
+ * cares about the second argument. On the following calls, it
+ * only cares that it isn't NULL. Therefore, we can safely give
+ * it our URI here.
+ */
+ ctx->_.dir.last_entry = OPENSSL_DIR_read(&ctx->_.dir.ctx, ctx->uri);
+ ctx->_.dir.last_errno = errno;
+ if (ctx->_.dir.last_entry == NULL && ctx->_.dir.last_errno == 0)
+ ctx->_.dir.end_reached = 1;
+ } while (newname == NULL);
+
+ object[1].data = newname;
+ object[1].data_size = strlen(newname);
+ ok = object_cb(object, object_cbarg);
+ OPENSSL_free(newname);
+ return ok;
+}
+
+/*-
+ * Loading, local dispatcher
+ * -------------------------
+ */
+
+static int file_load(void *loaderctx,
+ OSSL_CALLBACK *object_cb, void *object_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct file_ctx_st *ctx = loaderctx;
+
+ switch (ctx->type) {
+ case IS_FILE:
+ return file_load_file(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);
+ case IS_DIR:
+ return
+ file_load_dir_entry(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg);
+ default:
+ break;
+ }
+
+ /* ctx->type has an unexpected value */
+ assert(0);
+ return 0;
+}
+
+/*-
+ * Eof detection and closing
+ * -------------------------
+ */
+
+static int file_eof(void *loaderctx)
+{
+ struct file_ctx_st *ctx = loaderctx;
+
+ switch (ctx->type) {
+ case IS_DIR:
+ return ctx->_.dir.end_reached;
+ case IS_FILE:
+ /*
+ * BIO_pending() checks any filter BIO.
+ * BIO_eof() checks the source BIO.
+ */
+ return !BIO_pending(ctx->_.file.file)
+ && BIO_eof(ctx->_.file.file);
+ }
+
+ /* ctx->type has an unexpected value */
+ assert(0);
+ return 1;
+}
+
+static int file_close_dir(struct file_ctx_st *ctx)
+{
+ if (ctx->_.dir.ctx != NULL)
+ OPENSSL_DIR_end(&ctx->_.dir.ctx);
+ free_file_ctx(ctx);
+ return 1;
+}
+
+static int file_close_stream(struct file_ctx_st *ctx)
+{
+ /*
+ * This frees either the provider BIO filter (for file_attach()) OR
+ * the allocated file BIO (for file_open()).
+ */
+ BIO_free(ctx->_.file.file);
+ ctx->_.file.file = NULL;
+
+ free_file_ctx(ctx);
+ return 1;
+}
+
+static int file_close(void *loaderctx)
+{
+ struct file_ctx_st *ctx = loaderctx;
+
+ switch (ctx->type) {
+ case IS_DIR:
+ return file_close_dir(ctx);
+ case IS_FILE:
+ return file_close_stream(ctx);
+ }
+
+ /* ctx->type has an unexpected value */
+ assert(0);
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_file_store_functions[] = {
+ { OSSL_FUNC_STORE_OPEN, (void (*)(void))file_open },
+ { OSSL_FUNC_STORE_ATTACH, (void (*)(void))file_attach },
+ { OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS,
+ (void (*)(void))file_settable_ctx_params },
+ { OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))file_set_ctx_params },
+ { OSSL_FUNC_STORE_LOAD, (void (*)(void))file_load },
+ { OSSL_FUNC_STORE_EOF, (void (*)(void))file_eof },
+ { OSSL_FUNC_STORE_CLOSE, (void (*)(void))file_close },
+ OSSL_DISPATCH_END,
+};
diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c b/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c
new file mode 100644
index 000000000000..bec6091608a9
--- /dev/null
+++ b/crypto/openssl/providers/implementations/storemgmt/file_store_any2obj.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright 2020-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+/*
+ * This is a decoder that's completely internal to the 'file:' store
+ * implementation. Only code in file_store.c know about this one. Because
+ * of this close relationship, we can cut certain corners, such as making
+ * assumptions about the "provider context", which is currently simply the
+ * provider context that the file_store.c code operates within.
+ *
+ * All this does is to read known binary encodings (currently: DER, MSBLOB,
+ * PVK) from the input if it can, and passes it on to the data callback as
+ * an object abstraction, leaving it to the callback to figure out what it
+ * actually is.
+ *
+ * This MUST be made the last decoder in a chain, leaving it to other more
+ * specialized decoders to recognise and process their stuff first.
+ */
+
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
+#include <openssl/err.h>
+#include <openssl/asn1err.h>
+#include <openssl/params.h>
+#include "internal/asn1.h"
+#include "internal/sizes.h"
+#include "crypto/pem.h" /* For internal PVK and "blob" headers */
+#include "prov/bio.h"
+#include "file_store_local.h"
+
+/*
+ * newctx and freectx are not strictly necessary. However, the method creator,
+ * ossl_decoder_from_algorithm(), demands that they exist, so we make sure to
+ * oblige.
+ */
+
+static OSSL_FUNC_decoder_newctx_fn any2obj_newctx;
+static OSSL_FUNC_decoder_freectx_fn any2obj_freectx;
+
+struct any2obj_ctx_st {
+ PROV_CTX *provctx;
+ char data_structure[OSSL_MAX_CODEC_STRUCT_SIZE];
+};
+
+static void *any2obj_newctx(void *provctx)
+{
+ struct any2obj_ctx_st *ctx = OPENSSL_zalloc(sizeof(*ctx));
+
+ if (ctx != NULL)
+ ctx->provctx = provctx;
+ return ctx;
+}
+
+static void any2obj_freectx(void *ctx)
+{
+ OPENSSL_free(ctx);
+}
+
+static int any2obj_set_ctx_params(void *vctx, const OSSL_PARAM params[])
+{
+ struct any2obj_ctx_st *ctx = vctx;
+ const OSSL_PARAM *p;
+ char *str;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_OBJECT_PARAM_DATA_STRUCTURE);
+ str = ctx->data_structure;
+ if (p != NULL
+ && !OSSL_PARAM_get_utf8_string(p, &str, sizeof(ctx->data_structure)))
+ return 0;
+
+ return 1;
+}
+
+static const OSSL_PARAM *any2obj_settable_ctx_params(ossl_unused void *provctx)
+{
+ static const OSSL_PARAM settables[] = {
+ OSSL_PARAM_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return settables;
+}
+
+static int any2obj_decode_final(void *vctx, int objtype, const char *input_type,
+ const char *data_type, BUF_MEM *mem,
+ OSSL_CALLBACK *data_cb, void *data_cbarg)
+{
+ struct any2obj_ctx_st *ctx = vctx;
+ /*
+ * 1 indicates that we successfully decoded something, or not at all.
+ * Ending up "empty handed" is not an error.
+ */
+ int ok = 1;
+
+ if (mem != NULL) {
+ OSSL_PARAM params[6], *p = params;
+
+ if (data_type != NULL)
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,
+ (char *)data_type, 0);
+ if (input_type != NULL)
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_INPUT_TYPE,
+ (char *)input_type, 0);
+ if (*ctx->data_structure != '\0')
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_STRUCTURE,
+ (char *)ctx->data_structure, 0);
+ *p++ = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE, &objtype);
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_DATA,
+ mem->data, mem->length);
+ *p = OSSL_PARAM_construct_end();
+
+ ok = data_cb(params, data_cbarg);
+ BUF_MEM_free(mem);
+ }
+ return ok;
+}
+
+static OSSL_FUNC_decoder_decode_fn der2obj_decode;
+static int der2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct any2obj_ctx_st *ctx = vctx;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ BUF_MEM *mem = NULL;
+ int ok;
+
+ if (in == NULL)
+ return 0;
+
+ ERR_set_mark();
+ ok = (asn1_d2i_read_bio(in, &mem) >= 0);
+ ERR_pop_to_mark();
+ if (!ok && mem != NULL) {
+ BUF_MEM_free(mem);
+ mem = NULL;
+ }
+ BIO_free(in);
+
+ /* any2obj_decode_final() frees |mem| for us */
+ return any2obj_decode_final(ctx, OSSL_OBJECT_UNKNOWN, NULL, NULL, mem,
+ data_cb, data_cbarg);
+}
+
+static OSSL_FUNC_decoder_decode_fn msblob2obj_decode;
+static int msblob2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct any2obj_ctx_st *ctx = vctx;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ BUF_MEM *mem = NULL;
+ size_t mem_len = 0, mem_want;
+ const unsigned char *p;
+ unsigned int bitlen, magic;
+ int isdss = -1;
+ int ispub = -1;
+ int ok = 0;
+
+ if (in == NULL)
+ goto err;
+
+ mem_want = 16; /* The size of the MSBLOB header */
+ if ((mem = BUF_MEM_new()) == NULL
+ || !BUF_MEM_grow(mem, mem_want)) {
+ ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+ goto err;
+ }
+
+ ERR_set_mark();
+ ok = BIO_read(in, &mem->data[0], mem_want) == (int)mem_want;
+ mem_len += mem_want;
+ ERR_pop_to_mark();
+ if (!ok)
+ goto next;
+
+
+ ERR_set_mark();
+ p = (unsigned char *)&mem->data[0];
+ ok = ossl_do_blob_header(&p, 16, &magic, &bitlen, &isdss, &ispub) > 0;
+ ERR_pop_to_mark();
+ if (!ok)
+ goto next;
+
+ ok = 0;
+ mem_want = ossl_blob_length(bitlen, isdss, ispub);
+ if (!BUF_MEM_grow(mem, mem_len + mem_want)) {
+ ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+ goto err;
+ }
+
+ ERR_set_mark();
+ ok = BIO_read(in, &mem->data[mem_len], mem_want) == (int)mem_want;
+ mem_len += mem_want;
+ ERR_pop_to_mark();
+
+ next:
+ /* Free resources we no longer need. */
+ BIO_free(in);
+ if (!ok && mem != NULL) {
+ BUF_MEM_free(mem);
+ mem = NULL;
+ }
+
+ /* any2obj_decode_final() frees |mem| for us */
+ return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "msblob",
+ isdss ? "DSA" : "RSA", mem,
+ data_cb, data_cbarg);
+
+ err:
+ BIO_free(in);
+ BUF_MEM_free(mem);
+ return 0;
+}
+
+static OSSL_FUNC_decoder_decode_fn pvk2obj_decode;
+static int pvk2obj_decode(void *vctx, OSSL_CORE_BIO *cin, int selection,
+ OSSL_CALLBACK *data_cb, void *data_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ struct any2obj_ctx_st *ctx = vctx;
+ BIO *in = ossl_bio_new_from_core_bio(ctx->provctx, cin);
+ BUF_MEM *mem = NULL;
+ size_t mem_len = 0, mem_want;
+ const unsigned char *p;
+ unsigned int saltlen, keylen;
+ int ok = 0, isdss = -1;
+
+ if (in == NULL)
+ goto err;
+
+ mem_want = 24; /* The size of the PVK header */
+ if ((mem = BUF_MEM_new()) == NULL
+ || !BUF_MEM_grow(mem, mem_want)) {
+ ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+ goto err;
+ }
+
+ ERR_set_mark();
+ ok = BIO_read(in, &mem->data[0], mem_want) == (int)mem_want;
+ mem_len += mem_want;
+ ERR_pop_to_mark();
+ if (!ok)
+ goto next;
+
+
+ ERR_set_mark();
+ p = (unsigned char *)&mem->data[0];
+ ok = ossl_do_PVK_header(&p, 24, 0, &isdss, &saltlen, &keylen) > 0;
+ ERR_pop_to_mark();
+ if (!ok)
+ goto next;
+
+ ok = 0;
+ mem_want = saltlen + keylen;
+ if (!BUF_MEM_grow(mem, mem_len + mem_want)) {
+ ERR_raise(ERR_LIB_PEM, ERR_R_BUF_LIB);
+ goto err;
+ }
+
+ ERR_set_mark();
+ ok = BIO_read(in, &mem->data[mem_len], mem_want) == (int)mem_want;
+ mem_len += mem_want;
+ ERR_pop_to_mark();
+
+ next:
+ /* Free resources we no longer need. */
+ BIO_free(in);
+ if (!ok && mem != NULL) {
+ BUF_MEM_free(mem);
+ mem = NULL;
+ }
+
+ /* any2obj_decode_final() frees |mem| for us */
+ return any2obj_decode_final(ctx, OSSL_OBJECT_PKEY, "pvk",
+ ok ? (isdss ? "DSA" : "RSA") : NULL, mem,
+ data_cb, data_cbarg);
+
+ err:
+ BIO_free(in);
+ BUF_MEM_free(mem);
+ return 0;
+}
+
+#define MAKE_DECODER(fromtype, objtype) \
+ static const OSSL_DISPATCH fromtype##_to_obj_decoder_functions[] = { \
+ { OSSL_FUNC_DECODER_NEWCTX, (void (*)(void))any2obj_newctx }, \
+ { OSSL_FUNC_DECODER_FREECTX, (void (*)(void))any2obj_freectx }, \
+ { OSSL_FUNC_DECODER_DECODE, (void (*)(void))fromtype##2obj_decode }, \
+ { OSSL_FUNC_DECODER_SETTABLE_CTX_PARAMS, \
+ (void (*)(void))any2obj_settable_ctx_params }, \
+ { OSSL_FUNC_DECODER_SET_CTX_PARAMS, \
+ (void (*)(void))any2obj_set_ctx_params }, \
+ OSSL_DISPATCH_END \
+ }
+
+MAKE_DECODER(der, OSSL_OBJECT_UNKNOWN);
+MAKE_DECODER(msblob, OSSL_OBJECT_PKEY);
+MAKE_DECODER(pvk, OSSL_OBJECT_PKEY);
+
+const OSSL_ALGORITHM ossl_any_to_obj_algorithm[] = {
+ { "obj", "input=DER", der_to_obj_decoder_functions },
+ { "obj", "input=MSBLOB", msblob_to_obj_decoder_functions },
+ { "obj", "input=PVK", pvk_to_obj_decoder_functions },
+ { NULL, }
+};
diff --git a/crypto/openssl/providers/implementations/storemgmt/file_store_local.h b/crypto/openssl/providers/implementations/storemgmt/file_store_local.h
new file mode 100644
index 000000000000..b7d9e585163e
--- /dev/null
+++ b/crypto/openssl/providers/implementations/storemgmt/file_store_local.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+extern const OSSL_ALGORITHM ossl_any_to_obj_algorithm[];
+
diff --git a/crypto/openssl/providers/implementations/storemgmt/winstore_store.c b/crypto/openssl/providers/implementations/storemgmt/winstore_store.c
new file mode 100644
index 000000000000..59a86a105e9f
--- /dev/null
+++ b/crypto/openssl/providers/implementations/storemgmt/winstore_store.c
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2022-2025 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License"). You may not use
+ * this file except in compliance with the License. You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+#include <openssl/store.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/core_object.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/params.h>
+#include <openssl/decoder.h>
+#include <openssl/proverr.h>
+#include <openssl/store.h> /* The OSSL_STORE_INFO type numbers */
+#include "internal/cryptlib.h"
+#include "internal/o_dir.h"
+#include "crypto/decoder.h"
+#include "crypto/ctype.h" /* ossl_isdigit() */
+#include "prov/implementations.h"
+#include "prov/providercommon.h"
+#include "prov/bio.h"
+#include "file_store_local.h"
+#ifdef __CYGWIN__
+# include <windows.h>
+#endif
+#include <wincrypt.h>
+
+enum {
+ STATE_IDLE,
+ STATE_READ,
+ STATE_EOF,
+};
+
+struct winstore_ctx_st {
+ void *provctx;
+ char *propq;
+ unsigned char *subject;
+ size_t subject_len;
+
+ HCERTSTORE win_store;
+ const CERT_CONTEXT *win_ctx;
+ int state;
+
+ OSSL_DECODER_CTX *dctx;
+};
+
+static void winstore_win_reset(struct winstore_ctx_st *ctx)
+{
+ if (ctx->win_ctx != NULL) {
+ CertFreeCertificateContext(ctx->win_ctx);
+ ctx->win_ctx = NULL;
+ }
+
+ ctx->state = STATE_IDLE;
+}
+
+static void winstore_win_advance(struct winstore_ctx_st *ctx)
+{
+ CERT_NAME_BLOB name = {0};
+
+ if (ctx->state == STATE_EOF)
+ return;
+
+ name.cbData = ctx->subject_len;
+ name.pbData = ctx->subject;
+
+ ctx->win_ctx = (name.cbData == 0 ? NULL :
+ CertFindCertificateInStore(ctx->win_store,
+ X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+ 0, CERT_FIND_SUBJECT_NAME,
+ &name, ctx->win_ctx));
+
+ ctx->state = (ctx->win_ctx == NULL) ? STATE_EOF : STATE_READ;
+}
+
+static void *winstore_open(void *provctx, const char *uri)
+{
+ struct winstore_ctx_st *ctx = NULL;
+
+ if (!HAS_CASE_PREFIX(uri, "org.openssl.winstore:"))
+ return NULL;
+
+ ctx = OPENSSL_zalloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return NULL;
+
+ ctx->provctx = provctx;
+ ctx->win_store = CertOpenSystemStoreW(0, L"ROOT");
+ if (ctx->win_store == NULL) {
+ OPENSSL_free(ctx);
+ return NULL;
+ }
+
+ winstore_win_reset(ctx);
+ return ctx;
+}
+
+static void *winstore_attach(void *provctx, OSSL_CORE_BIO *cin)
+{
+ return NULL; /* not supported */
+}
+
+static const OSSL_PARAM *winstore_settable_ctx_params(void *loaderctx, const OSSL_PARAM params[])
+{
+ static const OSSL_PARAM known_settable_ctx_params[] = {
+ OSSL_PARAM_octet_string(OSSL_STORE_PARAM_SUBJECT, NULL, 0),
+ OSSL_PARAM_utf8_string(OSSL_STORE_PARAM_PROPERTIES, NULL, 0),
+ OSSL_PARAM_END
+ };
+ return known_settable_ctx_params;
+}
+
+static int winstore_set_ctx_params(void *loaderctx, const OSSL_PARAM params[])
+{
+ struct winstore_ctx_st *ctx = loaderctx;
+ const OSSL_PARAM *p;
+ int do_reset = 0;
+
+ if (ossl_param_is_empty(params))
+ return 1;
+
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_PROPERTIES);
+ if (p != NULL) {
+ do_reset = 1;
+ OPENSSL_free(ctx->propq);
+ ctx->propq = NULL;
+ if (!OSSL_PARAM_get_utf8_string(p, &ctx->propq, 0))
+ return 0;
+ }
+
+ p = OSSL_PARAM_locate_const(params, OSSL_STORE_PARAM_SUBJECT);
+ if (p != NULL) {
+ const unsigned char *der = NULL;
+ size_t der_len = 0;
+
+ if (!OSSL_PARAM_get_octet_string_ptr(p, (const void **)&der, &der_len))
+ return 0;
+
+ do_reset = 1;
+
+ OPENSSL_free(ctx->subject);
+
+ ctx->subject = OPENSSL_malloc(der_len);
+ if (ctx->subject == NULL) {
+ ctx->subject_len = 0;
+ return 0;
+ }
+
+ ctx->subject_len = der_len;
+ memcpy(ctx->subject, der, der_len);
+ }
+
+ if (do_reset) {
+ winstore_win_reset(ctx);
+ winstore_win_advance(ctx);
+ }
+
+ return 1;
+}
+
+struct load_data_st {
+ OSSL_CALLBACK *object_cb;
+ void *object_cbarg;
+};
+
+static int load_construct(OSSL_DECODER_INSTANCE *decoder_inst,
+ const OSSL_PARAM *params, void *construct_data)
+{
+ struct load_data_st *data = construct_data;
+ return data->object_cb(params, data->object_cbarg);
+}
+
+static void load_cleanup(void *construct_data)
+{
+ /* No-op. */
+}
+
+static int setup_decoder(struct winstore_ctx_st *ctx)
+{
+ OSSL_LIB_CTX *libctx = ossl_prov_ctx_get0_libctx(ctx->provctx);
+ const OSSL_ALGORITHM *to_algo = NULL;
+ const char *input_structure = NULL;
+
+ if (ctx->dctx != NULL)
+ return 1;
+
+ ctx->dctx = OSSL_DECODER_CTX_new();
+ if (ctx->dctx == NULL) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ return 0;
+ }
+
+ if (!OSSL_DECODER_CTX_set_input_type(ctx->dctx, "DER")) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ input_structure = "Certificate";
+ if (!OSSL_DECODER_CTX_set_input_structure(ctx->dctx, input_structure)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ for (to_algo = ossl_any_to_obj_algorithm;
+ to_algo->algorithm_names != NULL;
+ to_algo++) {
+ OSSL_DECODER *to_obj = NULL;
+ OSSL_DECODER_INSTANCE *to_obj_inst = NULL;
+ const char *input_type;
+
+ /*
+ * Create the internal last resort decoder implementation
+ * together with a "decoder instance".
+ * The decoder doesn't need any identification or to be
+ * attached to any provider, since it's only used locally.
+ */
+ to_obj = ossl_decoder_from_algorithm(0, to_algo, NULL);
+ if (to_obj != NULL)
+ to_obj_inst = ossl_decoder_instance_new_forprov(to_obj, ctx->provctx,
+ input_structure);
+
+ OSSL_DECODER_free(to_obj);
+ if (to_obj_inst == NULL)
+ goto err;
+
+ /*
+ * The input type has to be DER
+ */
+ input_type = OSSL_DECODER_INSTANCE_get_input_type(to_obj_inst);
+ if (OPENSSL_strcasecmp(input_type, "DER") != 0) {
+ ossl_decoder_instance_free(to_obj_inst);
+ continue;
+ }
+
+ if (!ossl_decoder_ctx_add_decoder_inst(ctx->dctx,
+ to_obj_inst)) {
+ ossl_decoder_instance_free(to_obj_inst);
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+ }
+
+ if (!OSSL_DECODER_CTX_add_extra(ctx->dctx, libctx, ctx->propq)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ if (!OSSL_DECODER_CTX_set_construct(ctx->dctx, load_construct)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ if (!OSSL_DECODER_CTX_set_cleanup(ctx->dctx, load_cleanup)) {
+ ERR_raise(ERR_LIB_PROV, ERR_R_OSSL_DECODER_LIB);
+ goto err;
+ }
+
+ return 1;
+
+err:
+ OSSL_DECODER_CTX_free(ctx->dctx);
+ ctx->dctx = NULL;
+ return 0;
+}
+
+static int winstore_load_using(struct winstore_ctx_st *ctx,
+ OSSL_CALLBACK *object_cb, void *object_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
+ const void *der, size_t der_len)
+{
+ struct load_data_st data;
+ const unsigned char *der_ = der;
+ size_t der_len_ = der_len;
+
+ if (setup_decoder(ctx) == 0)
+ return 0;
+
+ data.object_cb = object_cb;
+ data.object_cbarg = object_cbarg;
+
+ OSSL_DECODER_CTX_set_construct_data(ctx->dctx, &data);
+ OSSL_DECODER_CTX_set_passphrase_cb(ctx->dctx, pw_cb, pw_cbarg);
+
+ if (OSSL_DECODER_from_data(ctx->dctx, &der_, &der_len_) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int winstore_load(void *loaderctx,
+ OSSL_CALLBACK *object_cb, void *object_cbarg,
+ OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg)
+{
+ int ret = 0;
+ struct winstore_ctx_st *ctx = loaderctx;
+
+ if (ctx->state != STATE_READ)
+ return 0;
+
+ ret = winstore_load_using(ctx, object_cb, object_cbarg, pw_cb, pw_cbarg,
+ ctx->win_ctx->pbCertEncoded,
+ ctx->win_ctx->cbCertEncoded);
+
+ if (ret == 1)
+ winstore_win_advance(ctx);
+
+ return ret;
+}
+
+static int winstore_eof(void *loaderctx)
+{
+ struct winstore_ctx_st *ctx = loaderctx;
+
+ return ctx->state != STATE_READ;
+}
+
+static int winstore_close(void *loaderctx)
+{
+ struct winstore_ctx_st *ctx = loaderctx;
+
+ winstore_win_reset(ctx);
+ CertCloseStore(ctx->win_store, 0);
+ OSSL_DECODER_CTX_free(ctx->dctx);
+ OPENSSL_free(ctx->propq);
+ OPENSSL_free(ctx->subject);
+ OPENSSL_free(ctx);
+ return 1;
+}
+
+const OSSL_DISPATCH ossl_winstore_store_functions[] = {
+ { OSSL_FUNC_STORE_OPEN, (void (*)(void))winstore_open },
+ { OSSL_FUNC_STORE_ATTACH, (void (*)(void))winstore_attach },
+ { OSSL_FUNC_STORE_SETTABLE_CTX_PARAMS, (void (*)(void))winstore_settable_ctx_params },
+ { OSSL_FUNC_STORE_SET_CTX_PARAMS, (void (*)(void))winstore_set_ctx_params },
+ { OSSL_FUNC_STORE_LOAD, (void (*)(void))winstore_load },
+ { OSSL_FUNC_STORE_EOF, (void (*)(void))winstore_eof },
+ { OSSL_FUNC_STORE_CLOSE, (void (*)(void))winstore_close },
+ OSSL_DISPATCH_END,
+};