mal-packet-weaver
C++20 packet serialization/deserialization library.
Loading...
Searching...
No Matches
ecdsa.hpp
Go to the documentation of this file.
1#pragma once
2#include "sha.hpp"
3
4#ifdef MAL_PACKET_WEAVER_HAS_OPENSSL
5namespace mal_packet_weaver::crypto::ECDSA
6{
13 [[nodiscard]] inline int GetCurveByName(const std::string_view curve)
14 {
15 if (curve == "secp256k1")
16 {
17 return NID_secp256k1;
18 }
19 else if (curve == "secp384r1")
20 {
21 return NID_secp384r1;
22 }
23 else if (curve == "secp521r1")
24 {
25 return NID_secp521r1;
26 }
27 AlwaysAssert(false, "Unknown curve type");
28 throw std::invalid_argument("Unknown curve type");
29 }
30
35 class KeyPairGenerator : non_copyable_non_movable
36 {
37 public:
42 explicit KeyPairGenerator(const int curve_id)
43 {
44 ctx_.reset(EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr));
45 AlwaysAssert(ctx_ != nullptr, "EVP_PKEY_CTX_new_id() failed");
46 AlwaysAssert(EVP_PKEY_keygen_init(ctx_.get()) > 0, "EVP_PKEY_keygen_init() failed");
47 AlwaysAssert(EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx_.get(), curve_id) > 0,
48 "EVP_PKEY_CTX_set_ec_paramgen_curve_nid() failed");
49 }
50
55 explicit KeyPairGenerator(const std::string_view curve_name) : KeyPairGenerator(GetCurveByName(curve_name)) {}
56
61 [[nodiscard]] KeyPair generate() const
62 {
63 EVP_PKEY_WRAPPER pkey;
64 {
65 EVP_PKEY *pkey_ = nullptr;
66 AlwaysAssert(EVP_PKEY_keygen(ctx_.get(), &pkey_) > 0, "EVP_PKEY_keygen() failed");
67 pkey.reset(pkey_);
68 }
69 unsigned char *key_data;
70 unsigned long key_size;
71
72 BIO_WRAPPER bio{ BIO_new(BIO_s_mem()) };
73 AlwaysAssert(bio != nullptr, "BIO_new_file() failed");
74 AlwaysAssert(PEM_write_bio_PrivateKey(bio.get(), pkey.get(), nullptr, nullptr, 0, nullptr, nullptr) > 0,
75 "PEM_write_bio_PrivateKey() failed");
76 key_size = BIO_get_mem_data(bio.get(), &key_data);
77
78 Key private_key;
79 private_key.resize(key_size);
80 std::copy_n(key_data, key_size, private_key.as<unsigned char>());
81
82 bio.reset(BIO_new(BIO_s_mem()));
83 AlwaysAssert(bio != nullptr, "BIO_new_file() failed");
84 AlwaysAssert(PEM_write_bio_PUBKEY(bio.get(), pkey.get()) > 0, "PEM_write_bio_PUBKEY() failed");
85 key_size = BIO_get_mem_data(bio.get(), &key_data);
86 Key public_key;
87 public_key.resize(key_size);
88 std::copy_n(key_data, key_size, public_key.as<unsigned char>());
89
90 return KeyPair{ private_key, public_key };
91 }
92
93 private:
94 EVP_PKEY_CTX_WRAPPER ctx_ = nullptr;
95 };
96
100 class Signer : non_copyable_non_movable
101 {
102 public:
108 Signer(const KeyView private_key, const Hash::HashType hash_type) : hash_type_(hash_type)
109 {
110 BIO_WRAPPER bio{ BIO_new_mem_buf(private_key.data(), static_cast<int>(private_key.size())) };
111 AlwaysAssert(bio != nullptr, "BIO_new_file() failed");
112 pkey_.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
113 AlwaysAssert(pkey_ != nullptr, "PEM_read_bio_PrivateKey() failed");
114
115 ctx_.reset(EVP_PKEY_CTX_new(pkey_.get(), nullptr));
116 AlwaysAssert(ctx_ != nullptr, "EVP_PKEY_CTX_new() failed");
117 AlwaysAssert(EVP_PKEY_sign_init(ctx_.get()) > 0, "EVP_PKEY_sign_init() failed");
118 if (hash_type == Hash::HashType::SHA256)
119 {
120 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha256()) > 0,
121 "EVP_PKEY_CTX_set_signature_md() failed");
122 }
123 else if (hash_type == Hash::HashType::SHA384)
124 {
125 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha384()) > 0,
126 "EVP_PKEY_CTX_set_signature_md() failed");
127 }
128 else if (hash_type == Hash::HashType::SHA512)
129 {
130 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha512()) > 0,
131 "EVP_PKEY_CTX_set_signature_md() failed");
132 }
133 else
134 {
135 AlwaysAssert(false, "Unsupported hash type");
136 }
137 }
138
144 [[nodiscard]] ByteArray sign_hash(const Hash hash) const
145 {
146 Assert(hash.hash_type == hash_type_, "Unsupported hash type");
147
148 size_t signature_size = 0;
149 ByteArray signature;
150
151 AlwaysAssert(EVP_PKEY_sign(ctx_.get(), nullptr, &signature_size, hash.as_uint8(), hash.size()) > 0,
152 "EVP_PKEY_sign() failed");
153 signature.resize(signature_size);
154
155 AlwaysAssert(EVP_PKEY_sign(ctx_.get(), signature.as<unsigned char>(), &signature_size, hash.as_uint8(),
156 hash.size()) > 0,
157 "EVP_PKEY_sign() failed");
158 signature.resize(signature_size);
159 return signature;
160 }
161
167 [[nodiscard]] ByteArray sign_data(const ByteView data) const
168 {
169 const Hash hash = SHA::ComputeHash(data, hash_type_);
170 return sign_hash(hash);
171 }
172
173 private:
174 EVP_PKEY_CTX_WRAPPER ctx_ = nullptr;
175 EVP_PKEY_WRAPPER pkey_ = nullptr;
176 const Hash::HashType hash_type_;
177 };
178
182 class Verifier : non_copyable_non_movable
183 {
184 public:
190 Verifier(const KeyView public_key, const Hash::HashType hash_type) : hash_type_(hash_type)
191 {
192 BIO_WRAPPER bio{ BIO_new_mem_buf(public_key.data(), static_cast<int>(public_key.size())) };
193 AlwaysAssert(bio != nullptr, "BIO_new_file() failed");
194 pkey_.reset(PEM_read_bio_PUBKEY(bio.get(), nullptr, nullptr, nullptr));
195 AlwaysAssert(pkey_ != nullptr, "PEM_read_bio_PUBKEY() failed");
196
197 ctx_.reset(EVP_PKEY_CTX_new(pkey_.get(), nullptr));
198 AlwaysAssert(ctx_ != nullptr, "EVP_PKEY_CTX_new() failed");
199 AlwaysAssert(EVP_PKEY_verify_init(ctx_.get()) > 0, "EVP_PKEY_verify_init() failed");
200 if (hash_type == Hash::HashType::SHA256)
201 {
202 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha256()) > 0,
203 "EVP_PKEY_CTX_set_signature_md() failed");
204 }
205 else if (hash_type == Hash::HashType::SHA384)
206 {
207 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha384()) > 0,
208 "EVP_PKEY_CTX_set_signature_md() failed");
209 }
210 else if (hash_type == Hash::HashType::SHA512)
211 {
212 AlwaysAssert(EVP_PKEY_CTX_set_signature_md(ctx_.get(), EVP_sha512()) > 0,
213 "EVP_PKEY_CTX_set_signature_md() failed");
214 }
215 else
216 {
217 AlwaysAssert(false, "Unsupported hash type");
218 }
219 }
220
227 [[nodiscard]] bool verify_hash(const Hash hash, const ByteView signature) const
228 {
229 Assert(hash.hash_type == hash_type_, "Unsupported hash type");
230
231 return EVP_PKEY_verify(ctx_.get(), signature.as<unsigned char>(), signature.size(), hash.as_uint8(),
232 hash.size()) > 0;
233 }
234
241 [[nodiscard]] bool verify_data(const ByteView data, const ByteView signature) const
242 {
243 const Hash hash = SHA::ComputeHash(data, hash_type_);
244 return verify_hash(hash, signature);
245 }
246
247 private:
248 EVP_PKEY_CTX_WRAPPER ctx_ = nullptr;
249 EVP_PKEY_WRAPPER pkey_ = nullptr;
250 const Hash::HashType hash_type_;
251 };
256 [[deprecated]] [[nodiscard]] inline KeyPair generate_key_pair(std::string curve_name)
257 {
258 KeyPairGenerator instance(curve_name);
259 return instance.generate();
260 }
261
266 [[deprecated]] [[nodiscard]] inline ByteArray sign_data(const KeyView private_key, const ByteView data,
267 const Hash::HashType hash_type)
268 {
269 Signer instance(private_key, hash_type);
270 return instance.sign_data(data);
271 }
272
277 [[deprecated]] [[nodiscard]] inline ByteArray sign_hash(const KeyView private_key, const Hash hash)
278 {
279 Signer instance(private_key, hash.hash_type);
280 return instance.sign_hash(hash);
281 }
282
287 [[deprecated]] [[nodiscard]] inline bool verify_data(const KeyView public_key, const ByteView data,
288 const ByteView signature, const Hash::HashType hash_type)
289 {
290 Verifier instance(public_key, hash_type);
291 return instance.verify_data(data, signature);
292 }
293
298 [[deprecated]] [[nodiscard]] inline bool verify_hash(const KeyView public_key, const Hash hash,
299 const ByteView signature)
300 {
301 Verifier instance(public_key, hash.hash_type);
302 return instance.verify_hash(hash, signature);
303 }
304} // namespace mal_packet_weaver::crypto::ECDSA
305#endif
void Assert(bool value, std::string_view message="Assert failed", std::source_location location=std::source_location::current())
Asserts a condition with customizable behavior based on debug mode.
void AlwaysAssert(bool value, std::string_view message="Assert failed", std::source_location location=std::source_location::current())
Always asserts a condition with customizable behavior.
Represents a cryptographic hash value along with its type.