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/tpm2_algo_mappings.h>
31#include <botan/internal/tpm2_util.h>
33#if defined(BOTAN_HAS_EME_OAEP)
34 #include <botan/internal/oaep.h>
37#include <tss2/tss2_esys.h>
38#include <tss2/tss2_mu.h>
42#if defined(BOTAN_TSS2_SUPPORTS_CRYPTO_CALLBACKS)
48 std::variant<std::unique_ptr<Botan::HashFunction>, std::unique_ptr<Botan::MessageAuthenticationCode>>;
58typedef struct ESYS_CRYPTO_CONTEXT_BLOB {
68 requires std::constructible_from<DigestObject, std::unique_ptr<T>>
69[[nodiscard]] std::optional<std::reference_wrapper<T>> get(DigestCallbackState* blob)
noexcept {
74 if(!std::holds_alternative<std::unique_ptr<T>>(blob->ctx)) {
78 return {std::ref(*std::get<std::unique_ptr<T>>(blob->ctx))};
82 requires std::constructible_from<DigestObject, std::unique_ptr<T>>
83[[nodiscard]] std::optional<std::reference_wrapper<T>> get(DigestCallbackState** blob)
noexcept {
92[[nodiscard]] std::optional<std::reference_wrapper<Botan::TPM2::CryptoCallbackState>> get(
void* userdata)
noexcept {
110template <std::invocable<> F>
111 requires std::same_as<std::invoke_result_t<F>,
TSS2_RC>
112[[nodiscard]]
TSS2_RC thunk(F f)
noexcept {
116 return TSS2_ESYS_RC_BAD_VALUE;
118 return TSS2_ESYS_RC_BAD_SEQUENCE;
120 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
122 return TSS2_ESYS_RC_MALFORMED_RESPONSE;
124 return TSS2_ESYS_RC_GENERAL_FAILURE;
126 return TSS2_ESYS_RC_GENERAL_FAILURE;
136 TPM2_ALG_ID tpm_sym_alg,
137 TPMI_AES_KEY_BITS key_bits,
138 TPM2_ALG_ID tpm_mode,
141 const uint8_t* iv)
noexcept {
145 : TSS2_ESYS_RC_NO_DECRYPT_PARAM;
149 if(!buffer && buffer_size != 0) {
150 return TSS2_ESYS_RC_BAD_VALUE;
154 .algorithm = tpm_sym_alg,
155 .keyBits = {.sym = key_bits},
156 .mode = {.sym = tpm_mode},
159 return TSS2_ESYS_RC_NOT_SUPPORTED;
164 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
169 if(cipher->authenticated()) {
170 return TSS2_ESYS_RC_INSUFFICIENT_BUFFER;
174 const size_t keylength =
static_cast<size_t>(key_bits) / 8;
175 if(!cipher->valid_keylength(keylength)) {
176 return TSS2_ESYS_RC_BAD_VALUE;
179 const auto s_data = std::span{buffer, buffer_size};
180 const auto s_key = std::span{key, keylength};
181 const auto s_iv = [&]() -> std::span<const uint8_t> {
183 return {iv, cipher->default_nonce_length()};
189 cipher->set_key(s_key);
191 cipher->process(s_data);
192 return TSS2_RC_SUCCESS;
207TSS2_RC hash_start(ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
void* userdata) {
211 return TSS2_ESYS_RC_BAD_REFERENCE;
216 return TSS2_ESYS_RC_NOT_SUPPORTED;
221 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
225 *context =
new DigestCallbackState{std::move(hash)};
226 return TSS2_RC_SUCCESS;
241TSS2_RC hash_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
244 const auto hash = get<Botan::HashFunction>(context);
246 return TSS2_ESYS_RC_BAD_REFERENCE;
250 if(!buffer && size != 0) {
251 return TSS2_ESYS_RC_BAD_VALUE;
254 hash->get().update(std::span{buffer, size});
255 return TSS2_RC_SUCCESS;
270TSS2_RC hash_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
272 if(size !=
nullptr) {
277 auto hash = get<Botan::HashFunction>(context);
278 if(!hash || !buffer) {
279 return TSS2_ESYS_RC_BAD_REFERENCE;
282 const auto digest_size = hash->get().output_length();
283 hash->get().final(std::span{buffer, digest_size});
284 if(size !=
nullptr) {
290 return TSS2_RC_SUCCESS;
300void hash_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
321 ESYS_CRYPTO_CONTEXT_BLOB** context, TPM2_ALG_ID hash_alg,
const uint8_t* key,
size_t size,
void* userdata) {
324 if(!context || !key) {
325 return TSS2_ESYS_RC_BAD_REFERENCE;
330 return TSS2_ESYS_RC_NOT_SUPPORTED;
335 return TSS2_ESYS_RC_NOT_IMPLEMENTED;
338 hmac->set_key(std::span{key, size});
341 *context =
new DigestCallbackState{std::move(hmac)};
342 return TSS2_RC_SUCCESS;
357TSS2_RC hmac_update(ESYS_CRYPTO_CONTEXT_BLOB* context,
const uint8_t* buffer,
size_t size,
void* userdata) {
360 auto hmac = get<Botan::MessageAuthenticationCode>(context);
362 return TSS2_ESYS_RC_BAD_REFERENCE;
366 if(!buffer && size != 0) {
367 return TSS2_ESYS_RC_BAD_VALUE;
370 hmac->get().update(std::span{buffer, size});
371 return TSS2_RC_SUCCESS;
386TSS2_RC hmac_finish(ESYS_CRYPTO_CONTEXT_BLOB** context, uint8_t* buffer,
size_t* size,
void* userdata) {
388 if(size !=
nullptr) {
393 auto hmac = get<Botan::MessageAuthenticationCode>(context);
394 if(!hmac || !buffer) {
395 return TSS2_ESYS_RC_BAD_REFERENCE;
398 const auto digest_size = hmac->get().output_length();
399 hmac->get().final(std::span{buffer, digest_size});
400 if(size !=
nullptr) {
406 return TSS2_RC_SUCCESS;
416void hmac_abort(ESYS_CRYPTO_CONTEXT_BLOB** context,
void* userdata) {
434TSS2_RC get_random2b(TPM2B_NONCE* nonce,
size_t num_bytes,
void* userdata) {
436 auto ccs = get(userdata);
437 if(!ccs || !ccs->get().rng || !nonce) {
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);
543 if(!ccs || !pub_tpm_key || !in_buffer || !out_buffer || !ccs->get().rng) {
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);
641 if(!ccs || !key || !Z || !Q || !out_buffer | !ccs->get().rng) {
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
std::string fmt(std::string_view format, const T &... args)
constexpr void copy_mem(T *out, const T *in, size_t n)
uint32_t TSS2_RC
Forward declaration of TSS2 type for convenience.