Botan 3.6.0
Crypto and TLS for C&
tpm2_rsa.cpp
Go to the documentation of this file.
1/*
2* TPM 2.0 RSA Key Wrappres
3* (C) 2024 Jack Lloyd
4* (C) 2024 René Meusel, Amos Treiber - Rohde & Schwarz Cybersecurity GmbH, financed by LANCOM Systems GmbH
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/tpm2_rsa.h>
10
11#include <botan/hash.h>
12#include <botan/rsa.h>
13
14#include <botan/internal/ct_utils.h>
15#include <botan/internal/emsa.h>
16#include <botan/internal/fmt.h>
17#include <botan/internal/pss_params.h>
18#include <botan/internal/scan_name.h>
19#include <botan/internal/stl_util.h>
20#include <botan/internal/tpm2_algo_mappings.h>
21#include <botan/internal/tpm2_hash.h>
22#include <botan/internal/tpm2_pkops.h>
23#include <botan/internal/tpm2_util.h>
24
25#include <tss2/tss2_esys.h>
26
27namespace Botan::TPM2 {
28
29RSA_PublicKey::RSA_PublicKey(Object handle, SessionBundle session_bundle, const TPM2B_PUBLIC* public_blob) :
30 Botan::TPM2::PublicKey(std::move(handle), std::move(session_bundle)),
31 Botan::RSA_PublicKey(rsa_pubkey_from_tss2_public(public_blob)) {}
32
34 SessionBundle session_bundle,
35 const TPM2B_PUBLIC* public_blob,
36 std::span<const uint8_t> private_blob) :
37 Botan::TPM2::PrivateKey(std::move(handle), std::move(session_bundle), private_blob),
38 Botan::RSA_PublicKey(rsa_pubkey_from_tss2_public(public_blob)) {}
39
40std::unique_ptr<TPM2::PrivateKey> RSA_PrivateKey::create_unrestricted_transient(const std::shared_ptr<Context>& ctx,
41 const SessionBundle& sessions,
42 std::span<const uint8_t> auth_value,
43 const TPM2::PrivateKey& parent,
44 uint16_t keylength,
45 std::optional<uint32_t> exponent) {
46 BOTAN_ARG_CHECK(parent.is_parent(), "The passed key cannot be used as a parent key");
47
48 TPM2B_SENSITIVE_CREATE sensitive_data = {
49 .size = 0, // ignored
50 .sensitive =
51 {
52 .userAuth = copy_into<TPM2B_AUTH>(auth_value),
53
54 // Architecture Document, Section 25.2.3
55 // When an asymmetric key is created, the caller is not allowed to
56 // provide the sensitive data of the key.
58 },
59 };
60
61 TPMT_PUBLIC key_template = {
62 .type = TPM2_ALG_RSA,
63
64 // This is the algorithm for fingerprinting the newly created public key.
65 // For best compatibility we always use SHA-256.
66 .nameAlg = TPM2_ALG_SHA256,
67
68 // This sets up the key to be both a decryption and a signing key, forbids
69 // its duplication (fixed_tpm, fixed_parent) and ensures that the key's
70 // private portion can be used only by a user with an HMAC or password
71 // session.
72 .objectAttributes = ObjectAttributes::render({
73 .fixed_tpm = true,
74 .fixed_parent = true,
75 .sensitive_data_origin = true,
76 .user_with_auth = true,
77 .decrypt = true,
78 .sign_encrypt = true,
79 }),
80
81 // We currently do not support policy-based authorization
82 .authPolicy = init_empty<TPM2B_DIGEST>(),
83 .parameters =
84 {
85 .rsaDetail =
86 {
87 // Structures Document (Part 2), Section 12.2.3.5
88 // If the key is not a restricted decryption key, this field
89 // shall be set to TPM_ALG_NULL.
90 //
91 // TODO: Once we stop supporting TSS < 4.0, we could use
92 // `.keyBits = {.null = {}}, .mode = {.null = {}}`
93 // which better reflects our intention here.
94 .symmetric =
95 {
96 .algorithm = TPM2_ALG_NULL,
97 .keyBits = {.sym = 0},
98 .mode = {.sym = TPM2_ALG_NULL},
99 },
100
101 // Structures Document (Part 2), Section 12.2.3.5
102 // When both sign and decrypt are SET, restricted shall be
103 // CLEAR and scheme shall be TPM_ALG_NULL
104 //
105 // TODO: Once we stop supporting TSS < 4.0, we could use
106 // `.details = {.null = {}}`
107 // which better reflects our intention here.
108 .scheme =
109 {
110 .scheme = TPM2_ALG_NULL,
111 .details = {.anySig = {.hashAlg = TPM2_ALG_NULL}},
112 },
113 .keyBits = keylength,
114 .exponent = exponent.value_or(0 /* default value - 2^16 + 1*/),
115 },
116 },
117
118 // For creating an asymmetric key this value is not used.
119 .unique = {.rsa = init_empty<TPM2B_PUBLIC_KEY_RSA>()},
120 };
121
123 ctx, sessions, parent.handles().transient_handle(), key_template, sensitive_data);
124}
125
126namespace {
127
128SignatureAlgorithmSelection select_signature_algorithms(std::string_view padding) {
129 const SCAN_Name req(padding);
130 if(req.arg_count() == 0) {
131 throw Invalid_Argument("RSA signing padding scheme must at least specify a hash function");
132 }
133
134 auto sig_scheme = rsa_signature_scheme_botan_to_tss2(padding);
135 if(!sig_scheme) {
136 throw Not_Implemented(Botan::fmt("RSA signing with padding scheme {}", padding));
137 }
138
139 return {
140 .signature_scheme = sig_scheme.value(),
141 .hash_name = req.arg(0),
142 .padding = std::string(padding),
143 };
144}
145
146size_t signature_length_for_key_handle(const SessionBundle& sessions, const Object& key_handle) {
147 return key_handle._public_info(sessions, TPM2_ALG_RSA).pub->publicArea.parameters.rsaDetail.keyBits / 8;
148}
149
150class RSA_Signature_Operation final : public Signature_Operation {
151 public:
152 RSA_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) :
153 Signature_Operation(object, sessions, select_signature_algorithms(padding)) {}
154
155 size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); }
156
157 AlgorithmIdentifier algorithm_identifier() const override {
158 // TODO: This is essentially a copy of the ::algorithm_identifier()
159 // in `rsa.h`. We should probably refactor this into a common
160 // function.
161
162 // This EMSA object actually isn't required, we just need it to
163 // conveniently figure out the algorithm identifier.
164 //
165 // TODO: This is a hack, and we should clean this up.
166 BOTAN_STATE_CHECK(padding().has_value());
167 const auto emsa = EMSA::create_or_throw(padding().value());
168 const std::string emsa_name = emsa->name();
169
170 try {
171 const std::string full_name = "RSA/" + emsa_name;
172 const OID oid = OID::from_string(full_name);
173 return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
174 } catch(Lookup_Error&) {}
175
176 if(emsa_name.starts_with("EMSA4(")) {
177 auto parameters = PSS_Params::from_emsa_name(emsa_name).serialize();
178 return AlgorithmIdentifier("RSA/EMSA4", parameters);
179 }
180
181 throw Not_Implemented("No algorithm identifier defined for RSA with " + emsa_name);
182 }
183
184 private:
185 std::vector<uint8_t> marshal_signature(const TPMT_SIGNATURE& signature) const override {
186 const auto& sig = [&] {
187 if(signature.sigAlg == TPM2_ALG_RSASSA) {
188 return signature.signature.rsassa;
189 } else if(signature.sigAlg == TPM2_ALG_RSAPSS) {
190 return signature.signature.rsapss;
191 }
192 throw Invalid_State(fmt("TPM2 returned an unexpected signature scheme {}", signature.sigAlg));
193 }();
194
195 BOTAN_ASSERT_NOMSG(sig.sig.size == signature_length());
196 return copy_into<std::vector<uint8_t>>(sig.sig);
197 }
198};
199
200class RSA_Verification_Operation final : public Verification_Operation {
201 public:
202 RSA_Verification_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) :
203 Verification_Operation(object, sessions, select_signature_algorithms(padding)) {}
204
205 private:
206 TPMT_SIGNATURE unmarshal_signature(std::span<const uint8_t> signature) const override {
207 BOTAN_ARG_CHECK(signature.size() == signature_length_for_key_handle(sessions(), key_handle()),
208 "Unexpected signature byte length");
209
210 TPMT_SIGNATURE sig;
211 sig.sigAlg = scheme().scheme;
212
213 auto& sig_data = [&]() -> TPMS_SIGNATURE_RSA& {
214 if(sig.sigAlg == TPM2_ALG_RSASSA) {
215 return sig.signature.rsassa;
216 } else if(sig.sigAlg == TPM2_ALG_RSAPSS) {
217 return sig.signature.rsapss;
218 }
219 throw Invalid_State(fmt("Requested an unexpected signature scheme {}", sig.sigAlg));
220 }();
221
222 sig_data.hash = scheme().details.any.hashAlg;
223 copy_into(sig_data.sig, signature);
224 return sig;
225 }
226};
227
228TPMT_RSA_DECRYPT select_encryption_algorithms(std::string_view padding) {
229 auto scheme = rsa_encryption_scheme_botan_to_tss2(padding);
230 if(!scheme) {
231 throw Not_Implemented(Botan::fmt("RSA encryption with padding scheme {}", padding));
232 }
233 return scheme.value();
234}
235
236class RSA_Encryption_Operation final : public PK_Ops::Encryption {
237 public:
238 RSA_Encryption_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) :
239 m_key_handle(object), m_sessions(sessions), m_scheme(select_encryption_algorithms(padding)) {}
240
241 std::vector<uint8_t> encrypt(std::span<const uint8_t> msg, Botan::RandomNumberGenerator& /* rng */) override {
242 const auto plaintext = copy_into<TPM2B_PUBLIC_KEY_RSA>(msg);
243
244 // TODO: Figure out what this is for. Given that I didn't see any other
245 // way to pass an EME-OAEP label, I'm guessing that this is what
246 // it is for. But I'm not sure.
247 //
248 // Again, a follow-up of https://github.com/randombit/botan/pull/4318
249 // that targets async encryption will probably be quite helpful here.
250 const auto label = init_empty<TPM2B_DATA>();
251
253 check_rc("Esys_RSA_Encrypt",
254 Esys_RSA_Encrypt(*m_key_handle.context(),
255 m_key_handle.transient_handle(),
256 m_sessions[0],
257 m_sessions[1],
258 m_sessions[2],
259 &plaintext,
260 &m_scheme,
261 &label,
262 out_ptr(ciphertext)));
263 BOTAN_ASSERT_NONNULL(ciphertext);
264 return copy_into<std::vector<uint8_t>>(*ciphertext);
265 }
266
267 // This duplicates quite a bit of domain knowledge about those RSA
268 // EMEs. And I'm quite certain that I screwed up somewhere.
269 //
270 // TODO: See if we can somehow share the logic with the software
271 // RSA implementation and also PKCS#11 (which I believe is plain wrong).
272 size_t max_input_bits() const override {
273 const auto max_ptext_bytes =
274 (m_key_handle._public_info(m_sessions, TPM2_ALG_RSA).pub->publicArea.parameters.rsaDetail.keyBits - 1) / 8;
275 auto hash_output_bytes = [](TPM2_ALG_ID hash) -> size_t {
276 switch(hash) {
277 case TPM2_ALG_SHA1:
278 return 160 / 8;
279 case TPM2_ALG_SHA256:
280 case TPM2_ALG_SHA3_256:
281 return 256 / 8;
282 case TPM2_ALG_SHA384:
283 case TPM2_ALG_SHA3_384:
284 return 384 / 8;
285 case TPM2_ALG_SHA512:
286 case TPM2_ALG_SHA3_512:
287 return 512 / 8;
288 default:
289 throw Invalid_State("Unexpected hash algorithm");
290 }
291 };
292
293 const auto max_input_bytes = [&]() -> size_t {
294 switch(m_scheme.scheme) {
295 case TPM2_ALG_RSAES:
296 return max_ptext_bytes - 10;
297 case TPM2_ALG_OAEP:
298 return max_ptext_bytes - 2 * hash_output_bytes(m_scheme.details.oaep.hashAlg) - 1;
299 case TPM2_ALG_NULL:
300 return max_ptext_bytes;
301 default:
302 throw Invalid_State("Unexpected RSA encryption scheme");
303 }
304 }();
305
306 return max_input_bytes * 8;
307 }
308
309 size_t ciphertext_length(size_t /* ptext_len */) const override {
310 return m_key_handle._public_info(m_sessions, TPM2_ALG_RSA).pub->publicArea.parameters.rsaDetail.keyBits - 1;
311 }
312
313 private:
314 const Object& m_key_handle;
315 const SessionBundle& m_sessions;
316 TPMT_RSA_DECRYPT m_scheme;
317};
318
319class RSA_Decryption_Operation final : public PK_Ops::Decryption {
320 public:
321 RSA_Decryption_Operation(const Object& object, const SessionBundle& sessions, std::string_view padding) :
322 m_key_handle(object), m_sessions(sessions), m_scheme(select_encryption_algorithms(padding)) {}
323
324 secure_vector<uint8_t> decrypt(uint8_t& valid_mask, std::span<const uint8_t> input) override {
325 const auto ciphertext = copy_into<TPM2B_PUBLIC_KEY_RSA>(input);
326 const auto label = init_empty<TPM2B_DATA>(); // TODO: implement? see encrypt operation
328
329 // TODO: I'm not sure that TPM2_RC_FAILURE is the right error code for
330 // all cases here. It passed the test (with a faulty ciphertext),
331 // but I didn't find this to be clearly documented. :-(
332 auto rc = check_rc_expecting<TPM2_RC_FAILURE>("Esys_RSA_Decrypt",
333 Esys_RSA_Decrypt(*m_key_handle.context(),
334 m_key_handle.transient_handle(),
335 m_sessions[0],
336 m_sessions[1],
337 m_sessions[2],
338 &ciphertext,
339 &m_scheme,
340 &label,
341 out_ptr(plaintext)));
342
343 valid_mask = CT::Mask<uint8_t>::is_equal(rc, TPM2_RC_SUCCESS).value();
344 if(rc == TPM2_RC_SUCCESS) {
345 BOTAN_ASSERT_NONNULL(plaintext);
346 return copy_into<secure_vector<uint8_t>>(*plaintext);
347 } else {
348 return {};
349 }
350 }
351
352 size_t plaintext_length(size_t /* ciphertext_length */) const override {
353 return m_key_handle._public_info(m_sessions, TPM2_ALG_RSA).pub->publicArea.parameters.rsaDetail.keyBits / 8;
354 }
355
356 private:
357 const Object& m_key_handle;
358 const SessionBundle& m_sessions;
359 TPMT_RSA_DECRYPT m_scheme;
360};
361
362} // namespace
363
364std::unique_ptr<PK_Ops::Verification> RSA_PublicKey::create_verification_op(std::string_view params,
365 std::string_view provider) const {
366 BOTAN_UNUSED(provider);
367 return std::make_unique<RSA_Verification_Operation>(handles(), sessions(), params);
368}
369
371 std::string_view params,
372 std::string_view provider) const {
373 BOTAN_UNUSED(rng, provider);
374 return std::make_unique<RSA_Signature_Operation>(handles(), sessions(), params);
375}
376
378 std::string_view params,
379 std::string_view provider) const {
380 BOTAN_UNUSED(rng, provider);
381 return std::make_unique<RSA_Encryption_Operation>(handles(), sessions(), params);
382}
383
385 std::string_view params,
386 std::string_view provider) const {
387 BOTAN_UNUSED(rng, provider);
388 return std::make_unique<RSA_Decryption_Operation>(handles(), sessions(), params);
389}
390
391} // namespace Botan::TPM2
#define BOTAN_UNUSED
Definition assert.h:118
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ASSERT_NONNULL(ptr)
Definition assert.h:86
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
static constexpr Mask< T > is_equal(T x, T y)
Definition ct_utils.h:434
static std::unique_ptr< EMSA > create_or_throw(std::string_view algo_spec)
Definition emsa.cpp:131
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
std::vector< uint8_t > serialize() const
static PSS_Params from_emsa_name(std::string_view emsa_name)
ESYS_TR transient_handle() const noexcept
static std::unique_ptr< PrivateKey > create_transient_from_template(const std::shared_ptr< Context > &ctx, const SessionBundle &sessions, ESYS_TR parent, const TPMT_PUBLIC &key_template, const TPM2B_SENSITIVE_CREATE &sensitive_data)
Definition tpm2_key.cpp:220
const SessionBundle & sessions() const
Definition tpm2_key.h:224
const Object & handles() const
Definition tpm2_key.h:99
const SessionBundle & sessions() const
Definition tpm2_key.h:101
RSA_PrivateKey(Object handle, SessionBundle sessions, const TPM2B_PUBLIC *public_blob, std::span< const uint8_t > private_blob={})
Definition tpm2_rsa.cpp:33
static std::unique_ptr< TPM2::PrivateKey > create_unrestricted_transient(const std::shared_ptr< Context > &ctx, const SessionBundle &sessions, std::span< const uint8_t > auth_value, const TPM2::PrivateKey &parent, uint16_t keylength, std::optional< uint32_t > exponent={})
Definition tpm2_rsa.cpp:40
std::unique_ptr< PK_Ops::Decryption > create_decryption_op(Botan::RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition tpm2_rsa.cpp:384
std::unique_ptr< PK_Ops::Signature > create_signature_op(Botan::RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition tpm2_rsa.cpp:370
std::unique_ptr< PK_Ops::Encryption > create_encryption_op(Botan::RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition tpm2_rsa.cpp:377
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition tpm2_rsa.cpp:364
int(* final)(unsigned char *, CTX *)
std::string encrypt(const uint8_t input[], size_t input_len, std::string_view passphrase, RandomNumberGenerator &rng)
Definition cryptobox.cpp:42
std::string decrypt(const uint8_t input[], size_t input_len, std::string_view passphrase)
std::optional< TPMT_SIG_SCHEME > rsa_signature_scheme_botan_to_tss2(std::string_view name)
constexpr T init_empty()
Create an empty TPM2 buffer of the given type.
Definition tpm2_util.h:142
constexpr void check_rc(std::string_view location, TSS2_RC rc)
Definition tpm2_util.h:54
std::optional< TPMT_RSA_DECRYPT > rsa_encryption_scheme_botan_to_tss2(std::string_view padding)
std::unique_ptr< T, esys_liberator > unique_esys_ptr
A unique pointer type for ESYS handles that automatically frees the handle.
Definition tpm2_util.h:154
constexpr void copy_into(T &dest, std::span< const uint8_t > data)
Definition tpm2_util.h:117
constexpr TSS2_RC check_rc_expecting(std::string_view location, TSS2_RC rc)
Definition tpm2_util.h:72
constexpr auto out_ptr(T &outptr) noexcept
Definition stl_util.h:420
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
static TPMA_OBJECT render(ObjectAttributes attributes)