Botan 3.6.0
Crypto and TLS for C&
tpm2_ecc.cpp
Go to the documentation of this file.
1/*
2* TPM 2.0 ECC 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_ecc.h>
10
11#include <botan/internal/stl_util.h>
12#include <botan/internal/tpm2_algo_mappings.h>
13#include <botan/internal/tpm2_hash.h>
14#include <botan/internal/tpm2_pkops.h>
15#include <botan/internal/tpm2_util.h>
16
17#include <tss2/tss2_esys.h>
18
19namespace Botan::TPM2 {
20
21EC_PublicKey::EC_PublicKey(Object handle, SessionBundle sessions, const TPM2B_PUBLIC* public_blob) :
22 EC_PublicKey(std::move(handle), std::move(sessions), ecc_pubkey_from_tss2_public(public_blob)) {}
23
24EC_PublicKey::EC_PublicKey(Object handle, SessionBundle sessions, std::pair<EC_Group, EC_AffinePoint> public_key) :
25 Botan::TPM2::PublicKey(std::move(handle), std::move(sessions)),
26 Botan::EC_PublicKey(std::move(public_key.first), public_key.second) {}
27
29 SessionBundle sessions,
30 const TPM2B_PUBLIC* public_blob,
31 std::span<const uint8_t> private_blob) :
32 EC_PrivateKey(std::move(handle), std::move(sessions), ecc_pubkey_from_tss2_public(public_blob), private_blob) {}
33
35 SessionBundle sessions,
36 std::pair<EC_Group, EC_AffinePoint> public_key,
37 std::span<const uint8_t> private_blob) :
38 Botan::TPM2::PrivateKey(std::move(handle), std::move(sessions), private_blob),
39 Botan::EC_PublicKey(std::move(public_key.first), public_key.second) {}
40
41std::unique_ptr<Public_Key> EC_PrivateKey::public_key() const {
42 return std::make_unique<Botan::ECDSA_PublicKey>(domain(), public_point());
43}
44
45std::vector<uint8_t> EC_PublicKey::public_key_bits() const {
47}
48
49std::vector<uint8_t> EC_PublicKey::raw_public_key_bits() const {
51}
52
53std::vector<uint8_t> EC_PrivateKey::public_key_bits() const {
55}
56
57std::vector<uint8_t> EC_PrivateKey::raw_public_key_bits() const {
59}
60
61std::unique_ptr<TPM2::PrivateKey> EC_PrivateKey::create_unrestricted_transient(const std::shared_ptr<Context>& ctx,
62 const SessionBundle& sessions,
63 std::span<const uint8_t> auth_value,
64 const TPM2::PrivateKey& parent,
65 const EC_Group& group) {
66 // TODO: Code duplication from RSA_PrivateKey::create_unrestricted_transient
67 BOTAN_ARG_CHECK(parent.is_parent(), "The passed key cannot be used as a parent key");
68
69 const auto curve_id = get_tpm2_curve_id(group.get_curve_oid());
70 if(!curve_id) {
71 throw Invalid_Argument("Unsupported ECC curve");
72 }
73
74 TPM2B_SENSITIVE_CREATE sensitive_data = {
75 .size = 0, // ignored
76 .sensitive =
77 {
78 .userAuth = copy_into<TPM2B_AUTH>(auth_value),
79
80 // Architecture Document, Section 25.2.3
81 // When an asymmetric key is created, the caller is not allowed to
82 // provide the sensitive data of the key.
84 },
85 };
86
87 TPMT_PUBLIC key_template = {
88 .type = TPM2_ALG_ECC,
89
90 // This is the algorithm for fingerprinting the newly created public key.
91 // For best compatibility we always use SHA-256.
92 .nameAlg = TPM2_ALG_SHA256,
93
94 // This sets up the key to be both a decryption and a signing key, forbids
95 // its duplication (fixed_tpm, fixed_parent) and ensures that the key's
96 // private portion can be used only by a user with an HMAC or password
97 // session.
98 .objectAttributes = ObjectAttributes::render({
99 .fixed_tpm = true,
100 .fixed_parent = true,
101 .sensitive_data_origin = true,
102 .user_with_auth = true,
103 .decrypt = true, // TODO: Shall we set this?
104 .sign_encrypt = true,
105 }),
106
107 // We currently do not support policy-based authorization
108 .authPolicy = init_empty<TPM2B_DIGEST>(),
109 .parameters =
110 {
111 .eccDetail =
112 {
113 // Structures Document (Part 2), Section 12.2.3.5
114 // If the key is not a restricted decryption key, this field
115 // shall be set to TPM_ALG_NULL.
116 //
117 // TODO: Once we stop supporting TSS < 4.0, we could use
118 // `.keyBits = {.null = {}}, .mode = {.null = {}}`
119 // which better reflects our intention here.
120 .symmetric =
121 {
122 .algorithm = TPM2_ALG_NULL,
123 .keyBits = {.sym = 0},
124 .mode = {.sym = TPM2_ALG_NULL},
125 },
126
127 // Structures Document (Part 2), Section 12.2.3.6
128 // If the decrypt attribute of the key is SET, then this shall be a
129 // valid key exchange scheme or TPM_ALG_NULL
130 //
131 // TODO: Once we stop supporting TSS < 4.0, we could use
132 // `.details = {.null = {}}`
133 // which better reflects our intention here.
134 .scheme =
135 {
136 .scheme = TPM2_ALG_NULL,
137 .details = {.anySig = {.hashAlg = TPM2_ALG_NULL}},
138 },
139 .curveID = curve_id.value(),
140
141 // Structures Document (Part 2), Section 12.2.3.6
142 // If the kdf parameter associated with curveID is not
143 // TPM_ALG_NULL then this is required to be NULL.
144 // NOTE There are currently no commands where this parameter
145 // has effect and, in the reference code, this field needs to
146 // be set to TPM_ALG_NULL
147 // TODO: Easier initialization?
148 .kdf = {.scheme = TPM2_ALG_NULL, .details = {.kdf2 = {.hashAlg = TPM2_ALG_NULL}}},
149 },
150 },
151
152 // For creating an asymmetric key this value is not used.
153 .unique = {.ecc = {}},
154 };
155
157 ctx, sessions, parent.handles().transient_handle(), key_template, sensitive_data);
158}
159
160namespace {
161
162SignatureAlgorithmSelection make_signature_scheme(std::string_view hash_name) {
163 return {
164 .signature_scheme =
165 TPMT_SIG_SCHEME{
166 .scheme = TPM2_ALG_ECDSA, // Only support ECDSA
167 .details = {.any = {.hashAlg = get_tpm2_hash_type(hash_name)}},
168 },
169 .hash_name = std::string(hash_name),
170 .padding = std::nullopt,
171 };
172}
173
174size_t signature_length_for_key_handle(const SessionBundle& sessions, const Object& object) {
175 const auto curve_id = object._public_info(sessions, TPM2_ALG_ECDSA).pub->publicArea.parameters.eccDetail.curveID;
176
177 const auto order_bytes = curve_id_order_byte_size(curve_id);
178 if(!order_bytes) {
179 throw Invalid_Argument(Botan::fmt("Unsupported ECC curve: {}", curve_id));
180 };
181 return 2 * order_bytes.value();
182}
183
184class EC_Signature_Operation final : public Signature_Operation {
185 public:
186 EC_Signature_Operation(const Object& object, const SessionBundle& sessions, std::string_view hash) :
187 Signature_Operation(object, sessions, make_signature_scheme(hash)) {}
188
189 size_t signature_length() const override { return signature_length_for_key_handle(sessions(), key_handle()); }
190
191 AlgorithmIdentifier algorithm_identifier() const override {
192 // Copied from ECDSA
193 const std::string full_name = "ECDSA/" + hash_function();
194 const OID oid = OID::from_string(full_name);
195 return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
196 }
197
198 private:
199 std::vector<uint8_t> marshal_signature(const TPMT_SIGNATURE& signature) const override {
200 BOTAN_STATE_CHECK(signature.sigAlg == TPM2_ALG_ECDSA);
201
202 const auto r = as_span(signature.signature.ecdsa.signatureR);
203 const auto s = as_span(signature.signature.ecdsa.signatureS);
204 const auto sig_len = signature_length_for_key_handle(sessions(), key_handle());
205 BOTAN_ASSERT_NOMSG(sig_len % 2 == 0);
206 BOTAN_ASSERT_NOMSG(r.size() == sig_len / 2 && s.size() == sig_len / 2);
207
208 return concat<std::vector<uint8_t>>(r, s);
209 }
210};
211
212class EC_Verification_Operation final : public Verification_Operation {
213 public:
214 EC_Verification_Operation(const Object& object, const SessionBundle& sessions, std::string_view hash) :
215 Verification_Operation(object, sessions, make_signature_scheme(hash)) {}
216
217 private:
218 TPMT_SIGNATURE unmarshal_signature(std::span<const uint8_t> sig_data) const override {
219 BOTAN_STATE_CHECK(scheme().scheme == TPM2_ALG_ECDSA);
220
221 const auto sig_len = signature_length_for_key_handle(sessions(), key_handle());
222 BOTAN_ARG_CHECK(sig_data.size() == sig_len, "Invalid signature length");
223 BOTAN_ASSERT_NOMSG(sig_len % 2 == 0);
224
225 return {
226 .sigAlg = TPM2_ALG_ECDSA,
227 .signature =
228 {
229 .ecdsa =
230 {
231 .hash = scheme().details.any.hashAlg,
232 .signatureR = copy_into<TPM2B_ECC_PARAMETER>(sig_data.first(sig_len / 2)),
233 .signatureS = copy_into<TPM2B_ECC_PARAMETER>(sig_data.last(sig_len / 2)),
234 },
235 },
236 };
237 }
238};
239
240} // namespace
241
242std::unique_ptr<PK_Ops::Verification> EC_PublicKey::create_verification_op(std::string_view params,
243 std::string_view provider) const {
244 BOTAN_UNUSED(provider);
245 return std::make_unique<EC_Verification_Operation>(handles(), sessions(), params);
246}
247
249 std::string_view params,
250 std::string_view provider) const {
251 BOTAN_UNUSED(rng, provider);
252 return std::make_unique<EC_Signature_Operation>(handles(), sessions(), params);
253}
254
255} // 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_ARG_CHECK(expr, msg)
Definition assert.h:29
const OID & get_curve_oid() const
Definition ec_group.cpp:478
const EC_Group & domain() const
Definition ecc_key.cpp:59
std::vector< uint8_t > raw_public_key_bits() const override
Definition ecc_key.cpp:82
EC_PublicKey()=default
const EC_Point & public_point() const
Definition ecc_key.cpp:64
static OID from_string(std::string_view str)
Definition asn1_oid.cpp:86
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_ecc.cpp:57
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, const EC_Group &group)
Definition tpm2_ecc.cpp:61
std::unique_ptr< Public_Key > public_key() const override
Definition tpm2_ecc.cpp:41
std::unique_ptr< PK_Ops::Signature > create_signature_op(Botan::RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition tpm2_ecc.cpp:248
std::vector< uint8_t > public_key_bits() const override
Definition tpm2_ecc.cpp:53
std::vector< uint8_t > public_key_bits() const override
Definition tpm2_ecc.cpp:45
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_ecc.cpp:49
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition tpm2_ecc.cpp:242
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
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_key.cpp:279
const SessionBundle & sessions() const
Definition tpm2_key.h:224
std::vector< uint8_t > raw_public_key_bits() const override
Definition tpm2_key.cpp:157
const Object & handles() const
Definition tpm2_key.h:99
const SessionBundle & sessions() const
Definition tpm2_key.h:101
int(* final)(unsigned char *, CTX *)
constexpr T init_empty()
Create an empty TPM2 buffer of the given type.
Definition tpm2_util.h:142
std::optional< size_t > curve_id_order_byte_size(TPMI_ECC_CURVE curve_id)
TPMI_ALG_HASH get_tpm2_hash_type(std::string_view hash_name)
std::optional< TPM2_ECC_CURVE > get_tpm2_curve_id(const OID &curve_oid)
constexpr void copy_into(T &dest, std::span< const uint8_t > data)
Definition tpm2_util.h:117
constexpr auto as_span(tpm2_buffer auto &data)
Construct a std::span as a view into a TPM2 buffer.
Definition tpm2_util.h:102
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr auto concat(Rs &&... ranges)
Definition stl_util.h:263
static TPMA_OBJECT render(ObjectAttributes attributes)