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/eme.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 if(!std::holds_alternative<std::unique_ptr<T>>(blob->ctx)) {
79 return {std::ref(*std::get<std::unique_ptr<T>>(blob->ctx))};
83 requires std::constructible_from<DigestObject, std::unique_ptr<T>>
84[[nodiscard]] std::optional<std::reference_wrapper<T>> get(DigestCallbackState** blob)
noexcept {
93[[nodiscard]] std::optional<std::reference_wrapper<Botan::TPM2::CryptoCallbackState>> get(
void* userdata)
noexcept {
106template <std::invocable<> F>
107 requires std::same_as<std::invoke_result_t<F>,
TSS2_RC>
108[[nodiscard]]
TSS2_RC thunk(F f)
noexcept {
112 return TSS2_ESYS_RC_BAD_VALUE;
114 return TSS2_ESYS_RC_BAD_SEQUENCE;
116 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
118 return TSS2_ESYS_RC_MALFORMED_RESPONSE;
120 return TSS2_ESYS_RC_GENERAL_FAILURE;
122 return TSS2_ESYS_RC_GENERAL_FAILURE;
132 TPM2_ALG_ID tpm_sym_alg,
133 TPMI_AES_KEY_BITS key_bits,
134 TPM2_ALG_ID tpm_mode,
137 const uint8_t* iv)
noexcept {
141 : TSS2_ESYS_RC_NO_DECRYPT_PARAM;
145 if(buffer ==
nullptr && buffer_size != 0) {
146 return TSS2_ESYS_RC_BAD_VALUE;
150 .algorithm = tpm_sym_alg,
151 .keyBits = {.sym = key_bits},
152 .mode = {.sym = tpm_mode},
155 return TSS2_ESYS_RC_NOT_SUPPORTED;
160 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
165 if(cipher->authenticated()) {
166 return TSS2_ESYS_RC_INSUFFICIENT_BUFFER;
170 const size_t keylength =
static_cast<size_t>(key_bits) / 8;
171 if(!cipher->valid_keylength(keylength)) {
172 return TSS2_ESYS_RC_BAD_VALUE;
175 const auto s_data = std::span{buffer, buffer_size};
176 const auto s_key = std::span{key, keylength};
177 const auto s_iv = [&]() -> std::span<const uint8_t> {
179 return {iv, cipher->default_nonce_length()};
185 cipher->set_key(s_key);
187 cipher->process(s_data);
188 return TSS2_RC_SUCCESS;
203TSS2_RC hash_start(ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
void* userdata) {
206 if(context ==
nullptr) {
207 return TSS2_ESYS_RC_BAD_REFERENCE;
212 return TSS2_ESYS_RC_NOT_SUPPORTED;
216 if(hash ==
nullptr) {
217 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
221 *context =
new DigestCallbackState{std::move(hash)};
222 return TSS2_RC_SUCCESS;
237TSS2_RC hash_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
240 const auto hash = get<Botan::HashFunction>(context);
242 return TSS2_ESYS_RC_BAD_REFERENCE;
246 if(buffer ==
nullptr && size != 0) {
247 return TSS2_ESYS_RC_BAD_VALUE;
250 hash->get().update(std::span{buffer, size});
251 return TSS2_RC_SUCCESS;
266TSS2_RC hash_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
268 if(size !=
nullptr) {
273 auto hash = get<Botan::HashFunction>(context);
274 if(!hash || buffer ==
nullptr) {
275 return TSS2_ESYS_RC_BAD_REFERENCE;
278 const auto digest_size = hash->get().output_length();
279 hash->get().final(std::span{buffer, digest_size});
280 if(size !=
nullptr) {
287 return TSS2_RC_SUCCESS;
297void hash_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
299 if(context !=
nullptr) {
319 ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
const uint8_t* key,
size_t size,
void* userdata) {
323 return TSS2_ESYS_RC_BAD_REFERENCE;
328 return TSS2_ESYS_RC_NOT_SUPPORTED;
332 if(hmac ==
nullptr) {
333 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
336 hmac->set_key(std::span{key, size});
339 *context =
new DigestCallbackState{std::move(hmac)};
340 return TSS2_RC_SUCCESS;
355TSS2_RC hmac_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
358 auto hmac = get<Botan::MessageAuthenticationCode>(context);
360 return TSS2_ESYS_RC_BAD_REFERENCE;
364 if(buffer ==
nullptr && size != 0) {
365 return TSS2_ESYS_RC_BAD_VALUE;
368 hmac->get().update(std::span{buffer, size});
369 return TSS2_RC_SUCCESS;
384TSS2_RC hmac_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
386 if(size !=
nullptr) {
391 auto hmac = get<Botan::MessageAuthenticationCode>(context);
392 if(!hmac || buffer ==
nullptr) {
393 return TSS2_ESYS_RC_BAD_REFERENCE;
396 const auto digest_size = hmac->get().output_length();
397 hmac->get().final(std::span{buffer, digest_size});
398 if(size !=
nullptr) {
405 return TSS2_RC_SUCCESS;
415void hmac_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
417 if(context !=
nullptr) {
434TSS2_RC get_random2b(TPM2B_NONCE* nonce,
size_t num_bytes,
void* userdata) {
436 auto ccs = get(userdata);
438 return TSS2_ESYS_RC_BAD_REFERENCE;
442 return TSS2_RC_SUCCESS;
461TSS2_RC rsa_pk_encrypt(TPM2B_PUBLIC* pub_tpm_key,
469 if(out_size !=
nullptr) {
480 #if defined(BOTAN_HAS_RSA)
481 auto create_eme = [&](
482 const TPMT_RSA_SCHEME& scheme,
483 [[maybe_unused]] TPM2_ALG_ID name_algo,
484 [[maybe_unused]] TPMU_ASYM_SCHEME scheme_detail) -> std::optional<std::unique_ptr<Botan::EME>> {
488 auto create_oaep = [&]() -> std::optional<std::unique_ptr<Botan::EME>> {
489 #if defined(BOTAN_HAS_EME_OAEP)
494 (scheme_detail.oaep.hashAlg == TPM2_ALG_NULL || scheme_detail.oaep.hashAlg == TPM2_ALG_ERROR)
495 ? pub_tpm_key->publicArea.nameAlg
496 : scheme_detail.oaep.hashAlg);
502 if(!label_hash || !mgf1_hash) {
508 if(!H_label || !H_mgf1) {
515 std::string_view label_with_zero_terminator{label, std::strlen(label) + 1};
516 return std::make_unique<Botan::OAEP>(std::move(H_label), std::move(H_mgf1), label_with_zero_terminator);
524 switch(scheme.scheme) {
526 return create_oaep();
542 auto ccs = get(userdata);
544 return TSS2_ESYS_RC_BAD_REFERENCE;
551 const auto maybe_eme = create_eme(pub_tpm_key->publicArea.parameters.rsaDetail.scheme,
552 pub_tpm_key->publicArea.nameAlg,
553 pub_tpm_key->publicArea.parameters.rsaDetail.scheme.details);
554 if(!maybe_eme.has_value()) {
555 return TSS2_ESYS_RC_NOT_SUPPORTED;
558 const auto& eme = maybe_eme.value();
560 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
574 const auto pubkey = Botan::TPM2::rsa_pubkey_from_tss2_public(pub_tpm_key);
575 const auto keybits = pubkey.key_length();
576 const auto output_size = keybits / 8;
577 if(eme->maximum_input_size(keybits) < in_size) {
578 return TSS2_ESYS_RC_BAD_VALUE;
581 if(output_size > max_out_size) {
582 return TSS2_ESYS_RC_INSUFFICIENT_BUFFER;
585 const auto max_raw_bits = keybits - 1;
586 const auto max_raw_bytes = (max_raw_bits + 7) / 8;
587 const auto padded_bytes = eme->pad({out_buffer, max_raw_bytes}, {in_buffer, in_size}, max_raw_bits, rng);
592 const auto encrypted = encryptor.encrypt({out_buffer, padded_bytes}, rng);
598 if(out_size !=
nullptr) {
599 *out_size = encrypted.size();
602 return TSS2_RC_SUCCESS;
605 BOTAN_UNUSED(pub_tpm_key, in_size, in_buffer, max_out_size, out_buffer, label, userdata);
606 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
627TSS2_RC get_ecdh_point(TPM2B_PUBLIC* key,
629 TPM2B_ECC_PARAMETER* Z,
634 if(out_size !=
nullptr) {
638 #if defined(BOTAN_HAS_ECDH)
640 auto ccs = get(userdata);
642 return TSS2_ESYS_RC_BAD_REFERENCE;
648 const auto [tpm_ec_group, tpm_ec_point] = Botan::TPM2::ecc_pubkey_from_tss2_public(key);
651 const auto curve_order_byte_size = tpm_sw_pubkey.domain().get_p_bytes();
658 const auto& eph_pub_point = eph_key._public_ec_point();
664 const auto shared_secret = ecdh.derive_key(0 , tpm_sw_pubkey.public_value()).bits_of();
669 Tss2_MU_TPMS_ECC_POINT_Marshal(Q, out_buffer, max_out_size, out_size));
671 return TSS2_RC_SUCCESS;
674 BOTAN_UNUSED(key, max_out_size, Z, Q, out_buffer, userdata);
675 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
694TSS2_RC aes_encrypt(uint8_t* key,
695 TPM2_ALG_ID tpm_sym_alg,
696 TPMI_AES_KEY_BITS key_bits,
697 TPM2_ALG_ID tpm_mode,
703 if(tpm_sym_alg != TPM2_ALG_AES) {
704 return TSS2_ESYS_RC_BAD_VALUE;
725TSS2_RC aes_decrypt(uint8_t* key,
726 TPM2_ALG_ID tpm_sym_alg,
727 TPMI_AES_KEY_BITS key_bits,
728 TPM2_ALG_ID tpm_mode,
734 if(tpm_sym_alg != TPM2_ALG_AES) {
735 return TSS2_ESYS_RC_BAD_VALUE;
741 #if defined(BOTAN_TSS2_SUPPORTS_SM4_IN_CRYPTO_CALLBACKS)
758TSS2_RC sm4_encrypt(uint8_t* key,
759 TPM2_ALG_ID tpm_sym_alg,
760 TPMI_SM4_KEY_BITS key_bits,
761 TPM2_ALG_ID tpm_mode,
767 if(tpm_sym_alg != TPM2_ALG_SM4) {
768 return TSS2_ESYS_RC_BAD_VALUE;
789TSS2_RC sm4_decrypt(uint8_t* key,
790 TPM2_ALG_ID tpm_sym_alg,
791 TPMI_SM4_KEY_BITS key_bits,
792 TPM2_ALG_ID tpm_mode,
798 if(tpm_sym_alg != TPM2_ALG_SM4) {
799 return TSS2_ESYS_RC_BAD_VALUE;
818 return TSS2_RC_SUCCESS;
862#if defined(BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS)
866 ESYS_CRYPTO_CALLBACKS callbacks{
867 .rsa_pk_encrypt = &rsa_pk_encrypt,
868 .hash_start = &hash_start,
869 .hash_update = &hash_update,
870 .hash_finish = &hash_finish,
871 .hash_abort = &hash_abort,
872 .hmac_start = &hmac_start,
873 .hmac_update = &hmac_update,
874 .hmac_finish = &hmac_finish,
875 .hmac_abort = &hmac_abort,
876 .get_random2b = &get_random2b,
877 .get_ecdh_point = &get_ecdh_point,
878 .aes_encrypt = &aes_encrypt,
879 .aes_decrypt = &aes_decrypt,
880#if defined(BOTAN_TSS2_SUPPORTS_SM4_IN_CRYPTO_CALLBACKS)
881 .sm4_encrypt = &sm4_encrypt,
882 .sm4_decrypt = &sm4_decrypt,
885 .userdata = callback_state,
889 check_rc(
"Esys_SetCryptoCallbacks", Esys_SetCryptoCallbacks(ctx, &callbacks));
893 "This build of botan was compiled with a TSS2 version lower than 4.0.0, "
894 "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< EME > 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)
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.
std::optional< std::string > hash_algo_tss2_to_botan(TPMI_ALG_HASH hash_id) noexcept
void set_crypto_callbacks(ESYS_CONTEXT *ctx, void *callback_state)
std::optional< std::string > cipher_tss2_to_botan(TPMT_SYM_DEF cipher_def) noexcept
constexpr void copy_mem(T *out, const T *in, size_t n)
std::string fmt(std::string_view format, const T &... args)
bool any_null_pointers(Ptrs... ptr)
uint32_t TSS2_RC
Forward declaration of TSS2 type for convenience.