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#if OPENSSL_API_COMPAT < 0x10100000L
46 ERR_load_crypto_strings();
47#else
48 OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, nullptr);
49#endif
50}
51
53{
54 auto priv_key = internal_private_key(key);
55 auto digest = calculate_sha256_digest(data);
56
57 // sign message data represented by the digest
58 openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) };
59#if OPENSSL_API_COMPAT < 0x10100000L
60 const BIGNUM* sig_r = signature->r;
61 const BIGNUM* sig_s = signature->s;
62#else
63 const BIGNUM* sig_r = nullptr;
64 const BIGNUM* sig_s = nullptr;
65 ECDSA_SIG_get0(signature, &sig_r, &sig_s);
66#endif
67
68 EcdsaSignature ecdsa_signature;
69 X_Coordinate_Only coordinate;
70
71 if (sig_r && sig_s) {
72 const size_t len = field_size(v2::PublicKeyAlgorithm::ECDSA_NISTP256_With_SHA256);
73
74 const auto num_bytes_s = BN_num_bytes(sig_s);
75 assert(len >= static_cast<size_t>(num_bytes_s));
76 ecdsa_signature.s.resize(len, 0x00);
77 BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s);
78
79 const auto num_bytes_r = BN_num_bytes(sig_r);
80 assert(len >= static_cast<size_t>(num_bytes_r));
81 coordinate.x.resize(len, 0x00);
82 BN_bn2bin(sig_r, coordinate.x.data() + len - num_bytes_r);
83 } else {
84 throw openssl::Exception();
85 }
86
87 ecdsa_signature.R = std::move(coordinate);
88 return ecdsa_signature;
89}
90
91Signature BackendOpenSsl::sign_digest(const PrivateKey& key, const ByteBuffer& digest)
92{
93 // sign message data represented by the digest
94 auto priv_key = internal_private_key(key);
95 openssl::Signature signature { ECDSA_do_sign(digest.data(), digest.size(), priv_key) };
96#if OPENSSL_API_COMPAT < 0x10100000L
97 const BIGNUM* sig_r = signature->r;
98 const BIGNUM* sig_s = signature->s;
99#else
100 const BIGNUM* sig_r = nullptr;
101 const BIGNUM* sig_s = nullptr;
102 ECDSA_SIG_get0(signature, &sig_r, &sig_s);
103#endif
104
105 Signature ecdsa_signature;
106 ecdsa_signature.type = key.type;
107
108 if (sig_r && sig_s) {
109 const size_t len = key_length(key.type);
110
111 const auto num_bytes_s = BN_num_bytes(sig_s);
112 assert(len >= static_cast<size_t>(num_bytes_s));
113 ecdsa_signature.s.resize(len, 0x00);
114 BN_bn2bin(sig_s, ecdsa_signature.s.data() + len - num_bytes_s);
115
116 const auto num_bytes_r = BN_num_bytes(sig_r);
117 assert(len >= static_cast<size_t>(num_bytes_r));
118 ecdsa_signature.r.resize(len, 0x00);
119 BN_bn2bin(sig_r, ecdsa_signature.r.data() + len - num_bytes_r);
120 } else {
121 throw openssl::Exception();
122 }
123
124 return ecdsa_signature;
125}
126
127bool BackendOpenSsl::verify_data(const ecdsa256::PublicKey& key, const ByteBuffer& data, const EcdsaSignature& sig)
128{
129 auto digest = calculate_sha256_digest(data);
130 auto pub = internal_public_key(key);
131 openssl::Signature signature(sig);
132
133 return (ECDSA_do_verify(digest.data(), digest.size(), signature, pub) == 1);
134}
135
136bool BackendOpenSsl::verify_digest(const PublicKey& gpub, const ByteBuffer& digest, const Signature& gsig)
137{
138 if (gpub.type != gsig.type) {
139 return false;
140 }
141
143 openssl::Signature sig { gsig };
144 return ECDSA_do_verify(digest.data(), digest.size(), sig, pub) == 1;
145}
146
147boost::optional<Uncompressed> BackendOpenSsl::decompress_point(const EccPoint& ecc_point)
148{
149 struct DecompressionVisitor : public boost::static_visitor<bool>
150 {
151 bool operator()(const X_Coordinate_Only&)
152 {
153 return false;
154 }
155
156 bool operator()(const Compressed_Lsb_Y_0& p)
157 {
158 return decompress(p.x, 0);
159 }
160
161 bool operator()(const Compressed_Lsb_Y_1& p)
162 {
163 return decompress(p.x, 1);
164 }
165
166 bool operator()(const Uncompressed& p)
167 {
168 result = p;
169 return true;
170 }
171
172 bool decompress(const ByteBuffer& x, int y_bit)
173 {
175 openssl::BigNumber x_coordinate(x);
176 openssl::Group group(NID_X9_62_prime256v1);
177 openssl::Point point(group);
178 openssl::BigNumber y_coordinate;
179
180 result.x = x;
181 result.y.resize(result.x.size());
182
183#if OPENSSL_API_COMPAT < 0x10101000L
184 EC_POINT_set_compressed_coordinates_GFp(group, point, x_coordinate, y_bit, ctx);
185 EC_POINT_get_affine_coordinates_GFp(group, point, nullptr, y_coordinate, ctx);
186 std::size_t y_coordinate_bytes = BN_num_bytes(y_coordinate);
187 if (y_coordinate_bytes <= result.y.size()) {
188 BN_bn2bin(y_coordinate, result.y.data() + (result.y.size() - y_coordinate_bytes));
189 return true;
190 } else {
191 return false;
192 }
193#else
194 EC_POINT_set_compressed_coordinates(group, point, x_coordinate, y_bit, ctx);
195 EC_POINT_get_affine_coordinates(group, point, nullptr, y_coordinate, ctx);
196 return (BN_bn2binpad(y_coordinate, result.y.data(), result.y.size()) != -1);
197#endif
198 }
199
200 Uncompressed result;
201 };
202
203 DecompressionVisitor visitor;
204 if (boost::apply_visitor(visitor, ecc_point)) {
205 return visitor.result;
206 } else {
207 return boost::none;
208 }
209}
210
211ByteBuffer BackendOpenSsl::calculate_hash(HashAlgorithm algo, const ByteBuffer& data)
212{
213 ByteBuffer result;
214 if (algo == HashAlgorithm::SHA256) {
215 auto digest = calculate_sha256_digest(data);
216 result.assign(digest.begin(), digest.end());
217 } else if (algo == HashAlgorithm::SHA384) {
218 auto digest = calculate_sha384_digest(data);
219 result.assign(digest.begin(), digest.end());
220 }
221 return result;
222}
223
224std::array<uint8_t, 32> BackendOpenSsl::calculate_sha256_digest(const ByteBuffer& data) const
225{
226 static_assert(SHA256_DIGEST_LENGTH == 32, "Unexpected length of SHA256 digest");
227
228 std::array<uint8_t, 32> digest;
229 SHA256_CTX ctx;
230 SHA256_Init(&ctx);
231 SHA256_Update(&ctx, data.data(), data.size());
232 SHA256_Final(digest.data(), &ctx);
233 return digest;
234}
235
236std::array<uint8_t, 48> BackendOpenSsl::calculate_sha384_digest(const ByteBuffer& data) const
237{
238 static_assert(SHA384_DIGEST_LENGTH == 48, "Unexpected length of SHA384 digest");
239
240 std::array<uint8_t, 48> digest;
241 SHA384(data.data(), data.size(), digest.data());
242 return digest;
243}
244
246{
247 openssl::Key key(NID_X9_62_prime256v1);
248 openssl::BigNumber prv(generic.key);
249 EC_KEY_set_private_key(key, prv);
250
251 // OpenSSL requires public key, so we recreate it from private key
253 const EC_GROUP* group = EC_KEY_get0_group(key);
254 openssl::Point pub(group);
255 openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx));
256 EC_KEY_set_public_key(key, pub);
257
258 openssl::check(EC_KEY_check_key(key));
259 return key;
260}
261
263{
264 openssl::Key key(openssl_nid(generic.type));
265 openssl::BigNumber prv(generic.key);
266 EC_KEY_set_private_key(key, prv);
267
268 // OpenSSL requires public key, so we recreate it from private key
270 const EC_GROUP* group = EC_KEY_get0_group(key);
271 openssl::Point pub(group);
272 openssl::check(EC_POINT_mul(group, pub, prv, nullptr, nullptr, ctx));
273 EC_KEY_set_public_key(key, pub);
274
275 openssl::check(EC_KEY_check_key(key));
276 return key;
277}
278
280{
281 openssl::Key key(NID_X9_62_prime256v1);
282 openssl::BigNumber x(generic.x);
283 openssl::BigNumber y(generic.y);
284 EC_KEY_set_public_key_affine_coordinates(key, x, y);
285
286 openssl::check(EC_KEY_check_key(key));
287 return key;
288}
289
291{
292 openssl::Key key(openssl_nid(generic.type));
293 openssl::Point point = internal_ec_point(generic);
294 EC_KEY_set_public_key(key, point);
295
296 openssl::check(EC_KEY_check_key(key));
297 return key;
298}
299
301{
302 openssl::Group group { openssl_nid(generic.type) };
303 openssl::Point point { group };
305
306 switch (generic.compression)
307 {
308 case KeyCompression::NoCompression:
309 EC_POINT_set_affine_coordinates(group, point,
310 openssl::BigNumber { generic.x }, openssl::BigNumber {generic.y },
311 bn_ctx);
312 break;
313 case KeyCompression::Y0:
314 EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 0, bn_ctx);
315 break;
316 case KeyCompression::Y1:
317 EC_POINT_set_compressed_coordinates(group, point, openssl::BigNumber { generic.x }, 1, bn_ctx);
318 break;
319 default:
320 // no-op
321 break;
322 }
323
324 return point;
325}
326
327} // namespace security
328} // namespace vanetza
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
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