Vanetza
Loading...
Searching...
No Matches
backend_cryptopp.cpp
1#include <vanetza/security/backend_cryptopp.hpp>
2#include <vanetza/security/ecc_point.hpp>
3#include <boost/optional/optional.hpp>
4#include <cryptopp/oids.h>
5#include <algorithm>
6#include <cassert>
7#include <iterator>
8#include <functional>
9
10namespace vanetza
11{
12namespace security
13{
14
15namespace {
16
17/**
18 * Derive Crypto++ OID object from key type
19 * \param key_type key type from our API
20 * \return Crypto++ OID object (possibly empty)
21 */
22CryptoPP::OID get_oid(KeyType key_type)
23{
24 if (key_type == KeyType::NistP256) {
25 return CryptoPP::ASN1::secp256r1();
26 } else if (key_type == KeyType::BrainpoolP256r1) {
27 return CryptoPP::ASN1::brainpoolP256r1();
28 } else if (key_type == KeyType::BrainpoolP384r1) {
29 return CryptoPP::ASN1::brainpoolP384r1();
30 } else {
31 return CryptoPP::OID {};
32 }
33}
34
35/**
36 * Encode public key with prefix byte
37 * - 0x02 compressed with Y0
38 * - 0x03 compressed with Y1
39 * - 0x04 uncompressed
40 *
41 * \param pub_key generic public key
42 * \return encoded public key
43 */
44ByteBuffer encode_public_key(const PublicKey& pub_key)
45{
46 ByteBuffer encoded;
47
48 if (pub_key.compression == KeyCompression::NoCompression) {
49 encoded.reserve(1 + pub_key.x.size() + pub_key.y.size());
50 encoded.push_back(0x04);
51 encoded.insert(encoded.end(), pub_key.x.begin(), pub_key.x.end());
52 encoded.insert(encoded.end(), pub_key.y.begin(), pub_key.y.end());
53 } else if (pub_key.compression == KeyCompression::Y0) {
54 encoded.reserve(1 + pub_key.x.size());
55 encoded.push_back(0x02);
56 encoded.insert(encoded.end(), pub_key.x.begin(), pub_key.x.end());
57 } else if (pub_key.compression == KeyCompression::Y1) {
58 encoded.reserve(1 + pub_key.x.size());
59 encoded.push_back(0x03);
60 encoded.insert(encoded.end(), pub_key.x.begin(), pub_key.x.end());
61 }
62
63 return encoded;
64}
65
66using InternalPublicKey = CryptoPP::DL_PublicKey_EC<CryptoPP::ECP>;
67using InternalPrivateKey = CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP>;
68
69/**
70 * Convert our PublicKey type to a Crypto++ EC public key
71 * \param pub_key our public key
72 * \return public key as Crypto++ type (if conversion was possible)
73 */
74boost::optional<InternalPublicKey> convert_public_key(const PublicKey& pub_key)
75{
76 InternalPublicKey out;
77 out.AccessGroupParameters().Initialize(get_oid(pub_key.type));
78 auto& curve = out.GetGroupParameters().GetCurve();
79
80 CryptoPP::ECP::Point point;
81 ByteBuffer encoded_pub_key = encode_public_key(pub_key);
82 CryptoPP::StringStore store { encoded_pub_key.data(), encoded_pub_key.size() };
83 if (!curve.DecodePoint(point, store, store.MaxRetrievable())) {
84 return boost::none;
85 }
86 out.SetPublicElement(point);
87 return out;
88}
89
90/**
91 * Convert our PrivateKey type to a Crypto++ EC private key
92 * \param priv_key our private key
93 * \return private key as Crypto++ type
94 */
95InternalPrivateKey convert_private_key(const PrivateKey& priv_key)
96{
97 InternalPrivateKey out;
98 out.AccessGroupParameters().Initialize(get_oid(priv_key.type));
99 out.SetPrivateExponent(CryptoPP::Integer(priv_key.key.data(), priv_key.key.size()));
100 return out;
101}
102
103/**
104 * Specialized Crypto++ Verifier for C-ITS messages
105 */
106template<typename ECDSA>
107class Verifier : public ECDSA::Verifier
108{
109public:
110 using BaseVerifier = typename ECDSA::Verifier;
111
112 /**
113 * Construct verifier object for a public key
114 * \param pub public key
115 */
116 Verifier(const InternalPublicKey& pub)
117 : BaseVerifier(pub)
118 {
119 }
120
121 /**
122 * Verify digest and signature
123 * \param digest hash of to-be-verified data
124 * \param sig given signature
125 * \return true if digest, signature and public key match
126 */
127 bool VerifyDigest(const ByteBuffer& digest, const Signature& sig)
128 {
129 using namespace CryptoPP;
130 const auto& alg = this->GetSignatureAlgorithm();
131 const auto& params = this->GetAbstractGroupParameters();
132 const auto& key = this->GetKeyInterface();
133 this->GetMaterial().DoQuickSanityCheck();
134
135 Integer e { digest.data(), digest.size() };
136 Integer r { sig.r.data(), sig.r.size() };
137 Integer s { sig.s.data(), sig.s.size() };
138 return alg.Verify(params, key, e, r, s);
139 }
140};
141
142template<size_t N>
143class IdentityHash : public CryptoPP::HashTransformation
144{
145public:
146 CRYPTOPP_CONSTANT(DIGESTSIZE = N);
147
148 static const char* StaticAlgorithmName()
149 {
150 return "IdentityHash";
151 }
152
153 void Update(const uint8_t* input, size_t length) override
154 {
155 std::copy_n(input, std::min(length, m_hash.size()), m_hash.begin());;
156 }
157
158 void TruncatedFinal(uint8_t* output, size_t len) override
159 {
160 if (output != nullptr) {
161 std::copy_n(m_hash.begin(), std::min(len, m_hash.size()), output);
162 }
163 m_hash.fill(0);
164 }
165
166 unsigned int DigestSize() const override
167 {
168 return m_hash.size();
169 }
170
171private:
172 std::array<uint8_t, N> m_hash;
173};
174
175template<size_t N>
176using DigestEcdsa = CryptoPP::ECDSA<CryptoPP::ECP, IdentityHash<N>>;
177
178} // namespace
179
180using std::placeholders::_1;
181
182BackendCryptoPP::BackendCryptoPP()
183{
184}
185
186EcdsaSignature BackendCryptoPP::sign_data(const ecdsa256::PrivateKey& generic_key, const ByteBuffer& data)
187{
188 return sign_data(internal_private_key(generic_key), data);
189}
190
191EcdsaSignature BackendCryptoPP::sign_data(const Ecdsa256::PrivateKey& private_key, const ByteBuffer& data)
192{
193 // calculate signature
194 Ecdsa256::Signer signer(private_key);
195 ByteBuffer signature(signer.MaxSignatureLength(), 0x00);
196 auto signature_length = signer.SignMessage(m_prng, data.data(), data.size(), signature.data());
197 signature.resize(signature_length);
198
199 auto signature_delimiter = signature.begin();
200 std::advance(signature_delimiter, 32);
201
202 EcdsaSignature ecdsa_signature;
203 // set R
204 X_Coordinate_Only coordinate;
205 coordinate.x = ByteBuffer(signature.begin(), signature_delimiter);
206 ecdsa_signature.R = std::move(coordinate);
207 // set s
208 ByteBuffer trailer_field_buffer(signature_delimiter, signature.end());
209 ecdsa_signature.s = std::move(trailer_field_buffer);
210
211 return ecdsa_signature;
212}
213
214Signature BackendCryptoPP::sign_digest(const PrivateKey& private_key, const ByteBuffer& digest)
215{
216 static const Signature dummy = {};
217
218 CryptoPP::DL_Keys_ECDSA<CryptoPP::ECP>::PrivateKey key;
219 CryptoPP::Integer integer { private_key.key.data(), private_key.key.size() };
220
221 if (private_key.type == KeyType::NistP256) {
222 CryptoPP::Integer integer { private_key.key.data(), private_key.key.size() };
223 key.Initialize(CryptoPP::ASN1::secp256r1(), integer);
224 } else if (private_key.type == KeyType::BrainpoolP256r1) {
225 CryptoPP::Integer integer { private_key.key.data(), private_key.key.size() };
226 key.Initialize(CryptoPP::ASN1::brainpoolP256r1(), integer);
227 } else if (private_key.type == KeyType::BrainpoolP384r1) {
228 CryptoPP::Integer integer { private_key.key.data(), private_key.key.size() };
229 key.Initialize(CryptoPP::ASN1::brainpoolP384r1(), integer);
230 } else {
231 return dummy;
232 }
233
234 auto calculate_signature = [&](CryptoPP::PK_Signer* signer) -> Signature
235 {
236 // calculate signature
237 ByteBuffer signature(signer->MaxSignatureLength(), 0x00);
238 auto signature_length = signer->SignMessage(m_prng, digest.data(), digest.size(), signature.data());
239 signature.resize(signature_length);
240
241 auto signature_delimiter = signature.begin();
242 std::advance(signature_delimiter, signer->MaxSignatureLength() / 2);
243
244 Signature ecdsa_signature;
245 ecdsa_signature.type = private_key.type;
246 ecdsa_signature.r = ByteBuffer(signature.begin(), signature_delimiter);
247 ecdsa_signature.s = ByteBuffer(signature_delimiter, signature.end());
248 return ecdsa_signature;
249 };
250
251 if (digest.size() == 32) {
252 DigestEcdsa<32>::Signer signer(key);
253 return calculate_signature(&signer);
254 } else if (digest.size() == 48) {
255 DigestEcdsa<48>::Signer signer(key);
256 return calculate_signature(&signer);
257 } else {
258 return dummy;
259 }
260}
261
262bool BackendCryptoPP::verify_data(const ecdsa256::PublicKey& generic_key, const ByteBuffer& msg, const EcdsaSignature& sig)
263{
264 const ByteBuffer sigbuf = extract_signature_buffer(sig);
265 return verify_data(internal_public_key(generic_key), msg, sigbuf);
266}
267
268bool BackendCryptoPP::verify_digest(const PublicKey& public_key, const ByteBuffer& digest, const Signature& sig)
269{
270 if (public_key.type != sig.type) {
271 return false;
272 }
273
274 boost::optional<InternalPublicKey> internal_pub_key = convert_public_key(public_key);
275 if (!internal_pub_key) {
276 return false;
277 } else if (!internal_pub_key->Validate(m_prng, 3)) {
278 return false;
279 }
280
281 if (sig.type == KeyType::NistP256 || sig.type == KeyType::BrainpoolP256r1) {
282 Verifier<Ecdsa256> verifier(*internal_pub_key);
283 return verifier.VerifyDigest(digest, sig);
284 } else if (sig.type == KeyType::BrainpoolP384r1) {
285 Verifier<Ecdsa384> verifier(*internal_pub_key);
286 return verifier.VerifyDigest(digest, sig);
287 }
288
289 return false;
290}
291
292bool BackendCryptoPP::verify_data(const Ecdsa256::PublicKey& public_key, const ByteBuffer& msg, const ByteBuffer& sig)
293{
294 Ecdsa256::Verifier verifier(public_key);
295 return verifier.VerifyMessage(msg.data(), msg.size(), sig.data(), sig.size());
296}
297
298boost::optional<Uncompressed> BackendCryptoPP::decompress_point(const EccPoint& ecc_point)
299{
300 struct DecompressionVisitor : public boost::static_visitor<bool>
301 {
302 bool operator()(const X_Coordinate_Only&)
303 {
304 return false;
305 }
306
307 bool operator()(const Compressed_Lsb_Y_0& p)
308 {
309 decompress(p.x, 0x02);
310 return true;
311 }
312
313 bool operator()(const Compressed_Lsb_Y_1& p)
314 {
315 decompress(p.x, 0x03);
316 return true;
317 }
318
319 bool operator()(const Uncompressed& p)
320 {
321 result = p;
322 return true;
323 }
324
325 void decompress(const ByteBuffer& x, ByteBuffer::value_type type)
326 {
327 ByteBuffer compact;
328 compact.reserve(x.size() + 1);
329 compact.push_back(type);
330 std::copy(x.begin(), x.end(), std::back_inserter(compact));
331
332 CryptoPP::ECP::Point point;
333 CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> group(CryptoPP::ASN1::secp256r1());
334 group.GetCurve().DecodePoint(point, compact.data(), compact.size());
335
336 result.x = x;
337 result.y.resize(result.x.size());
338 point.y.Encode(result.y.data(), result.y.size());
339 }
340
341 Uncompressed result;
342 };
343
344 DecompressionVisitor visitor;
345 if (boost::apply_visitor(visitor, ecc_point)) {
346 return visitor.result;
347 } else {
348 return boost::none;
349 }
350}
351
352ByteBuffer BackendCryptoPP::calculate_hash(HashAlgorithm algo_id, const ByteBuffer& buffer)
353{
354 ByteBuffer hash;
355 if (algo_id == HashAlgorithm::SHA256) {
356 CryptoPP::SHA256 algo;
357 hash.resize(algo.DigestSize());
358 algo.CalculateDigest(hash.data(), buffer.data(), buffer.size());
359 } else if (algo_id == HashAlgorithm::SHA384) {
360 CryptoPP::SHA384 algo;
361 hash.resize(algo.DigestSize());
362 algo.CalculateDigest(hash.data(), buffer.data(), buffer.size());
363 }
364 return hash;
365}
366
368{
370 auto private_key = generate_private_key();
371 auto& private_exponent = private_key.GetPrivateExponent();
372 assert(kp.private_key.key.size() >= private_exponent.ByteCount());
373 private_exponent.Encode(kp.private_key.key.data(), kp.private_key.key.size());
374
375 auto public_key = generate_public_key(private_key);
376 auto& public_element = public_key.GetPublicElement();
377 assert(kp.public_key.x.size() >= public_element.x.ByteCount());
378 assert(kp.public_key.y.size() >= public_element.y.ByteCount());
379 public_element.x.Encode(kp.public_key.x.data(), kp.public_key.x.size());
380 public_element.y.Encode(kp.public_key.y.data(), kp.public_key.y.size());
381 return kp;
382}
383
384BackendCryptoPP::Ecdsa256::PrivateKey BackendCryptoPP::generate_private_key()
385{
386 CryptoPP::OID oid(CryptoPP::ASN1::secp256r1());
387 Ecdsa256::PrivateKey private_key;
388 private_key.Initialize(m_prng, oid);
389 assert(private_key.Validate(m_prng, 3));
390 return private_key;
391}
392
393BackendCryptoPP::Ecdsa256::PublicKey BackendCryptoPP::generate_public_key(const Ecdsa256::PrivateKey& private_key)
394{
395 Ecdsa256::PublicKey public_key;
396 private_key.MakePublicKey(public_key);
397 assert(public_key.Validate(m_prng, 3));
398 return public_key;
399}
400
401BackendCryptoPP::Ecdsa256::PublicKey BackendCryptoPP::internal_public_key(const ecdsa256::PublicKey& generic)
402{
403 CryptoPP::Integer x { generic.x.data(), generic.x.size() };
404 CryptoPP::Integer y { generic.y.data(), generic.y.size() };
405 CryptoPP::ECP::Point q { x, y };
406
407 Ecdsa256::PublicKey pub;
408 pub.Initialize(CryptoPP::ASN1::secp256r1(), q);
409 assert(pub.Validate(m_prng, 3));
410 return pub;
411}
412
413BackendCryptoPP::Ecdsa256::PrivateKey BackendCryptoPP::internal_private_key(const ecdsa256::PrivateKey& generic)
414{
415 Ecdsa256::PrivateKey key;
416 CryptoPP::Integer integer { generic.key.data(), generic.key.size() };
417 key.Initialize(CryptoPP::ASN1::secp256r1(), integer);
418 return key;
419}
420
421} // namespace security
422} // namespace vanetza
Ecdsa256::PrivateKey generate_private_key()
create private key
Ecdsa256::PublicKey internal_public_key(const ecdsa256::PublicKey &)
adapt generic public key to internal structure
ByteBuffer calculate_hash(HashAlgorithm, const ByteBuffer &) override
boost::optional< Uncompressed > decompress_point(const EccPoint &ecc_point) override
Ecdsa256::PublicKey generate_public_key(const Ecdsa256::PrivateKey &)
derive public key from private key
bool verify_data(const ecdsa256::PublicKey &public_key, const ByteBuffer &data, const EcdsaSignature &sig) override
Signature sign_digest(const PrivateKey &, const ByteBuffer &digest) override
EcdsaSignature sign_data(const ecdsa256::PrivateKey &private_key, const ByteBuffer &data_buffer) override
bool verify_digest(const PublicKey &, const ByteBuffer &digest, const Signature &) override
ecdsa256::KeyPair generate_key_pair()
generate a private key and the corresponding public key
Ecdsa256::PrivateKey internal_private_key(const ecdsa256::PrivateKey &)
adapt generic private key to internal structure
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