Vanetza
Loading...
Searching...
No Matches
persistence.cpp
1#include <vanetza/security/persistence.hpp>
2#include <vanetza/security/key_type.hpp>
3#include <fstream>
4#include <stdexcept>
5
6#ifdef VANETZA_WITH_OPENSSL
7#include <vanetza/security/openssl_wrapper.hpp>
8#include <openssl/bn.h>
9#include <openssl/ec.h>
10#include <openssl/objects.h>
11#include <openssl/pem.h>
12#endif
13
14#ifdef VANETZA_WITH_CRYPTOPP
15#include <cryptopp/base64.h>
16#include <cryptopp/eccrypto.h>
17#include <cryptopp/files.h>
18#include <cryptopp/integer.h>
19#include <cryptopp/oids.h>
20#include <cryptopp/osrng.h>
21#include <cryptopp/queue.h>
22#include <cryptopp/sha.h>
23#endif
24
25namespace vanetza
26{
27namespace security
28{
29
30#ifdef VANETZA_WITH_OPENSSL
31namespace
32{
33
34KeyType key_type_from_nid(int nid)
35{
36 switch (nid) {
37 case NID_X9_62_prime256v1:
38 return KeyType::NistP256;
39 case NID_brainpoolP256r1:
40 return KeyType::BrainpoolP256r1;
41 case NID_brainpoolP384r1:
42 return KeyType::BrainpoolP384r1;
43 default:
44 return KeyType::Unspecified;
45 }
46}
47
48PrivateKey extract_private_key(openssl::EvpKey& pkey)
49{
50 openssl::Key ec_key(EVP_PKEY_get1_EC_KEY(pkey));
51 if (!ec_key) {
52 throw std::runtime_error("Key is not an EC key");
53 }
54
55 const EC_GROUP* group = EC_KEY_get0_group(ec_key);
56 const KeyType type = key_type_from_nid(group ? EC_GROUP_get_curve_name(group) : NID_undef);
57 if (type == KeyType::Unspecified) {
58 throw std::runtime_error("Unsupported EC curve in private key");
59 }
60
61 const BIGNUM* priv_bn = EC_KEY_get0_private_key(ec_key);
62 if (!priv_bn) {
63 throw std::runtime_error("EC key has no private component");
64 }
65
66 PrivateKey key;
67 key.type = type;
68 key.key.resize(key_length(type));
69 if (BN_bn2binpad(priv_bn, key.key.data(), key.key.size()) < 0) {
70 throw std::runtime_error("private key does not fit the curve length");
71 }
72 return key;
73}
74
75} // namespace
76
77PrivateKey load_private_key_from_pem_file_openssl(const std::string& key_path)
78{
79 openssl::Bio bio(BIO_new_file(key_path.c_str(), "rb"));
80 if (!bio) {
81 throw std::runtime_error("Cannot open key file: " + key_path);
82 }
83 openssl::EvpKey pkey(PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr));
84 if (!pkey) {
85 throw std::runtime_error("Failed to load PEM private key from: " + key_path);
86 }
87 return extract_private_key(pkey);
88}
89
90PrivateKey load_private_key_from_der_file_openssl(const std::string& key_path)
91{
92 openssl::Bio bio(BIO_new_file(key_path.c_str(), "rb"));
93 if (!bio) {
94 throw std::runtime_error("Cannot open key file: " + key_path);
95 }
96 openssl::EvpKey pkey(d2i_PrivateKey_bio(bio, nullptr));
97 if (!pkey) {
98 throw std::runtime_error("Failed to load DER private key from: " + key_path);
99 }
100 return extract_private_key(pkey);
101}
102#endif /* VANETZA_WITH_OPENSSL */
103
104#ifdef VANETZA_WITH_CRYPTOPP
105namespace
106{
107
108using EcPrivateKey = CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey;
109using EcGroupParameters = CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP>;
110
111KeyType detect_curve(const EcGroupParameters& gp)
112{
113 KeyType key_type = KeyType::Unspecified;
114
115 if (EcGroupParameters(CryptoPP::ASN1::secp256r1()) == gp) {
116 key_type = KeyType::NistP256;
117 } else if (EcGroupParameters(CryptoPP::ASN1::brainpoolP256r1()) == gp) {
118 key_type = KeyType::BrainpoolP256r1;
119 } else if (EcGroupParameters(CryptoPP::ASN1::brainpoolP384r1()) == gp) {
120 key_type = KeyType::BrainpoolP384r1;
121 }
122
123 return key_type;
124}
125
126PrivateKey extract_private_key(CryptoPP::BufferedTransformation& der)
127{
128 CryptoPP::AutoSeededRandomPool rng;
129 EcPrivateKey private_key;
130 private_key.Load(der);
131 if (!private_key.Validate(rng, 3)) {
132 throw std::runtime_error("Private key validation failed");
133 }
134
135 const KeyType type = detect_curve(private_key.GetGroupParameters());
136 if (type == KeyType::Unspecified) {
137 throw std::runtime_error("Unsupported EC curve in private key");
138 }
139
140 PrivateKey key;
141 key.type = type;
142 key.key.resize(key_length(type));
143 private_key.GetPrivateExponent().Encode(key.key.data(), key.key.size());
144 return key;
145}
146
147void pem_decode(std::istream& in, CryptoPP::BufferedTransformation& dest)
148{
149 static const std::string HEADER = "-----BEGIN PRIVATE KEY-----";
150 static const std::string FOOTER = "-----END PRIVATE KEY-----";
151
152 std::string line;
153 while (std::getline(in, line)) {
154 if (line.find(HEADER) != std::string::npos) {
155 break;
156 }
157 }
158 if (!in) {
159 throw std::runtime_error("PEM header not found");
160 }
161
162 CryptoPP::Base64Decoder decoder;
163 decoder.Attach(new CryptoPP::Redirector(dest));
164
165 while (std::getline(in, line)) {
166 if (line.find(FOOTER) != std::string::npos) {
167 break;
168 }
169 decoder.Put(reinterpret_cast<const uint8_t*>(line.data()), line.length());
170 }
171 if (!in) {
172 throw std::runtime_error("PEM footer not found");
173 }
174
175 decoder.MessageEnd();
176}
177
178} // namespace
179
180PrivateKey load_private_key_from_pem_file_cryptopp(const std::string& key_path)
181{
182 std::ifstream file(key_path);
183 if (!file) {
184 throw std::runtime_error("Cannot open key file: " + key_path);
185 }
186 CryptoPP::ByteQueue der;
187 pem_decode(file, der);
188 return extract_private_key(der);
189}
190
191PrivateKey load_private_key_from_der_file_cryptopp(const std::string& key_path)
192{
193 try {
194 CryptoPP::FileSource source(key_path.c_str(), true);
195 return extract_private_key(source);
196 } catch (const CryptoPP::FileStore::OpenErr&) {
197 throw std::runtime_error("Cannot open key file: " + key_path);
198 }
199}
200#endif /* VANETZA_WITH_CRYPTOPP */
201
202PrivateKey load_private_key_from_pem_file(const std::string& key_path)
203{
204#if defined(VANETZA_WITH_OPENSSL)
205 return load_private_key_from_pem_file_openssl(key_path);
206#elif defined(VANETZA_WITH_CRYPTOPP)
207 return load_private_key_from_pem_file_cryptopp(key_path);
208#else
209# warning "no crypto backend available for persistence"
210 return PrivateKey {};
211#endif
212}
213
214PrivateKey load_private_key_from_der_file(const std::string& key_path)
215{
216#if defined(VANETZA_WITH_OPENSSL)
217 return load_private_key_from_der_file_openssl(key_path);
218#elif defined(VANETZA_WITH_CRYPTOPP)
219 return load_private_key_from_der_file_cryptopp(key_path);
220#else
221# warning "no crypto backend available for persistence"
222 return PrivateKey {};
223#endif
224}
225
226} // namespace security
227} // namespace vanetza