Botan 3.13.0
Crypto and TLS for C&
eckcdsa.cpp
Go to the documentation of this file.
1/*
2* ECKCDSA (ISO/IEC 14888-3:2006/Cor.2:2009)
3* (C) 2016 René Korthaus, Sirrix AG
4* (C) 2018,2024 Jack Lloyd
5* (C) 2023 Philippe Lieser - Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/eckcdsa.h>
11
12#include <botan/ec_group.h>
13#include <botan/hash.h>
14#include <botan/mem_ops.h>
15#include <botan/rng.h>
16#include <botan/internal/concat_util.h>
17#include <botan/internal/fmt.h>
18#include <botan/internal/keypair.h>
19#include <botan/internal/parsing.h>
20#include <botan/internal/pk_ops_impl.h>
21#include <botan/internal/scan_name.h>
22
23namespace Botan {
24
25std::unique_ptr<Public_Key> ECKCDSA_PrivateKey::public_key() const {
26 return std::make_unique<ECKCDSA_PublicKey>(domain(), _public_ec_point());
27}
28
30 if(!EC_PrivateKey::check_key(rng, strong)) {
31 return false;
32 }
33
34 if(!strong) {
35 return true;
36 }
37
38 return KeyPair::signature_consistency_check(rng, *this, "SHA-256");
39}
40
41namespace {
42
43std::unique_ptr<HashFunction> eckcdsa_signature_hash(std::string_view padding) {
44 if(auto hash = HashFunction::create(padding)) {
45 return hash;
46 }
47
48 const SCAN_Name req(padding);
49
50 if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
51 if(auto hash = HashFunction::create(req.arg(0))) {
52 return hash;
53 }
54 }
55
56 // intentionally not supporting Raw for ECKCDSA, we need to know
57 // the length in advance which complicates the logic for Raw
58
59 throw Algorithm_Not_Found(padding);
60}
61
62std::unique_ptr<HashFunction> eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) {
63 if(const auto name = alg_id.oid().registered_name()) {
64 const auto alg_info = split_on(*name, '/');
65
66 if(alg_info.size() == 2 && alg_info[0] == "ECKCDSA") {
67 if(!alg_id.parameters_are_empty()) {
68 throw Decoding_Error("Unexpected non-empty AlgorithmIdentifier parameters for ECKCDSA");
69 }
70
71 return HashFunction::create_or_throw(alg_info[1]);
72 }
73 }
74
75 throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier OID {} in association with ECKCDSA key", alg_id.oid()));
76}
77
78std::vector<uint8_t> eckcdsa_prefix(const EC_AffinePoint& point, size_t hash_block_size) {
79 auto prefix = point.xy_bytes<std::vector<uint8_t>>();
80
81 // Either truncate or zero-extend to match the hash block size
82 prefix.resize(hash_block_size);
83
84 return prefix;
85}
86
87/**
88 * @brief Truncate hash output if needed.
89 *
90 * If the output length of the hash function exceeds the size of the group order,
91 * ISO/IEC 14888-3:2018 specifies a truncation of the hash output
92 * when calculating the witness R (the first part of the signature) and H.
93 *
94 * The truncation is specified as follows:
95 *
96 * R = I2BS(beta', BS2I(gamma, R) mod 2^beta')
97 * H = I2BS(beta', BS2I(gamma, H) mod 2^beta')
98 *
99 * where
100 * - gamma: the output bit-length of the hash-function
101 * - beta: the bit-length of the prime number q (i.e. the group order size)
102 * - beta' = 8 * ceil(beta / 8)
103 *
104 * This essentially means a truncation on the byte level
105 * happens from the low side of the hash.
106 *
107 * @param[in,out] digest The hash output to potentially truncate.
108 * @param[in] group_order_bytes Size of the group order.
109 */
110void truncate_hash_if_needed(std::vector<uint8_t>& digest, size_t group_order_bytes) {
111 if(digest.size() > group_order_bytes) {
112 const size_t bytes_to_truncate = digest.size() - group_order_bytes;
113 digest.erase(digest.begin(), digest.begin() + bytes_to_truncate);
114 }
115}
116
117/**
118* ECKCDSA signature operation
119*/
120class ECKCDSA_Signature_Operation final : public PK_Ops::Signature {
121 public:
122 ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa, std::string_view padding) :
123 m_group(eckcdsa.domain()),
124 m_x(eckcdsa._private_key()),
125 m_hash(eckcdsa_signature_hash(padding)),
126 m_prefix(eckcdsa_prefix(eckcdsa._public_ec_point(), m_hash->hash_block_size())),
127 m_prefix_used(false) {}
128
129 void update(std::span<const uint8_t> input) override {
130 if(!m_prefix_used) {
131 m_hash->update(m_prefix);
132 m_prefix_used = true;
133 }
134 m_hash->update(input);
135 }
136
137 std::vector<uint8_t> sign(RandomNumberGenerator& rng) override {
138 m_prefix_used = false;
139 std::vector<uint8_t> digest = m_hash->final_stdvec();
140 truncate_hash_if_needed(digest, m_group.get_order_bytes());
141 return raw_sign(digest, rng);
142 }
143
144 size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
145
146 AlgorithmIdentifier algorithm_identifier() const override;
147
148 std::string hash_function() const override { return m_hash->name(); }
149
150 private:
151 std::vector<uint8_t> raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng);
152
153 const EC_Group m_group;
154 const EC_Scalar m_x;
155 std::unique_ptr<HashFunction> m_hash;
156 std::vector<uint8_t> m_prefix;
157 bool m_prefix_used;
158};
159
160AlgorithmIdentifier ECKCDSA_Signature_Operation::algorithm_identifier() const {
161 const std::string full_name = "ECKCDSA/" + m_hash->name();
162 const OID oid = OID::from_string(full_name);
163 return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
164}
165
166std::vector<uint8_t> ECKCDSA_Signature_Operation::raw_sign(std::span<const uint8_t> msg, RandomNumberGenerator& rng) {
167 const auto k = EC_Scalar::random(m_group, rng);
168
169 // We cannot use gk_x_mod_order because ECKCDSA, unlike ECDSA or ECGDSA, does
170 // not reduce the x coordinate modulo the group order.
171 m_hash->update(EC_AffinePoint::g_mul(k, rng).x_bytes());
172 auto c = m_hash->final_stdvec();
173 truncate_hash_if_needed(c, m_group.get_order_bytes());
174
175 const auto r = c;
176
177 xor_buf(c, msg);
178 const auto w = EC_Scalar::from_bytes_mod_order(m_group, c);
179
180 const auto s = m_x * (k - w);
181 if(s.is_zero()) {
182 throw Internal_Error("During ECKCDSA signature generation created zero s");
183 }
184
185 return concat(r, s.serialize());
186}
187
188/**
189* ECKCDSA verification operation
190*/
191class ECKCDSA_Verification_Operation final : public PK_Ops::Verification {
192 public:
193 ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, std::string_view padding) :
194 m_group(eckcdsa.domain()),
195 m_gy_mul(eckcdsa._public_ec_point()),
196 m_hash(eckcdsa_signature_hash(padding)),
197 m_prefix(eckcdsa_prefix(eckcdsa._public_ec_point(), m_hash->hash_block_size())),
198 m_prefix_used(false) {}
199
200 ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, const AlgorithmIdentifier& alg_id) :
201 m_group(eckcdsa.domain()),
202 m_gy_mul(eckcdsa._public_ec_point()),
203 m_hash(eckcdsa_signature_hash(alg_id)),
204 m_prefix(eckcdsa_prefix(eckcdsa._public_ec_point(), m_hash->hash_block_size())),
205 m_prefix_used(false) {}
206
207 void update(std::span<const uint8_t> msg) override;
208
209 bool is_valid_signature(std::span<const uint8_t> sig) override;
210
211 std::string hash_function() const override { return m_hash->name(); }
212
213 private:
214 bool verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig);
215
216 const EC_Group m_group;
217 const EC_Group::Mul2Table m_gy_mul;
218 std::unique_ptr<HashFunction> m_hash;
219 std::vector<uint8_t> m_prefix;
220 bool m_prefix_used;
221};
222
223void ECKCDSA_Verification_Operation::update(std::span<const uint8_t> msg) {
224 if(!m_prefix_used) {
225 m_prefix_used = true;
226 m_hash->update(m_prefix.data(), m_prefix.size());
227 }
228 m_hash->update(msg);
229}
230
231bool ECKCDSA_Verification_Operation::is_valid_signature(std::span<const uint8_t> sig) {
232 m_prefix_used = false;
233 std::vector<uint8_t> digest = m_hash->final_stdvec();
234 truncate_hash_if_needed(digest, m_group.get_order_bytes());
235 return verify(digest, sig);
236}
237
238bool ECKCDSA_Verification_Operation::verify(std::span<const uint8_t> msg, std::span<const uint8_t> sig) {
239 const size_t order_bytes = m_group.get_order_bytes();
240
241 const size_t size_r = std::min(msg.size(), order_bytes);
242 if(sig.size() != size_r + order_bytes) {
243 return false;
244 }
245
246 auto r = sig.first(size_r);
247
248 if(auto s = EC_Scalar::deserialize(m_group, sig.last(order_bytes))) {
249 std::vector<uint8_t> r_xor_e(r.size());
250 xor_buf(r_xor_e, r, msg.first(size_r));
251
252 const auto w = EC_Scalar::from_bytes_mod_order(m_group, r_xor_e);
253
254 if(auto q = m_gy_mul.mul2_vartime(w, s.value())) {
255 std::vector<uint8_t> v = m_hash->process<std::vector<uint8_t>>(q->x_bytes());
256 truncate_hash_if_needed(v, m_group.get_order_bytes());
257 return constant_time_compare(v, r);
258 }
259 }
260
261 return false;
262}
263
264} // namespace
265
267 return domain().get_order_bytes();
268}
269
270std::unique_ptr<Private_Key> ECKCDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const {
271 return std::make_unique<ECKCDSA_PrivateKey>(rng, domain());
272}
273
274std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_verification_op(std::string_view params,
275 std::string_view provider) const {
276 if(provider == "base" || provider.empty()) {
277 return std::make_unique<ECKCDSA_Verification_Operation>(*this, params);
278 }
279 throw Provider_Not_Found(algo_name(), provider);
280}
281
282std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_x509_verification_op(
283 const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
284 if(provider == "base" || provider.empty()) {
285 return std::make_unique<ECKCDSA_Verification_Operation>(*this, signature_algorithm);
286 }
287
288 throw Provider_Not_Found(algo_name(), provider);
289}
290
291std::unique_ptr<PK_Ops::Signature> ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
292 std::string_view params,
293 std::string_view provider) const {
294 if(provider == "base" || provider.empty()) {
295 return std::make_unique<ECKCDSA_Signature_Operation>(*this, params);
296 }
297 throw Provider_Not_Found(algo_name(), provider);
298}
299
300} // namespace Botan
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition eckcdsa.cpp:29
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition eckcdsa.cpp:291
std::unique_ptr< Public_Key > public_key() const override
Definition eckcdsa.cpp:25
std::unique_ptr< PK_Ops::Verification > create_x509_verification_op(const AlgorithmIdentifier &signature_algorithm, std::string_view provider) const override
Definition eckcdsa.cpp:282
std::string algo_name() const override
Definition eckcdsa.h:47
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition eckcdsa.cpp:274
std::optional< size_t > _signature_element_size_for_DER_encoding() const override
Definition eckcdsa.cpp:266
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition eckcdsa.cpp:270
std::optional< EC_AffinePoint > mul2_vartime(const EC_Scalar &x, const EC_Scalar &y) const
Definition ec_group.cpp:915
size_t get_order_bytes() const
Definition ec_group.cpp:666
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition ecc_key.cpp:235
const EC_Group & domain() const
Definition ecc_key.cpp:76
const EC_AffinePoint & _public_ec_point() const
Definition ecc_key.cpp:88
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:308
static std::unique_ptr< HashFunction > create(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:111
bool signature_consistency_check(RandomNumberGenerator &rng, const Private_Key &private_key, const Public_Key &public_key, std::string_view padding)
Definition keypair.cpp:49
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::vector< std::string > split_on(std::string_view str, char delim)
Definition parsing.cpp:141
constexpr auto concat(Rs &&... ranges)
Definition concat_util.h:90
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
bool constant_time_compare(std::span< const uint8_t > x, std::span< const uint8_t > y)
Definition mem_ops.cpp:17