UltrafastSecp256k1 3.50.0
Ultra high-performance secp256k1 elliptic curve cryptography library
Loading...
Searching...
No Matches
private_key.hpp
Go to the documentation of this file.
1#ifndef SECP256K1_PRIVATE_KEY_HPP
2#define SECP256K1_PRIVATE_KEY_HPP
3#pragma once
4
5// ============================================================================
6// PrivateKey -- Strong Type for Secret Key Material
7// ============================================================================
8// Wraps fast::Scalar with:
9// - No implicit conversion to Scalar (prevents accidental fast:: usage)
10// - Explicit .scalar() accessor (code-review signal for CT correctness)
11// - Destructor securely erases key material (volatile memset)
12// - Factory methods with strict validation (rejects 0 and >= n)
13//
14// Use PrivateKey instead of raw Scalar when handling long-lived secret keys.
15// Pass to ct::ecdsa_sign(), ct::schnorr_sign() etc. for safe signing.
16//
17// Example:
18// secp256k1::PrivateKey pk;
19// if (!secp256k1::PrivateKey::from_bytes(raw_key, pk)) { /* invalid */ }
20// auto sig = secp256k1::ct::ecdsa_sign(msg_hash, pk);
21// auto kp = secp256k1::ct::schnorr_keypair_create(pk);
22// ============================================================================
23
24#include <array>
25#include <cstdint>
26#include <cstring>
27#include "secp256k1/scalar.hpp"
28
29namespace secp256k1 {
30
32public:
33 // -- Construction (explicit only) -----------------------------------------
34
35 // Default: zero (invalid key, must be initialized via from_bytes).
36 PrivateKey() noexcept = default;
37
38 // Parse from 32-byte big-endian. Rejects values >= n or == 0.
39 // Returns false on invalid input (out is left zeroed).
40 [[nodiscard]] static bool from_bytes(const std::uint8_t* bytes32,
41 PrivateKey& out) noexcept {
42 return fast::Scalar::parse_bytes_strict_nonzero(bytes32, out.scalar_);
43 }
44 [[nodiscard]] static bool from_bytes(const std::array<std::uint8_t, 32>& bytes,
45 PrivateKey& out) noexcept {
46 return fast::Scalar::parse_bytes_strict_nonzero(bytes, out.scalar_);
47 }
48
49 // Wrap an already-validated scalar. Caller must ensure 0 < scalar < n.
50 // Named "wrap" to emphasize this bypasses validation.
51 [[nodiscard]] static PrivateKey wrap(const fast::Scalar& s) noexcept {
52 PrivateKey pk;
53 pk.scalar_ = s;
54 return pk;
55 }
56
57 // -- Access (explicit only) -----------------------------------------------
58
59 // Returns the underlying scalar for use in signing operations.
60 // WARNING: any call site using this MUST use ct:: operations for
61 // secret-dependent computation. Variable-time fast:: paths leak timing.
62 [[nodiscard]] const fast::Scalar& scalar() const noexcept { return scalar_; }
63
64 // Serialize to 32-byte big-endian.
65 [[nodiscard]] std::array<std::uint8_t, 32> to_bytes() const {
66 return scalar_.to_bytes();
67 }
68
69 // Check if the key is valid (nonzero).
70 [[nodiscard]] bool is_valid() const noexcept { return !scalar_.is_zero(); }
71
72 // -- No implicit conversion -----------------------------------------------
73 // Intentionally no operator Scalar() or operator const Scalar&().
74 // This forces callers to write .scalar() explicitly -- a code-review signal.
75
76 // -- Lifecycle ------------------------------------------------------------
77
78 ~PrivateKey() { secure_erase(); }
79
80 // Copy: allowed but zeroes are copied (no hidden state).
81 PrivateKey(const PrivateKey& other) noexcept : scalar_(other.scalar_) {}
82 PrivateKey& operator=(const PrivateKey& other) noexcept {
83 if (this != &other) {
84 secure_erase();
85 scalar_ = other.scalar_;
86 }
87 return *this;
88 }
89
90 // Move: source is zeroed after transfer.
91 PrivateKey(PrivateKey&& other) noexcept : scalar_(other.scalar_) {
92 other.secure_erase();
93 }
94 PrivateKey& operator=(PrivateKey&& other) noexcept {
95 if (this != &other) {
96 secure_erase();
97 scalar_ = other.scalar_;
98 other.secure_erase();
99 }
100 return *this;
101 }
102
103private:
104 void secure_erase() noexcept {
105 // volatile prevents compiler from optimizing away the memset
106 auto* p =
107 reinterpret_cast<volatile std::uint8_t*>(&scalar_);
108 for (std::size_t i = 0; i < sizeof(scalar_); ++i) {
109 p[i] = 0;
110 }
111 }
112
113 fast::Scalar scalar_{};
114};
115
116// Comparison (for testing; compares underlying scalars)
117inline bool operator==(const PrivateKey& a, const PrivateKey& b) noexcept {
118 return a.scalar() == b.scalar();
119}
120
121} // namespace secp256k1
122
123#endif // SECP256K1_PRIVATE_KEY_HPP
PrivateKey(const PrivateKey &other) noexcept
std::array< std::uint8_t, 32 > to_bytes() const
PrivateKey() noexcept=default
PrivateKey & operator=(PrivateKey &&other) noexcept
static bool from_bytes(const std::uint8_t *bytes32, PrivateKey &out) noexcept
bool is_valid() const noexcept
static bool from_bytes(const std::array< std::uint8_t, 32 > &bytes, PrivateKey &out) noexcept
const fast::Scalar & scalar() const noexcept
PrivateKey & operator=(const PrivateKey &other) noexcept
static PrivateKey wrap(const fast::Scalar &s) noexcept
PrivateKey(PrivateKey &&other) noexcept
static bool parse_bytes_strict_nonzero(const std::uint8_t *bytes32, Scalar &out) noexcept
std::array< std::uint8_t, 32 > to_bytes() const
bool is_zero() const noexcept
bool operator==(const PrivateKey &a, const PrivateKey &b) noexcept