9#include <botan/internal/tpm2_crypto_backend_impl.h>
11#include <botan/cipher_mode.h>
12#include <botan/hash.h>
14#include <botan/mem_ops.h>
15#include <botan/pubkey.h>
16#include <botan/tpm2_context.h>
17#include <botan/tpm2_crypto_backend.h>
18#include <botan/tpm2_key.h>
20#if defined(BOTAN_HAS_RSA)
21 #include <botan/rsa.h>
24#if defined(BOTAN_HAS_ECDH)
25 #include <botan/ecdh.h>
28#include <botan/internal/enc_padding.h>
29#include <botan/internal/fmt.h>
30#include <botan/internal/mem_utils.h>
31#include <botan/internal/tpm2_algo_mappings.h>
32#include <botan/internal/tpm2_util.h>
34#if defined(BOTAN_HAS_EME_OAEP)
35 #include <botan/internal/oaep.h>
38#include <tss2/tss2_esys.h>
39#include <tss2/tss2_mu.h>
43#if defined(BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS)
49 std::variant<std::unique_ptr<Botan::HashFunction>, std::unique_ptr<Botan::MessageAuthenticationCode>>;
59typedef struct ESYS_CRYPTO_CONTEXT_BLOB {
69 requires std::constructible_from<DigestObject, std::unique_ptr<T>>
70[[nodiscard]] std::optional<std::reference_wrapper<T>> get(DigestCallbackState* blob)
noexcept {
75 auto* ctx = std::get_if<std::unique_ptr<T>>(&blob->ctx);
80 return {std::ref(**ctx)};
84 requires std::constructible_from<DigestObject, std::unique_ptr<T>>
85[[nodiscard]] std::optional<std::reference_wrapper<T>> get(DigestCallbackState** blob)
noexcept {
94[[nodiscard]] std::optional<std::reference_wrapper<Botan::TPM2::CryptoCallbackState>> get(
void* userdata)
noexcept {
107template <std::invocable<> F>
108 requires std::same_as<std::invoke_result_t<F>,
TSS2_RC>
109[[nodiscard]]
TSS2_RC thunk(F f)
noexcept {
113 return TSS2_ESYS_RC_BAD_VALUE;
115 return TSS2_ESYS_RC_BAD_SEQUENCE;
117 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
119 return TSS2_ESYS_RC_MALFORMED_RESPONSE;
121 return TSS2_ESYS_RC_GENERAL_FAILURE;
123 return TSS2_ESYS_RC_GENERAL_FAILURE;
133 TPM2_ALG_ID tpm_sym_alg,
134 TPMI_AES_KEY_BITS key_bits,
135 TPM2_ALG_ID tpm_mode,
138 const uint8_t* iv)
noexcept {
142 : TSS2_ESYS_RC_NO_DECRYPT_PARAM;
146 if(buffer ==
nullptr && buffer_size != 0) {
147 return TSS2_ESYS_RC_BAD_VALUE;
151 .algorithm = tpm_sym_alg,
152 .keyBits = {.sym = key_bits},
153 .mode = {.sym = tpm_mode},
156 return TSS2_ESYS_RC_NOT_SUPPORTED;
161 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
166 if(cipher->authenticated()) {
167 return TSS2_ESYS_RC_INSUFFICIENT_BUFFER;
171 const size_t keylength =
static_cast<size_t>(key_bits) / 8;
172 if(!cipher->valid_keylength(keylength)) {
173 return TSS2_ESYS_RC_BAD_VALUE;
176 const auto s_data = std::span{buffer, buffer_size};
177 const auto s_key = std::span{key, keylength};
178 const auto s_iv = [&]() -> std::span<const uint8_t> {
180 return {iv, cipher->default_nonce_length()};
186 cipher->set_key(s_key);
188 cipher->process(s_data);
189 return TSS2_RC_SUCCESS;
204TSS2_RC hash_start(ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
void* userdata) {
207 if(context ==
nullptr) {
208 return TSS2_ESYS_RC_BAD_REFERENCE;
213 return TSS2_ESYS_RC_NOT_SUPPORTED;
217 if(hash ==
nullptr) {
218 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
222 *context =
new DigestCallbackState{std::move(hash)};
223 return TSS2_RC_SUCCESS;
238TSS2_RC hash_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
241 const auto hash = get<Botan::HashFunction>(context);
243 return TSS2_ESYS_RC_BAD_REFERENCE;
247 if(buffer ==
nullptr && size != 0) {
248 return TSS2_ESYS_RC_BAD_VALUE;
251 hash->get().update(std::span{buffer, size});
252 return TSS2_RC_SUCCESS;
267TSS2_RC hash_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
269 if(size !=
nullptr) {
274 auto hash = get<Botan::HashFunction>(context);
275 if(!hash || buffer ==
nullptr) {
276 return TSS2_ESYS_RC_BAD_REFERENCE;
279 const auto digest_size = hash->get().output_length();
280 hash->get().final(std::span{buffer, digest_size});
281 if(size !=
nullptr) {
288 return TSS2_RC_SUCCESS;
298void hash_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
300 if(context !=
nullptr) {
320 ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
const uint8_t* key,
size_t size,
void* userdata) {
324 return TSS2_ESYS_RC_BAD_REFERENCE;
329 return TSS2_ESYS_RC_NOT_SUPPORTED;
333 if(hmac ==
nullptr) {
334 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
337 hmac->set_key(std::span{key, size});
340 *context =
new DigestCallbackState{std::move(hmac)};
341 return TSS2_RC_SUCCESS;
356TSS2_RC hmac_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
359 auto hmac = get<Botan::MessageAuthenticationCode>(context);
361 return TSS2_ESYS_RC_BAD_REFERENCE;
365 if(buffer ==
nullptr && size != 0) {
366 return TSS2_ESYS_RC_BAD_VALUE;
369 hmac->get().update(std::span{buffer, size});
370 return TSS2_RC_SUCCESS;
385TSS2_RC hmac_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
387 if(size !=
nullptr) {
392 auto hmac = get<Botan::MessageAuthenticationCode>(context);
393 if(!hmac || buffer ==
nullptr) {
394 return TSS2_ESYS_RC_BAD_REFERENCE;
397 const auto digest_size = hmac->get().output_length();
398 hmac->get().final(std::span{buffer, digest_size});
399 if(size !=
nullptr) {
406 return TSS2_RC_SUCCESS;
416void hmac_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
418 if(context !=
nullptr) {
435TSS2_RC get_random2b(TPM2B_NONCE* nonce,
size_t num_bytes,
void* userdata) {
437 auto ccs = get(userdata);
439 return TSS2_ESYS_RC_BAD_REFERENCE;
443 return TSS2_RC_SUCCESS;
462TSS2_RC rsa_pk_encrypt(TPM2B_PUBLIC* pub_tpm_key,
470 if(out_size !=
nullptr) {
481 #if defined(BOTAN_HAS_RSA)
482 auto create_eme = [&](
const TPMT_RSA_SCHEME& scheme,
483 [[maybe_unused]] TPM2_ALG_ID name_algo,
484 [[maybe_unused]] TPMU_ASYM_SCHEME scheme_detail)
485 -> std::optional<std::unique_ptr<Botan::EncryptionPaddingScheme>> {
489 auto create_oaep = [&]() -> std::optional<std::unique_ptr<Botan::EncryptionPaddingScheme>> {
490 #if defined(BOTAN_HAS_EME_OAEP)
495 (scheme_detail.oaep.hashAlg == TPM2_ALG_NULL || scheme_detail.oaep.hashAlg == TPM2_ALG_ERROR)
496 ? pub_tpm_key->publicArea.nameAlg
497 : scheme_detail.oaep.hashAlg);
503 if(!label_hash || !mgf1_hash) {
509 if(!H_label || !H_mgf1) {
516 const std::string_view label_with_zero_terminator{label, std::strlen(label) + 1};
517 return std::make_unique<Botan::OAEP>(std::move(H_label), std::move(H_mgf1), label_with_zero_terminator);
525 switch(scheme.scheme) {
527 return create_oaep();
543 auto ccs = get(userdata);
545 return TSS2_ESYS_RC_BAD_REFERENCE;
552 const auto maybe_eme = create_eme(pub_tpm_key->publicArea.parameters.rsaDetail.scheme,
553 pub_tpm_key->publicArea.nameAlg,
554 pub_tpm_key->publicArea.parameters.rsaDetail.scheme.details);
555 if(!maybe_eme.has_value()) {
556 return TSS2_ESYS_RC_NOT_SUPPORTED;
559 const auto& eme = maybe_eme.value();
561 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
575 const auto pubkey = Botan::TPM2::rsa_pubkey_from_tss2_public(pub_tpm_key);
576 const auto keybits = pubkey.key_length();
577 const auto output_size = keybits / 8;
578 if(eme->maximum_input_size(keybits) < in_size) {
579 return TSS2_ESYS_RC_BAD_VALUE;
582 if(output_size > max_out_size) {
583 return TSS2_ESYS_RC_INSUFFICIENT_BUFFER;
586 const auto max_raw_bits = keybits - 1;
587 const auto max_raw_bytes = (max_raw_bits + 7) / 8;
588 const auto padded_bytes = eme->pad({out_buffer, max_raw_bytes}, {in_buffer, in_size}, max_raw_bits, rng);
593 const auto encrypted = encryptor.encrypt({out_buffer, padded_bytes}, rng);
599 if(out_size !=
nullptr) {
600 *out_size = encrypted.size();
603 return TSS2_RC_SUCCESS;
606 BOTAN_UNUSED(pub_tpm_key, in_size, in_buffer, max_out_size, out_buffer, label, userdata);
607 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
628TSS2_RC get_ecdh_point(TPM2B_PUBLIC* key,
630 TPM2B_ECC_PARAMETER* Z,
635 if(out_size !=
nullptr) {
639 #if defined(BOTAN_HAS_ECDH)
641 auto ccs = get(userdata);
643 return TSS2_ESYS_RC_BAD_REFERENCE;
649 const auto [tpm_ec_group, tpm_ec_point] = Botan::TPM2::ecc_pubkey_from_tss2_public(key);
652 const auto curve_order_byte_size = tpm_sw_pubkey.domain().get_p_bytes();
659 const auto& eph_pub_point = eph_key._public_ec_point();
665 const auto shared_secret = ecdh.derive_key(0 , tpm_sw_pubkey.public_value()).bits_of();
670 Tss2_MU_TPMS_ECC_POINT_Marshal(Q, out_buffer, max_out_size, out_size));
672 return TSS2_RC_SUCCESS;
675 BOTAN_UNUSED(key, max_out_size, Z, Q, out_buffer, userdata);
676 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
695TSS2_RC aes_encrypt(uint8_t* key,
696 TPM2_ALG_ID tpm_sym_alg,
697 TPMI_AES_KEY_BITS key_bits,
698 TPM2_ALG_ID tpm_mode,
704 if(tpm_sym_alg != TPM2_ALG_AES) {
705 return TSS2_ESYS_RC_BAD_VALUE;
726TSS2_RC aes_decrypt(uint8_t* key,
727 TPM2_ALG_ID tpm_sym_alg,
728 TPMI_AES_KEY_BITS key_bits,
729 TPM2_ALG_ID tpm_mode,
735 if(tpm_sym_alg != TPM2_ALG_AES) {
736 return TSS2_ESYS_RC_BAD_VALUE;
742 #if defined(BOTAN_TSS2_SUPPORTS_SM4_IN_CRYPTO_CALLBACKS)
759TSS2_RC sm4_encrypt(uint8_t* key,
760 TPM2_ALG_ID tpm_sym_alg,
761 TPMI_SM4_KEY_BITS key_bits,
762 TPM2_ALG_ID tpm_mode,
768 if(tpm_sym_alg != TPM2_ALG_SM4) {
769 return TSS2_ESYS_RC_BAD_VALUE;
790TSS2_RC sm4_decrypt(uint8_t* key,
791 TPM2_ALG_ID tpm_sym_alg,
792 TPMI_SM4_KEY_BITS key_bits,
793 TPM2_ALG_ID tpm_mode,
799 if(tpm_sym_alg != TPM2_ALG_SM4) {
800 return TSS2_ESYS_RC_BAD_VALUE;
819 return TSS2_RC_SUCCESS;
863#if defined(BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS)
867 ESYS_CRYPTO_CALLBACKS callbacks{
868 .rsa_pk_encrypt = &rsa_pk_encrypt,
869 .hash_start = &hash_start,
870 .hash_update = &hash_update,
871 .hash_finish = &hash_finish,
872 .hash_abort = &hash_abort,
873 .hmac_start = &hmac_start,
874 .hmac_update = &hmac_update,
875 .hmac_finish = &hmac_finish,
876 .hmac_abort = &hmac_abort,
877 .get_random2b = &get_random2b,
878 .get_ecdh_point = &get_ecdh_point,
879 .aes_encrypt = &aes_encrypt,
880 .aes_decrypt = &aes_decrypt,
881#if defined(BOTAN_TSS2_SUPPORTS_SM4_IN_CRYPTO_CALLBACKS)
882 .sm4_encrypt = &sm4_encrypt,
883 .sm4_decrypt = &sm4_decrypt,
886 .userdata = callback_state,
890 check_rc(
"Esys_SetCryptoCallbacks", Esys_SetCryptoCallbacks(ctx, &callbacks));
894 "This build of botan was compiled with a TSS2 version lower than 4.0.0, "
895 "which does not support custom runtime crypto backends");
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_DEBUG_ASSERT(expr)
#define BOTAN_ASSERT_NONNULL(ptr)
static std::unique_ptr< Cipher_Mode > create(std::string_view algo, Cipher_Dir direction, std::string_view provider="")
static std::unique_ptr< EncryptionPaddingScheme > create(std::string_view algo_spec)
static std::unique_ptr< HashFunction > create(std::string_view algo_spec, std::string_view provider="")
static std::unique_ptr< MessageAuthenticationCode > create(std::string_view algo_spec, std::string_view provider="")
constexpr void check_rc(std::string_view location, TSS2_RC rc)
std::optional< std::string > hash_algo_tss2_to_botan(TPMI_ALG_HASH hash_id)
std::optional< std::string > cipher_tss2_to_botan(TPMT_SYM_DEF cipher_def)
constexpr void copy_into(T &dest, std::span< const uint8_t > data)
constexpr auto as_span(tpm2_buffer auto &data)
Construct a std::span as a view into a TPM2 buffer.
void set_crypto_callbacks(ESYS_CONTEXT *ctx, void *callback_state)
std::string fmt(std::string_view format, const T &... args)
constexpr void copy_mem(T *out, const T *in, size_t n)
bool any_null_pointers(Ptrs... ptr)
uint32_t TSS2_RC
Forward declaration of TSS2 type for convenience.