Botan 3.4.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 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/hash.h>
13#include <botan/reducer.h>
14#include <botan/rng.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/keypair.h>
17#include <botan/internal/parsing.h>
18#include <botan/internal/pk_ops_impl.h>
19#include <botan/internal/point_mul.h>
20#include <botan/internal/scan_name.h>
21
22namespace Botan {
23
24std::unique_ptr<Public_Key> ECKCDSA_PrivateKey::public_key() const {
25 return std::make_unique<ECKCDSA_PublicKey>(domain(), public_point());
26}
27
29 if(!EC_PrivateKey::check_key(rng, strong)) {
30 return false;
31 }
32
33 if(!strong) {
34 return true;
35 }
36
37 return KeyPair::signature_consistency_check(rng, *this, "SHA-256");
38}
39
40namespace {
41
42std::unique_ptr<HashFunction> eckcdsa_signature_hash(std::string_view padding) {
43 if(auto hash = HashFunction::create(padding)) {
44 return hash;
45 }
46
47 SCAN_Name req(padding);
48
49 if(req.algo_name() == "EMSA1" && req.arg_count() == 1) {
50 if(auto hash = HashFunction::create(req.arg(0))) {
51 return hash;
52 }
53 }
54
55 // intentionally not supporting Raw for ECKCDSA, we need to know
56 // the length in advance which complicates the logic for Raw
57
58 throw Algorithm_Not_Found(padding);
59}
60
61std::unique_ptr<HashFunction> eckcdsa_signature_hash(const AlgorithmIdentifier& alg_id) {
62 const auto oid_info = split_on(alg_id.oid().to_formatted_string(), '/');
63
64 if(oid_info.size() != 2 || oid_info[0] != "ECKCDSA") {
65 throw Decoding_Error(fmt("Unexpected AlgorithmIdentifier OID {} in association with ECKCDSA key", alg_id.oid()));
66 }
67
68 if(!alg_id.parameters_are_empty()) {
69 throw Decoding_Error("Unexpected non-empty AlgorithmIdentifier parameters for ECKCDSA");
70 }
71
72 return HashFunction::create_or_throw(oid_info[1]);
73}
74
75std::vector<uint8_t> eckcdsa_prefix(const PointGFp& point, size_t order_bytes, size_t hash_block_size) {
76 const BigInt public_x = point.get_affine_x();
77 const BigInt public_y = point.get_affine_y();
78
79 std::vector<uint8_t> prefix(2 * order_bytes);
80 BigInt::encode_1363(&prefix[0], order_bytes, public_x);
81 BigInt::encode_1363(&prefix[order_bytes], order_bytes, public_y);
82
83 // Either truncate or zero-extend to match the hash block size
84 prefix.resize(hash_block_size);
85
86 return prefix;
87}
88
89/**
90 * @brief Truncate hash output if needed.
91 *
92 * If the output length of the hash function exceeds the size of the group order,
93 * ISO/IEC 14888-3:2018 specifies a truncation of the hash output
94 * when calculating the witness R (the first part of the signature) and H.
95 *
96 * The truncation is specified as follows:
97 *
98 * R = I2BS(beta', BS2I(gamma, R) mod 2^beta')
99 * H = I2BS(beta', BS2I(gamma, H) mod 2^beta')
100 *
101 * where
102 * - gamma: the output bit-length of the hash-function
103 * - beta: the bit-length of the prime number q (i.e. the group order size)
104 * - beta' = 8 * ceil(beta / 8)
105 *
106 * This essentially means a truncation on the byte level
107 * happens from the low side of the hash.
108 *
109 * @param[in,out] digest The hash output to potentially truncate.
110 * @param[in] group_order_bytes Size of the group order.
111 */
112void truncate_hash_if_needed(secure_vector<uint8_t>& digest, size_t group_order_bytes) {
113 if(digest.size() > group_order_bytes) {
114 const size_t bytes_to_truncate = digest.size() - group_order_bytes;
115 digest.erase(digest.begin(), digest.begin() + bytes_to_truncate);
116 }
117}
118
119/**
120* ECKCDSA signature operation
121*/
122class ECKCDSA_Signature_Operation final : public PK_Ops::Signature {
123 public:
124 ECKCDSA_Signature_Operation(const ECKCDSA_PrivateKey& eckcdsa, std::string_view padding) :
125 m_group(eckcdsa.domain()),
126 m_x(eckcdsa.private_value()),
127 m_hash(eckcdsa_signature_hash(padding)),
128 m_prefix_used(false) {
129 m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
130 }
131
132 void update(const uint8_t msg[], size_t msg_len) override {
133 if(!m_prefix_used) {
134 m_hash->update(m_prefix.data(), m_prefix.size());
135 m_prefix_used = true;
136 }
137 m_hash->update(msg, msg_len);
138 }
139
140 secure_vector<uint8_t> sign(RandomNumberGenerator& rng) override {
141 m_prefix_used = false;
142 secure_vector<uint8_t> digest = m_hash->final();
143 truncate_hash_if_needed(digest, m_group.get_order_bytes());
144 return raw_sign(digest.data(), digest.size(), rng);
145 }
146
147 size_t signature_length() const override { return 2 * m_group.get_order_bytes(); }
148
149 AlgorithmIdentifier algorithm_identifier() const override;
150
151 std::string hash_function() const override { return m_hash->name(); }
152
153 private:
154 secure_vector<uint8_t> raw_sign(const uint8_t msg[], size_t msg_len, RandomNumberGenerator& rng);
155
156 const EC_Group m_group;
157 const BigInt m_x;
158 std::unique_ptr<HashFunction> m_hash;
159 std::vector<uint8_t> m_prefix;
160 std::vector<BigInt> m_ws;
161 bool m_prefix_used;
162};
163
164AlgorithmIdentifier ECKCDSA_Signature_Operation::algorithm_identifier() const {
165 const std::string full_name = "ECKCDSA/" + m_hash->name();
166 const OID oid = OID::from_string(full_name);
167 return AlgorithmIdentifier(oid, AlgorithmIdentifier::USE_EMPTY_PARAM);
168}
169
170secure_vector<uint8_t> ECKCDSA_Signature_Operation::raw_sign(const uint8_t msg[],
171 size_t msg_len,
172 RandomNumberGenerator& rng) {
173 const BigInt k = m_group.random_scalar(rng);
174 const BigInt k_times_P_x = m_group.blinded_base_point_multiply_x(k, rng, m_ws);
175
176 secure_vector<uint8_t> to_be_hashed(k_times_P_x.bytes());
177 k_times_P_x.binary_encode(to_be_hashed.data());
178
179 auto hash = m_hash->new_object();
180 hash->update(to_be_hashed);
181 secure_vector<uint8_t> c = hash->final();
182 truncate_hash_if_needed(c, m_group.get_order_bytes());
183
184 const BigInt r(c.data(), c.size());
185
186 BOTAN_ASSERT_NOMSG(msg_len == c.size());
187 xor_buf(c, msg, c.size());
188 BigInt w(c.data(), c.size());
189 w = m_group.mod_order(w);
190
191 const BigInt s = m_group.multiply_mod_order(m_x, k - w);
192 if(s.is_zero()) {
193 throw Internal_Error("During ECKCDSA signature generation created zero s");
194 }
195
196 secure_vector<uint8_t> output = BigInt::encode_1363(r, c.size());
197 output += BigInt::encode_1363(s, m_group.get_order_bytes());
198 return output;
199}
200
201/**
202* ECKCDSA verification operation
203*/
204class ECKCDSA_Verification_Operation final : public PK_Ops::Verification {
205 public:
206 ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, std::string_view padding) :
207 m_group(eckcdsa.domain()),
208 m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
209 m_hash(eckcdsa_signature_hash(padding)),
210 m_prefix_used(false) {
211 m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
212 }
213
214 ECKCDSA_Verification_Operation(const ECKCDSA_PublicKey& eckcdsa, const AlgorithmIdentifier& alg_id) :
215 m_group(eckcdsa.domain()),
216 m_gy_mul(m_group.get_base_point(), eckcdsa.public_point()),
217 m_hash(eckcdsa_signature_hash(alg_id)),
218 m_prefix_used(false) {
219 m_prefix = eckcdsa_prefix(eckcdsa.public_point(), m_group.get_order_bytes(), m_hash->hash_block_size());
220 }
221
222 void update(const uint8_t msg[], size_t msg_len) override;
223
224 bool is_valid_signature(const uint8_t sig[], size_t sig_len) override;
225
226 std::string hash_function() const override { return m_hash->name(); }
227
228 private:
229 bool verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len);
230
231 const EC_Group m_group;
232 const EC_Point_Multi_Point_Precompute m_gy_mul;
233 std::vector<uint8_t> m_prefix;
234 std::unique_ptr<HashFunction> m_hash;
235 bool m_prefix_used;
236};
237
238void ECKCDSA_Verification_Operation::update(const uint8_t msg[], size_t msg_len) {
239 if(!m_prefix_used) {
240 m_prefix_used = true;
241 m_hash->update(m_prefix.data(), m_prefix.size());
242 }
243 m_hash->update(msg, msg_len);
244}
245
246bool ECKCDSA_Verification_Operation::is_valid_signature(const uint8_t sig[], size_t sig_len) {
247 m_prefix_used = false;
248 secure_vector<uint8_t> digest = m_hash->final();
249 truncate_hash_if_needed(digest, m_group.get_order_bytes());
250 return verify(digest.data(), digest.size(), sig, sig_len);
251}
252
253bool ECKCDSA_Verification_Operation::verify(const uint8_t msg[], size_t msg_len, const uint8_t sig[], size_t sig_len) {
254 //calculate size of r
255
256 const size_t order_bytes = m_group.get_order_bytes();
257
258 const size_t size_r = std::min(msg_len, order_bytes);
259 if(sig_len != size_r + order_bytes) {
260 return false;
261 }
262
263 secure_vector<uint8_t> r(sig, sig + size_r);
264
265 // check that 0 < s < q
266 const BigInt s(sig + size_r, order_bytes);
267
268 if(s <= 0 || s >= m_group.get_order()) {
269 return false;
270 }
271
272 secure_vector<uint8_t> r_xor_e(r);
273 xor_buf(r_xor_e, msg, r.size());
274 BigInt w(r_xor_e.data(), r_xor_e.size());
275 w = m_group.mod_order(w);
276
277 const EC_Point q = m_gy_mul.multi_exp(w, s);
278 if(q.is_zero()) {
279 return false;
280 }
281
282 const BigInt q_x = q.get_affine_x();
283 secure_vector<uint8_t> c(q_x.bytes());
284 q_x.binary_encode(c.data());
285 auto c_hash = m_hash->new_object();
286 c_hash->update(c.data(), c.size());
287 secure_vector<uint8_t> v = c_hash->final();
288 truncate_hash_if_needed(v, m_group.get_order_bytes());
289
290 return (v == r);
291}
292
293} // namespace
294
295std::unique_ptr<Private_Key> ECKCDSA_PublicKey::generate_another(RandomNumberGenerator& rng) const {
296 return std::make_unique<ECKCDSA_PrivateKey>(rng, domain());
297}
298
299std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_verification_op(std::string_view params,
300 std::string_view provider) const {
301 if(provider == "base" || provider.empty()) {
302 return std::make_unique<ECKCDSA_Verification_Operation>(*this, params);
303 }
304 throw Provider_Not_Found(algo_name(), provider);
305}
306
307std::unique_ptr<PK_Ops::Verification> ECKCDSA_PublicKey::create_x509_verification_op(
308 const AlgorithmIdentifier& signature_algorithm, std::string_view provider) const {
309 if(provider == "base" || provider.empty()) {
310 return std::make_unique<ECKCDSA_Verification_Operation>(*this, signature_algorithm);
311 }
312
313 throw Provider_Not_Found(algo_name(), provider);
314}
315
316std::unique_ptr<PK_Ops::Signature> ECKCDSA_PrivateKey::create_signature_op(RandomNumberGenerator& /*rng*/,
317 std::string_view params,
318 std::string_view provider) const {
319 if(provider == "base" || provider.empty()) {
320 return std::make_unique<ECKCDSA_Signature_Operation>(*this, params);
321 }
322 throw Provider_Not_Found(algo_name(), provider);
323}
324
325} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
static secure_vector< uint8_t > encode_1363(const BigInt &n, size_t bytes)
Definition big_code.cpp:105
std::unique_ptr< PK_Ops::Signature > create_signature_op(RandomNumberGenerator &rng, std::string_view params, std::string_view provider) const override
Definition eckcdsa.cpp:316
std::unique_ptr< Public_Key > public_key() const override
Definition eckcdsa.cpp:24
bool check_key(RandomNumberGenerator &rng, bool) const override
Definition eckcdsa.cpp:28
std::unique_ptr< PK_Ops::Verification > create_x509_verification_op(const AlgorithmIdentifier &signature_algorithm, std::string_view provider) const override
Definition eckcdsa.cpp:307
std::unique_ptr< PK_Ops::Verification > create_verification_op(std::string_view params, std::string_view provider) const override
Definition eckcdsa.cpp:299
std::unique_ptr< Private_Key > generate_another(RandomNumberGenerator &rng) const final
Definition eckcdsa.cpp:295
BigInt blinded_base_point_multiply_x(const BigInt &k, RandomNumberGenerator &rng, std::vector< BigInt > &ws) const
Definition ec_group.cpp:581
BigInt mod_order(const BigInt &x) const
Definition ec_group.cpp:524
BigInt multiply_mod_order(const BigInt &x, const BigInt &y) const
Definition ec_group.cpp:532
const BigInt & get_order() const
Definition ec_group.cpp:508
BigInt random_scalar(RandomNumberGenerator &rng) const
Definition ec_group.cpp:592
size_t get_order_bytes() const
Definition ec_group.cpp:488
EC_Point multi_exp(const BigInt &k1, const BigInt &k2) const
BigInt get_affine_x() const
Definition ec_point.cpp:469
bool check_key(RandomNumberGenerator &rng, bool strong) const override
Definition ecc_key.cpp:161
const EC_Group & domain() const
Definition ecc_key.h:54
const EC_Point & public_point() const
Definition ecc_key.h:40
static std::unique_ptr< HashFunction > create_or_throw(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:298
static std::unique_ptr< HashFunction > create(std::string_view algo_spec, std::string_view provider="")
Definition hash.cpp:107
int(* update)(CTX *, const void *, CC_LONG len)
int(* final)(unsigned char *, CTX *)
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
EC_Point PointGFp
Definition ec_point.h:403
std::vector< std::string > split_on(std::string_view str, char delim)
Definition parsing.cpp:111
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:343