• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 4583199153

pending completion
4583199153

Pull #1350

github

GitHub
Merge 0cf19ee36 into 319037208
Pull Request #1350: Add is_hybrid_key, guard against empty data_slice null data pointer.

6 of 6 new or added lines in 1 file covered. (100.0%)

9441 of 11341 relevant lines covered (83.25%)

6733080.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.98
/src/crypto/secp256k1.cpp
1
/**
2
 * Copyright (c) 2011-2022 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/system/crypto/secp256k1.hpp>
20

21
#include <algorithm>
22
#include <utility>
23
#include <secp256k1.h>
24
#include <secp256k1_recovery.h>
25
#include <bitcoin/system/crypto/der_parser.hpp>
26
#include <bitcoin/system/data/data.hpp>
27
#include <bitcoin/system/hash/hash.hpp>
28
#include <bitcoin/system/math/math.hpp>
29
#include "ec_context.hpp"
30

31
namespace libbitcoin {
32
namespace system {
33

34
static constexpr auto ec_success = 1;
35
static constexpr auto maximum_recovery_id = 3;
36
static constexpr auto compressed_even = 0x02_u8;
37
static constexpr auto compressed_odd = 0x03_u8;
38
static constexpr auto uncompressed = 0x04_u8;
39
static constexpr auto hybrid_even = 0x06_u8;
40
static constexpr auto hybrid_odd = 0x07_u8;
41

42
constexpr int to_flags(bool compressed) NOEXCEPT
43
{
44
    return compressed ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED;
45
}
46

47
// Helper functions.
48
// ----------------------------------------------------------------------------
49
// The templates allow strong typing of private keys without redundant code.
50

51
bool parse(const secp256k1_context* context, secp256k1_pubkey& out,
486✔
52
    const data_slice& point) NOEXCEPT
53
{
54
    if (point.empty())
486✔
55
        return false;
56

57
    // secp256k1_ec_pubkey_parse supports compressed (33 bytes, header byte 0x02
58
    // or 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes,
59
    // header byte 0x06 or 0x07) format public keys.
60
    return secp256k1_ec_pubkey_parse(context, &out, point.data(), point.size())
486✔
61
        == ec_success;
486✔
62
}
63

64
bool parse(const secp256k1_context* context, std::vector<secp256k1_pubkey>& out,
95✔
65
    const compressed_list& points) NOEXCEPT
66
{
67
    out.resize(points.size());
95✔
68
    auto key = out.begin();
95✔
69

70
    for (const auto& point: points)
287✔
71
        if (!parse(context, *key++, point))
192✔
72
            return false;
×
73

74
    return true;
95✔
75
}
76

77
// Create an array of secp256k1_pubkey pointers for secp256k1 call.
78
std::vector<const secp256k1_pubkey*> to_pointers(
95✔
79
    const std::vector<secp256k1_pubkey>& keys) NOEXCEPT
80
{
81
    std::vector<const secp256k1_pubkey*> pointers(keys.size());
95✔
82

83
    std::transform(keys.begin(), keys.end(), pointers.begin(),
95✔
84
        [](const secp256k1_pubkey& point) NOEXCEPT
85
        {
86
            return &point;
87
        });
88

89
    return pointers;
95✔
90
}
91

92
template <size_t Size>
93
bool serialize(const secp256k1_context* context, data_array<Size>& out,
723✔
94
    const secp256k1_pubkey point) NOEXCEPT
95
{
96
    auto size = Size;
723✔
97
    constexpr auto flags = to_flags(Size == ec_compressed_size);
98
    secp256k1_ec_pubkey_serialize(context, out.data(), &size, &point, flags);
723✔
99
    return size == Size;
723✔
100
}
101

102
// parse, add, serialize
103
template <size_t Size>
104
bool ec_add(const secp256k1_context* context, data_array<Size>& in_out,
12✔
105
    const ec_secret& secret) NOEXCEPT
106
{
107
    secp256k1_pubkey pubkey;
108
    return parse(context, pubkey, in_out) &&
24✔
109
        secp256k1_ec_pubkey_tweak_add(context, &pubkey, secret.data()) ==
12✔
110
            ec_success && serialize(context, in_out, pubkey);
23✔
111
}
112

113
// parse, multiply, serialize
114
template <size_t Size>
115
bool ec_multiply(const secp256k1_context* context, data_array<Size>& in_out,
200✔
116
    const ec_secret& secret) NOEXCEPT
117
{
118
    secp256k1_pubkey pubkey;
119
    return parse(context, pubkey, in_out) &&
400✔
120
        secp256k1_ec_pubkey_tweak_mul(context, &pubkey, secret.data()) ==
200✔
121
            ec_success && serialize(context, in_out, pubkey);
400✔
122
}
123

124
// parse, negate, serialize
125
template <size_t Size>
126
bool ec_negate(const secp256k1_context* context,
×
127
    data_array<Size>& in_out) NOEXCEPT
128
{
129
    secp256k1_pubkey pubkey;
130
    return parse(context, pubkey, in_out) &&
×
131
        secp256k1_ec_pubkey_negate(context, &pubkey) == ec_success &&
×
132
        serialize(context, in_out, pubkey);
×
133
}
134

135
// create, serialize (secrets are normal)
136
template <size_t Size>
137
bool secret_to_public(const secp256k1_context* context, data_array<Size>& out,
381✔
138
    const ec_secret& secret) NOEXCEPT
139
{
140
    secp256k1_pubkey pubkey;
141
    return secp256k1_ec_pubkey_create(context, &pubkey, secret.data()) ==
381✔
142
        ec_success && serialize(context, out, pubkey);
381✔
143
}
144

145
// parse, recover, serialize
146
template <size_t Size>
147
bool recover_public(const secp256k1_context* context, data_array<Size>& out,
6✔
148
    const recoverable_signature& recoverable, const hash_digest& hash) NOEXCEPT
149
{
150
    secp256k1_pubkey pubkey;
151
    secp256k1_ecdsa_recoverable_signature sign;
152
    const auto recovery_id = static_cast<int>(recoverable.recovery_id);
6✔
153
    return
154
        secp256k1_ecdsa_recoverable_signature_parse_compact(context,
6✔
155
            &sign, recoverable.signature.data(), recovery_id) == ec_success &&
6✔
156
        secp256k1_ecdsa_recover(context, &pubkey, &sign, hash.data()) ==
6✔
157
            ec_success && serialize(context, out, pubkey);
12✔
158
}
159

160
// parsed - normalize, verify
161
bool verify_signature(const secp256k1_context* context,
52✔
162
    const secp256k1_pubkey& point, const hash_digest& hash,
163
    const ec_signature& signature) NOEXCEPT
164
{
165
    const auto parsed = pointer_cast<const secp256k1_ecdsa_signature>(
52✔
166
        signature.data());
167

168
    secp256k1_ecdsa_signature normal;
52✔
169
    secp256k1_ecdsa_signature_normalize(context, &normal, parsed);
52✔
170

171
    // BIP62 required low-s signatures, but that is not active.
172
    // secp256k1_ecdsa_verify rejects non-normalized (low-s) signatures, but
173
    // bitcoin does not have such a limitation, so we always normalize.
174
    return secp256k1_ecdsa_verify(context, &normal, hash.data(), &point) ==
52✔
175
        ec_success;
52✔
176
}
177

178
// Add EC values
179
// ----------------------------------------------------------------------------
180

181
bool ec_add(ec_compressed& point, const ec_secret& scalar) NOEXCEPT
12✔
182
{
183
    auto const* context = ec_context_verify::context();
12✔
184
    return ec_add(context, point, scalar);
12✔
185
}
186

187
bool ec_add(ec_uncompressed& point, const ec_secret& scalar) NOEXCEPT
×
188
{
189
    auto const* context = ec_context_verify::context();
×
190
    return ec_add(context, point, scalar);
×
191
}
192

193
// secrets are normal
194
bool ec_add(ec_secret& left, const ec_secret& right) NOEXCEPT
73✔
195
{
196
    auto const* context = ec_context_verify::context();
73✔
197
    return secp256k1_ec_seckey_tweak_add(context, left.data(),
73✔
198
        right.data()) == 1;
73✔
199
}
200

201
bool ec_add(ec_compressed& left, const ec_compressed& right) NOEXCEPT
94✔
202
{
203
    return ec_sum(left, { left, right });
94✔
204
}
205

206
bool ec_add(ec_compressed& left, const ec_uncompressed& right) NOEXCEPT
×
207
{
208
    ec_compressed out;
×
209
    return compress(out, right) && ec_add(left, out);
×
210
}
211

212
// parse, combine, serialize
213
bool ec_sum(ec_compressed& out, const compressed_list& points) NOEXCEPT
95✔
214
{
215
    if (points.empty())
95✔
216
        return false;
217

218
    auto const* context = ec_context_verify::context();
95✔
219

220
    std::vector<secp256k1_pubkey> keys;
95✔
221
    if (!parse(context, keys, points))
95✔
222
        return false;
223

224
    secp256k1_pubkey pubkey;
95✔
225
    return secp256k1_ec_pubkey_combine(context, &pubkey,
95✔
226
        to_pointers(keys).data(), points.size()) == 
190✔
227
            ec_success && serialize(context, out, pubkey);
95✔
228
}
229

230
// Multiply EC values
231
// ----------------------------------------------------------------------------
232

233
bool ec_multiply(ec_compressed& point, const ec_secret& scalar) NOEXCEPT
200✔
234
{
235
    auto const* context = ec_context_verify::context();
200✔
236
    return ec_multiply(context, point, scalar);
200✔
237
}
238

239
bool ec_multiply(ec_uncompressed& point, const ec_secret& scalar) NOEXCEPT
×
240
{
241
    auto const* context = ec_context_verify::context();
×
242
    return ec_multiply(context, point, scalar);
×
243
}
244

245
// secrets are normal
246
bool ec_multiply(ec_secret& left, const ec_secret& right) NOEXCEPT
17✔
247
{
248
    auto const* context = ec_context_verify::context();
17✔
249
    return secp256k1_ec_seckey_tweak_mul(context, left.data(),
17✔
250
        right.data()) == ec_success;
17✔
251
}
252

253
// Negate EC values
254
// ----------------------------------------------------------------------------
255

256
// secrets are normal
257
bool ec_negate(ec_secret& scalar) NOEXCEPT
39✔
258
{
259
    auto const* context = ec_context_verify::context();
39✔
260
    return secp256k1_ec_seckey_negate(context, scalar.data()) == ec_success;
39✔
261
}
262

263
bool ec_negate(ec_compressed& point) NOEXCEPT
×
264
{
265
    auto const* context = ec_context_verify::context();
×
266
    return ec_negate(context, point);
×
267
}
268

269
bool ec_negate(ec_uncompressed& point) NOEXCEPT
×
270
{
271
    auto const* context = ec_context_verify::context();
×
272
    return ec_negate(context, point);
×
273
}
274

275
// Convert keys
276
// ----------------------------------------------------------------------------
277

278
bool compress(ec_compressed& out, const ec_uncompressed& point) NOEXCEPT
19✔
279
{
280
    secp256k1_pubkey pubkey;
19✔
281
    auto const* context = ec_context_verify::context();
19✔
282
    return parse(context, pubkey, point) && serialize(context, out, pubkey);
19✔
283
}
284

285
bool decompress(ec_uncompressed& out, const ec_compressed& point) NOEXCEPT
11✔
286
{
287
    secp256k1_pubkey pubkey;
11✔
288
    auto const* context = ec_context_verify::context();
11✔
289
    return parse(context, pubkey, point) && serialize(context, out, pubkey);
11✔
290
}
291

292
bool secret_to_public(ec_compressed& out, const ec_secret& secret) NOEXCEPT
380✔
293
{
294
    auto const* context = ec_context_sign::context();
380✔
295
    return secret_to_public(context, out, secret);
380✔
296
}
297

298
bool secret_to_public(ec_uncompressed& out, const ec_secret& secret) NOEXCEPT
1✔
299
{
300
    auto const* context = ec_context_sign::context();
1✔
301
    return secret_to_public(context, out, secret);
1✔
302
}
303

304
// Verify keys
305
// ----------------------------------------------------------------------------
306

307
bool verify(const ec_secret& secret) NOEXCEPT
157✔
308
{
309
    auto const* context = ec_context_verify::context();
157✔
310
    return secp256k1_ec_seckey_verify(context, secret.data()) == ec_success;
157✔
311
}
312

313
bool verify(const data_slice& point) NOEXCEPT
×
314
{
315
    secp256k1_pubkey pubkey;
×
316
    auto const* context = ec_context_verify::context();
×
317
    return parse(context, pubkey, point);
×
318
}
319

320
// Detect public keys
321
// ----------------------------------------------------------------------------
322

323
bool is_even_key(const ec_compressed& point) NOEXCEPT
1✔
324
{
325
    return point.front() == ec_even_sign;
1✔
326
}
327

328
bool is_compressed_key(const data_slice& point) NOEXCEPT
83✔
329
{
330
    const auto size = point.size();
83✔
331
    if (size != ec_compressed_size)
83✔
332
        return false;
333

334
    const auto first = point.front();
61✔
335
    return first == compressed_even || first == compressed_odd;
61✔
336
}
337

338
bool is_uncompressed_key(const data_slice& point) NOEXCEPT
20✔
339
{
340
    const auto size = point.size();
20✔
341
    if (size != ec_uncompressed_size)
20✔
342
        return false;
343

344
    const auto first = point.front();
18✔
345
    return first == uncompressed;
18✔
346
}
347

348
bool is_hybrid_key(const data_slice& point) NOEXCEPT
×
349
{
350
    const auto size = point.size();
×
351
    if (size != ec_uncompressed_size)
×
352
        return false;
353

354
    const auto first = point.front();
×
355
    return first == hybrid_even || first == hybrid_odd;
×
356
}
357

358
bool is_public_key(const data_slice& point) NOEXCEPT
76✔
359
{
360
    return is_compressed_key(point) || is_uncompressed_key(point);
76✔
361
}
362

363
bool is_endorsement(const endorsement& endorsement) NOEXCEPT
14✔
364
{
365
    const auto size = endorsement.size();
14✔
366
    return size >= min_endorsement_size && size <= max_endorsement_size;
14✔
367
}
368

369
// DER parse/encode
370
// ----------------------------------------------------------------------------
371

372
bool parse_endorsement(uint8_t& sighash_flags, data_slice& der_signature,
46✔
373
    const endorsement& endorsement) NOEXCEPT
374
{
375
    if (endorsement.empty())
46✔
376
        return false;
377

378
    sighash_flags = endorsement.back();
46✔
379
    der_signature = { endorsement.begin(), std::prev(endorsement.end()) };
46✔
380
    return true;
46✔
381
}
382

383
// BIP66 requires strict DER signature encoding.
384
bool parse_signature(ec_signature& out, const data_slice& der_signature,
50✔
385
    bool strict) NOEXCEPT
386
{
387
    if (der_signature.empty())
50✔
388
        return false;
389

390
    auto const* context = ec_context_verify::context();
50✔
391
    auto parsed = pointer_cast<secp256k1_ecdsa_signature>(out.data());
50✔
392

393
    if (strict)
50✔
394
        return secp256k1_ecdsa_signature_parse_der(context, parsed,
3✔
395
            der_signature.data(), der_signature.size()) == ec_success;
3✔
396

397
    return ecdsa_signature_parse_der_lax(context, parsed,
47✔
398
        der_signature.data(), der_signature.size());
47✔
399
}
400

401
bool encode_signature(der_signature& out,
3✔
402
    const ec_signature& signature) NOEXCEPT
403
{
404
    const auto sign = pointer_cast<const secp256k1_ecdsa_signature>(
3✔
405
        signature.data());
406

407
    auto const* context = ec_context_sign::context();
3✔
408
    auto size = max_der_signature_size;
3✔
409
    out.resize(size);
3✔
410

411
    if (secp256k1_ecdsa_signature_serialize_der(context, out.data(), &size,
3✔
412
        sign) != ec_success)
413
        return false;
414

415
    out.resize(size);
3✔
416
    return true;
3✔
417
}
418

419
// EC sign/verify
420
// ----------------------------------------------------------------------------
421

422
// create (serialize???) (secrets are normal)
423
bool sign(ec_signature& out, const ec_secret& secret,
5✔
424
    const hash_digest& hash) NOEXCEPT
425
{
426
    const auto context = ec_context_sign::context();
5✔
427
    const auto signature = pointer_cast<secp256k1_ecdsa_signature>(
5✔
428
        out.data());
429

430
    return (secp256k1_ecdsa_sign(context, signature, hash.data(), secret.data(),
5✔
431
        secp256k1_nonce_function_rfc6979, nullptr) == ec_success);
5✔
432
}
433

434
// parse<>, verify<>
435
bool verify_signature(const data_slice& point, const hash_digest& hash,
52✔
436
    const ec_signature& signature) NOEXCEPT
437
{
438
    secp256k1_pubkey pubkey;
52✔
439
    const auto context = ec_context_verify::context();
52✔
440

441
    return parse(context, pubkey, point) &&
104✔
442
        verify_signature(context, pubkey, hash, signature);
52✔
443
}
444

445
// Recoverable sign/recover
446
// ----------------------------------------------------------------------------
447

448
// sign, serialize (secrets are normal)
449
bool sign_recoverable(recoverable_signature& out, const ec_secret& secret,
5✔
450
    const hash_digest& hash) NOEXCEPT
451
{
452
    int recovery_id = 0;
5✔
453
    auto const* context = ec_context_sign::context();
5✔
454
    secp256k1_ecdsa_recoverable_signature signature;
5✔
455

456
    const auto result =
5✔
457
        secp256k1_ecdsa_sign_recoverable(context, &signature, hash.data(),
5✔
458
            secret.data(), secp256k1_nonce_function_rfc6979, nullptr) ==
459
                ec_success &&
10✔
460
        secp256k1_ecdsa_recoverable_signature_serialize_compact(context,
5✔
461
            out.signature.data(), &recovery_id, &signature) == ec_success;
462

463
    if (is_negative(recovery_id) || recovery_id > maximum_recovery_id)
5✔
464
        return false;
465

466
    out.recovery_id = narrow_sign_cast<uint8_t>(recovery_id);
5✔
467
    return result;
5✔
468
}
469

470
bool recover_public(ec_compressed& out,
4✔
471
    const recoverable_signature& recoverable, const hash_digest& hash) NOEXCEPT
472
{
473
    const auto context = ec_context_verify::context();
4✔
474
    return recover_public(context, out, recoverable, hash);
4✔
475
}
476

477
bool recover_public(ec_uncompressed& out,
2✔
478
    const recoverable_signature& recoverable, const hash_digest& hash) NOEXCEPT
479
{
480
    const auto context = ec_context_verify::context();
2✔
481
    return recover_public(context, out, recoverable, hash);
2✔
482
}
483

484
} // namespace system
485
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc