Vanetza
Loading...
Searching...
No Matches
backend_openssl.cpp
1#include <vanetza/security/backend_openssl.hpp>
2#include <vanetza/security/key_type.hpp>
3#include <vanetza/security/openssl_wrapper.hpp>
4#include <vanetza/security/v2/public_key.hpp>
5#include <vanetza/security/v2/signature.hpp>
6#include <openssl/bn.h>
7#include <openssl/ec.h>
8#include <openssl/ecdsa.h>
9#include <openssl/obj_mac.h>
10#include <openssl/sha.h>
11#include <cassert>
12
13namespace vanetza
14{
15namespace security
16{
17
18namespace
19{
20
21int openssl_nid(KeyType key)
22{
23 int nid;
24 switch (key) {
25 case KeyType::NistP256:
26 nid = NID_X9_62_prime256v1;
27 break;
28 case KeyType::BrainpoolP256r1:
29 nid = NID_brainpoolP256r1;
30 break;
31 case KeyType::BrainpoolP384r1:
32 nid = NID_brainpoolP384r1;
33 break;
34 default:
35 nid = NID_undef;
36 break;
37 }
38 return nid;
39}
40
41} // namespace
42
43BackendOpenSsl::BackendOpenSsl()
44{
45 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
46}
47
48EcdsaSignature BackendOpenSsl::sign_data(const ecdsa256::PrivateKey& key, const ByteBuffer& data)
49{
50 auto priv_key = internal_private_key(key);
51 auto digest = calculate_sha256_digest(data);
52
53 // sign message data represented by the digest
54 openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) };
55 const BIGNUM* sig_r = nullptr;
56 const BIGNUM* sig_s = nullptr;
57 ECDSA_SIG_get0(signature, &sig_r, &sig_s);
58
59 EcdsaSignature ecdsa_signature;
60 X_Coordinate_Only coordinate;
61
62 if (sig_r && sig_s) {
63 const size_t len = field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
64
65 const auto num_bytes_s = BN_num_bytes(sig_s);
66 assert(len >= static_cast<size_t>(num_bytes_s));
67 ecdsa_signature.s.resize(len, 0x00);
68 BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s);
69
70 const auto num_bytes_r = BN_num_bytes(sig_r);
71 assert(len >= static_cast<size_t>(num_bytes_r));
72 coordinate.x.resize(len, 0x00);
73 BN_bn2bin(sig_r, coordinate.x.data() + len - num_bytes_r);
74 } else {
75 throw openssl::Exception();
76 }
77
78 ecdsa_signature.R = std::move(coordinate);
79 return ecdsa_signature;
80}
81
82Signature BackendOpenSsl::sign_digest(const PrivateKey& key, const ByteBuffer& digest)
83{
84 // sign message data represented by the digest
85 auto priv_key = internal_private_key(key);
86 openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) };
87 const BIGNUM* sig_r = nullptr;
88 const BIGNUM* sig_s = nullptr;
89 ECDSA_SIG_get0(signature, &sig_r, &sig_s);
90
91 Signature ecdsa_signature;
92 ecdsa_signature.type = key.type;
93
94 if (sig_r && sig_s) {
95 const size_t len = key_length(key.type);
96
97 const auto num_bytes_s = BN_num_bytes(sig_s);
98 assert(len >= static_cast<size_t>(num_bytes_s));
99 ecdsa_signature.s.resize(len, 0x00);
100 BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s);
101
102 const auto num_bytes_r = BN_num_bytes(sig_r);
103 assert(len >= static_cast<size_t>(num_bytes_r));
104 ecdsa_signature.r.resize(len, 0x00);
105 BN_bn2bin(sig_r, ecdsa_signature.r.data() + len - num_bytes_r);
106 } else {
107 throw openssl::Exception();
108 }
109
110 return ecdsa_signature;
111}
112
113bool BackendOpenSsl::verify_data(const ecdsa256::PublicKey& key, const ByteBuffer& data, const EcdsaSignature& sig)
114{
115 try {
116 auto digest = calculate_sha256_digest(data);
117 auto pub = internal_public_key(key);
118 openssl::Signature signature(sig);
119
120 return (ECDSA_do_verify(digest.data(), digest.size(), signature, pub) == 1);
121 } catch (const openssl::Exception&) {
122 return false;
123 }
124}
125
126bool BackendOpenSsl::verify_digest(const PublicKey& gpub, const ByteBuffer& digest, const Signature& gsig)
127{
128 if (gpub.type != gsig.type) {
129 return false;
130 }
131
132 try {
133 openssl::Key pub = internal_public_key(gpub);
134 openssl::Signature sig { gsig };
135 return ECDSA_do_verify(digest.data(), digest.size(), sig, pub) == 1;
136 } catch (const openssl::Exception&) {
137 return false;
138 }
139}
140
141boost::optional<Uncompressed> BackendOpenSsl::decompress_point(const EccPoint& ecc_point)
142{
143 struct DecompressionVisitor : public boost::static_visitor<bool>
144 {
145 bool operator()(const X_Coordinate_Only&)
146 {
147 return false;
148 }
149
150 bool operator()(const Compressed_Lsb_Y_0& p)
151 {
152 return decompress(p.x, 0);
153 }
154
155 bool operator()(const Compressed_Lsb_Y_1& p)
156 {
157 return decompress(p.x, 1);
158 }
159
160 bool operator()(const Uncompressed& p)
161 {
162 result = p;
163 return true;
164 }
165
166 bool decompress(const ByteBuffer& x, int y_bit)
167 {
168 try {
169 openssl::BigNumberContext ctx;
170 openssl::BigNumber x_coordinate(x);
171 openssl::Group group(NID_X9_62_prime256v1);
172 openssl::Point point(group);
173 openssl::BigNumber y_coordinate;
174
175 result.x = x;
176 result.y.resize(result.x.size());
177
178 EC_POINT_set_compressed_coordinates(group, point, x_coordinate, y_bit, ctx);
179 EC_POINT_get_affine_coordinates(group, point, nullptr, y_coordinate, ctx);
180 return (BN_bn2binpad(y_coordinate, result.y.data(), result.y.size()) != -1);
181 } catch (const openssl::Exception&) {
182 return false;
183 }
184 }
185
186 Uncompressed result;
187 };
188
189 DecompressionVisitor visitor;
190 if (boost::apply_visitor(visitor, ecc_point)) {
191 return visitor.result;
192 } else {
193 return boost::none;
194 }
195}
196
197ByteBuffer BackendOpenSsl::calculate_hash(HashAlgorithm algo, const ByteBuffer& data)
198{
199 ByteBuffer result;
200 if (algo == HashAlgorithm::SHA256) {
201 auto digest = calculate_sha256_digest(data);
202 result.assign(digest.begin(), digest.end());
203 } else if (algo == HashAlgorithm::SHA384) {
204 auto digest = calculate_sha384_digest(data);
205 result.assign(digest.begin(), digest.end());
206 }
207 return result;
208}
209
210std::array<uint8_t, 32> BackendOpenSsl::calculate_sha256_digest(const ByteBuffer& data) const
211{
212 static_assert(SHA256_DIGEST_LENGTH == 32, "Unexpected length of SHA256 digest");
213
214 std::array<uint8_t, 32> digest;
215 SHA256_CTX ctx;
216 SHA256_Init(&ctx);
217 SHA256_Update(&ctx, data.data(), data.size());
218 SHA256_Final(digest.data(), &ctx);
219 return digest;
220}
221
222std::array<uint8_t, 48> BackendOpenSsl::calculate_sha384_digest(const ByteBuffer& data) const
223{
224 static_assert(SHA384_DIGEST_LENGTH == 48, "Unexpected length of SHA384 digest");
225
226 std::array<uint8_t, 48> digest;
227 SHA384(data.data(), data.size(), digest.data());
228 return digest;
229}
230
231openssl::Key BackendOpenSsl::internal_private_key(const ecdsa256::PrivateKey& generic) const
232{
233 openssl::Key key(NID_X9_62_prime256v1);
234 openssl::BigNumber prv(generic.key);
235 EC_KEY_set_private_key(key, prv);
236
237 // OpenSSL requires public key, so we recreate it from private key
238 openssl::BigNumberContext ctx;
239 const EC_GROUP* group = EC_KEY_get0_group(key);
240 openssl::Point pub(group);
241 openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx));
242 EC_KEY_set_public_key(key, pub);
243
244 openssl::check(EC_KEY_check_key(key));
245 return key;
246}
247
248openssl::Key BackendOpenSsl::internal_private_key(const PrivateKey& generic) const
249{
250 openssl::Key key(openssl_nid(generic.type));
251 openssl::BigNumber prv(generic.key);
252 EC_KEY_set_private_key(key, prv);
253
254 // OpenSSL requires public key, so we recreate it from private key
255 openssl::BigNumberContext ctx;
256 const EC_GROUP* group = EC_KEY_get0_group(key);
257 openssl::Point pub(group);
258 openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx));
259 EC_KEY_set_public_key(key, pub);
260
261 openssl::check(EC_KEY_check_key(key));
262 return key;
263}
264
265openssl::Key BackendOpenSsl::internal_public_key(const ecdsa256::PublicKey& generic) const
266{
267 openssl::Key key(NID_X9_62_prime256v1);
268 openssl::BigNumber x(generic.x);
269 openssl::BigNumber y(generic.y);
270 EC_KEY_set_public_key_affine_coordinates(key, x, y);
271
272 openssl::check(EC_KEY_check_key(key));
273 return key;
274}
275
276openssl::Key BackendOpenSsl::internal_public_key(const PublicKey& generic) const
277{
278 openssl::Key key(openssl_nid(generic.type));
279 openssl::Point point = internal_ec_point(generic);
280 EC_KEY_set_public_key(key, point);
281
282 openssl::check(EC_KEY_check_key(key));
283 return key;
284}
285
287{
288 ecdsa256::KeyPair key_pair;
289 openssl::Key key(NID_X9_62_prime256v1);
290 openssl::check(EC_KEY_generate_key(key));
291
292 const BIGNUM* priv_bn = EC_KEY_get0_private_key(key);
293 const EC_POINT* pub_point = EC_KEY_get0_public_key(key);
294 const EC_GROUP* group = EC_KEY_get0_group(key);
295
296 // extract private key
297 key_pair.private_key.key.fill(0);
298 auto priv_bytes = BN_num_bytes(priv_bn);
299 BN_bn2bin(priv_bn, key_pair.private_key.key.data() + key_pair.private_key.key.size() - priv_bytes);
300
301 // extract public key coordinates
302 openssl::BigNumber x;
303 openssl::BigNumber y;
304 openssl::BigNumberContext ctx;
305 EC_POINT_get_affine_coordinates(group, pub_point, x, y, ctx);
306 BN_bn2binpad(x, key_pair.public_key.x.data(), key_pair.public_key.x.size());
307 BN_bn2binpad(y, key_pair.public_key.y.data(), key_pair.public_key.y.size());
308
309 return key_pair;
310}
311
312openssl::Point BackendOpenSsl::internal_ec_point(const PublicKey& generic) const
313{
314 openssl::Group group { openssl_nid(generic.type) };
315 openssl::Point point { group };
316 openssl::BigNumberContext bn_ctx;
317
318 switch (generic.compression)
319 {
320 case KeyCompression::NoCompression:
321 EC_POINT_set_affine_coordinates(group, point,
322 openssl::BigNumber { generic.x }, openssl::BigNumber {generic.y },
323 bn_ctx);
324 break;
325 case KeyCompression::Y0:
326 EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 0, bn_ctx);
327 break;
328 case KeyCompression::Y1:
329 EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 1, bn_ctx);
330 break;
331 default:
332 // no-op
333 break;
334 }
335
336 return point;
337}
338
339namespace openssl
340{
341
342PublicKey derive_public_key(const PrivateKey& private_key)
343{
344 Key ec_key(openssl_nid(private_key.type));
345 BigNumber prv(private_key.key);
346 EC_KEY_set_private_key(ec_key, prv);
347
348 const EC_GROUP* group = EC_KEY_get0_group(ec_key);
349 Point pub(group);
351 check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx));
352
353 BigNumber x;
354 BigNumber y;
355 EC_POINT_get_affine_coordinates(group, pub, x, y, ctx);
356
357 PublicKey public_key;
358 public_key.type = private_key.type;
359 public_key.compression = KeyCompression::NoCompression;
360 public_key.x.resize(key_length(private_key.type));
361 public_key.y.resize(key_length(private_key.type));
362 BN_bn2binpad(x, public_key.x.data(), public_key.x.size());
363 BN_bn2binpad(y, public_key.y.data(), public_key.y.size());
364 return public_key;
365}
366
367} // namespace openssl
368
369} // namespace security
370} // namespace vanetza
Backend implementation based on OpenSSL.
std::array< uint8_t, 32 > calculate_sha256_digest(const ByteBuffer &data) const
calculate SHA256 digest of data buffer
bool verify_data(const ecdsa256::PublicKey &public_key, const ByteBuffer &data, const EcdsaSignature &sig) override
bool verify_digest(const PublicKey &, const ByteBuffer &digest, const Signature &) override
std::array< uint8_t, 48 > calculate_sha384_digest(const ByteBuffer &data) const
calculate SHA384 digest of data buffer
openssl::Key internal_public_key(const ecdsa256::PublicKey &) const
convert to internal format of public key
openssl::Point internal_ec_point(const PublicKey &) const
convert to internal format of an EC point
ecdsa256::KeyPair generate_key_pair() override
Signature sign_digest(const PrivateKey &, const ByteBuffer &digest) override
EcdsaSignature sign_data(const ecdsa256::PrivateKey &private_key, const ByteBuffer &data_buffer) override
boost::optional< Uncompressed > decompress_point(const EccPoint &ecc_point) override
openssl::Key internal_private_key(const ecdsa256::PrivateKey &) const
convert to internal format of private key
ByteBuffer calculate_hash(HashAlgorithm, const ByteBuffer &) override
calculate hash value of data
Compressed_Lsb_Y_0 specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition ecc_point.hpp:24
Compressed_Lsb_Y_1 specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition ecc_point.hpp:30
EcdsaSignature specified in TS 103 097 v1.2.1, section 4.2.9.
Definition signature.hpp:17
Uncompressed specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition ecc_point.hpp:36
X_Coordinate_Only specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition ecc_point.hpp:18