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>;
67
68/**
69 * Convert our PublicKey type to a Crypto++ EC public key
70 * \param pub_key our public key
71 * \return public key as Crypto++ type (if conversion was possible)
72 */
73boost::optional<InternalPublicKey> convert_public_key(const PublicKey& pub_key)
74{
75 InternalPublicKey out;
76 out.AccessGroupParameters().Initialize(get_oid(pub_key.type));
77 auto& curve = out.GetGroupParameters().GetCurve();
78
79 CryptoPP::ECP::Point point;
80 ByteBuffer encoded_pub_key = encode_public_key(pub_key);
81 CryptoPP::StringStore store { encoded_pub_key.data(), encoded_pub_key.size() };
82 if (!curve.DecodePoint(point, store, store.MaxRetrievable())) {
83 return boost::none;
84 }
85 out.SetPublicElement(point);
86 return out;
87}
88
89/**
90 * Specialized Crypto++ Verifier for C-ITS messages
91 */
92template<typename ECDSA>
93class Verifier : public ECDSA::Verifier
94{
95public:
96 using BaseVerifier = typename ECDSA::Verifier;
97
98 /**
99 * Construct verifier object for a public key
100 * \param pub public key
101 */
102 Verifier(const InternalPublicKey& pub)
103 : BaseVerifier(pub)
104 {
105 }
106
107 /**
108 * Verify digest and signature
109 * \param digest hash of to-be-verified data
110 * \param sig given signature
111 * \return true if digest, signature and public key match
112 */
113 bool VerifyDigest(const ByteBuffer& digest, const Signature& sig)
114 {
115 using namespace CryptoPP;
116 const auto& alg = this->GetSignatureAlgorithm();
117 const auto& params = this->GetAbstractGroupParameters();
118 const auto& key = this->GetKeyInterface();
119 this->GetMaterial().DoQuickSanityCheck();
120
121 Integer e { digest.data(), digest.size() };
122 Integer r { sig.r.data(), sig.r.size() };
123 Integer s { sig.s.data(), sig.s.size() };
124 return alg.Verify(params, key, e, r, s);
125 }
126};
127
128} // namespace
129
130using std::placeholders::_1;
131
132BackendCryptoPP::BackendCryptoPP() :
133 m_private_cache(std::bind(&BackendCryptoPP::internal_private_key, this, _1), 8),
134 m_public_cache(std::bind(&BackendCryptoPP::internal_public_key, this, _1), 2048)
135{
136}
137
138EcdsaSignature BackendCryptoPP::sign_data(const ecdsa256::PrivateKey& generic_key, const ByteBuffer& data)
139{
140 return sign_data(m_private_cache[generic_key], data);
141}
142
143EcdsaSignature BackendCryptoPP::sign_data(const Ecdsa256::PrivateKey& private_key, const ByteBuffer& data)
144{
145 // calculate signature
146 Ecdsa256::Signer signer(private_key);
147 ByteBuffer signature(signer.MaxSignatureLength(), 0x00);
148 auto signature_length = signer.SignMessage(m_prng, data.data(), data.size(), signature.data());
149 signature.resize(signature_length);
150
151 auto signature_delimiter = signature.begin();
152 std::advance(signature_delimiter, 32);
153
154 EcdsaSignature ecdsa_signature;
155 // set R
156 X_Coordinate_Only coordinate;
157 coordinate.x = ByteBuffer(signature.begin(), signature_delimiter);
158 ecdsa_signature.R = std::move(coordinate);
159 // set s
160 ByteBuffer trailer_field_buffer(signature_delimiter, signature.end());
161 ecdsa_signature.s = std::move(trailer_field_buffer);
162
163 return ecdsa_signature;
164}
165
166bool BackendCryptoPP::verify_data(const ecdsa256::PublicKey& generic_key, const ByteBuffer& msg, const EcdsaSignature& sig)
167{
168 const ByteBuffer sigbuf = extract_signature_buffer(sig);
169 return verify_data(m_public_cache[generic_key], msg, sigbuf);
170}
171
172bool BackendCryptoPP::verify_digest(const PublicKey& public_key, const ByteBuffer& digest, const Signature& sig)
173{
174 if (public_key.type != sig.type) {
175 return false;
176 }
177
178 boost::optional<InternalPublicKey> internal_pub_key = convert_public_key(public_key);
179 if (!internal_pub_key) {
180 return false;
181 } else if (!internal_pub_key->Validate(m_prng, 3)) {
182 return false;
183 }
184
185 if (sig.type == KeyType::NistP256 || sig.type == KeyType::BrainpoolP256r1) {
186 Verifier<Ecdsa256> verifier(*internal_pub_key);
187 return verifier.VerifyDigest(digest, sig);
188 } else if (sig.type == KeyType::BrainpoolP384r1) {
189 Verifier<Ecdsa384> verifier(*internal_pub_key);
190 return verifier.VerifyDigest(digest, sig);
191 }
192
193 return false;
194}
195
196bool BackendCryptoPP::verify_data(const Ecdsa256::PublicKey& public_key, const ByteBuffer& msg, const ByteBuffer& sig)
197{
198 Ecdsa256::Verifier verifier(public_key);
199 return verifier.VerifyMessage(msg.data(), msg.size(), sig.data(), sig.size());
200}
201
202boost::optional<Uncompressed> BackendCryptoPP::decompress_point(const EccPoint& ecc_point)
203{
204 struct DecompressionVisitor : public boost::static_visitor<bool>
205 {
206 bool operator()(const X_Coordinate_Only&)
207 {
208 return false;
209 }
210
211 bool operator()(const Compressed_Lsb_Y_0& p)
212 {
213 decompress(p.x, 0x02);
214 return true;
215 }
216
217 bool operator()(const Compressed_Lsb_Y_1& p)
218 {
219 decompress(p.x, 0x03);
220 return true;
221 }
222
223 bool operator()(const Uncompressed& p)
224 {
225 result = p;
226 return true;
227 }
228
229 void decompress(const ByteBuffer& x, ByteBuffer::value_type type)
230 {
231 ByteBuffer compact;
232 compact.reserve(x.size() + 1);
233 compact.push_back(type);
234 std::copy(x.begin(), x.end(), std::back_inserter(compact));
235
236 CryptoPP::ECP::Point point;
237 CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> group(CryptoPP::ASN1::secp256r1());
238 group.GetCurve().DecodePoint(point, compact.data(), compact.size());
239
240 result.x = x;
241 result.y.resize(result.x.size());
242 point.y.Encode(result.y.data(), result.y.size());
243 }
244
245 Uncompressed result;
246 };
247
248 DecompressionVisitor visitor;
249 if (boost::apply_visitor(visitor, ecc_point)) {
250 return visitor.result;
251 } else {
252 return boost::none;
253 }
254}
255
256ByteBuffer BackendCryptoPP::calculate_hash(KeyType key, const ByteBuffer& buffer)
257{
258 ByteBuffer hash;
259 switch (key) {
260 case KeyType::NistP256:
261 case KeyType::BrainpoolP256r1: {
262 CryptoPP::SHA256 algo;
263 hash.resize(algo.DigestSize());
264 algo.CalculateDigest(hash.data(), buffer.data(), buffer.size());
265 break;
266 }
267 case KeyType::BrainpoolP384r1: {
268 CryptoPP::SHA384 algo;
269 hash.resize(algo.DigestSize());
270 algo.CalculateDigest(hash.data(), buffer.data(), buffer.size());
271 break;
272 }
273 default:
274 break;
275 }
276
277 return hash;
278}
279
280ecdsa256::KeyPair BackendCryptoPP::generate_key_pair()
281{
283 auto private_key = generate_private_key();
284 auto& private_exponent = private_key.GetPrivateExponent();
285 assert(kp.private_key.key.size() >= private_exponent.ByteCount());
286 private_exponent.Encode(kp.private_key.key.data(), kp.private_key.key.size());
287
288 auto public_key = generate_public_key(private_key);
289 auto& public_element = public_key.GetPublicElement();
290 assert(kp.public_key.x.size() >= public_element.x.ByteCount());
291 assert(kp.public_key.y.size() >= public_element.y.ByteCount());
292 public_element.x.Encode(kp.public_key.x.data(), kp.public_key.x.size());
293 public_element.y.Encode(kp.public_key.y.data(), kp.public_key.y.size());
294 return kp;
295}
296
297BackendCryptoPP::Ecdsa256::PrivateKey BackendCryptoPP::generate_private_key()
298{
299 CryptoPP::OID oid(CryptoPP::ASN1::secp256r1());
300 Ecdsa256::PrivateKey private_key;
301 private_key.Initialize(m_prng, oid);
302 assert(private_key.Validate(m_prng, 3));
303 return private_key;
304}
305
306BackendCryptoPP::Ecdsa256::PublicKey BackendCryptoPP::generate_public_key(const Ecdsa256::PrivateKey& private_key)
307{
308 Ecdsa256::PublicKey public_key;
309 private_key.MakePublicKey(public_key);
310 assert(public_key.Validate(m_prng, 3));
311 return public_key;
312}
313
314BackendCryptoPP::Ecdsa256::PublicKey BackendCryptoPP::internal_public_key(const ecdsa256::PublicKey& generic)
315{
316 CryptoPP::Integer x { generic.x.data(), generic.x.size() };
317 CryptoPP::Integer y { generic.y.data(), generic.y.size() };
318 CryptoPP::ECP::Point q { x, y };
319
320 Ecdsa256::PublicKey pub;
321 pub.Initialize(CryptoPP::ASN1::secp256r1(), q);
322 assert(pub.Validate(m_prng, 3));
323 return pub;
324}
325
326BackendCryptoPP::Ecdsa256::PrivateKey BackendCryptoPP::internal_private_key(const ecdsa256::PrivateKey& generic)
327{
328 Ecdsa256::PrivateKey key;
329 CryptoPP::Integer integer { generic.key.data(), generic.key.size() };
330 key.Initialize(CryptoPP::ASN1::secp256r1(), integer);
331 return key;
332}
333
334} // namespace security
335} // namespace vanetza
STL namespace.
Compressed_Lsb_Y_0 specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition: ecc_point.hpp:20
Compressed_Lsb_Y_1 specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition: ecc_point.hpp:26
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:32
X_Coordinate_Only specified in TS 103 097 v1.2.1 in section 4.2.5.
Definition: ecc_point.hpp:14