]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/opensslsigners.cc
Merge pull request #3139 from mind04/openssl
[thirdparty/pdns.git] / pdns / opensslsigners.cc
CommitLineData
7581a35b
KM
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4#include <openssl/obj_mac.h>
5#include <openssl/ecdsa.h>
6#include <openssl/sha.h>
7#include <openssl/rand.h>
8
9#include "dnssecinfra.hh"
10
11
12class OpenSSLECDSADNSCryptoKeyEngine : public DNSCryptoKeyEngine
13{
14public:
15 explicit OpenSSLECDSADNSCryptoKeyEngine(unsigned int algo) : DNSCryptoKeyEngine(algo)
16 {RAND_cleanup();
17
18 int ret = RAND_status();
19 if (ret != 1) {
20 throw runtime_error(getName()+" insufficient entropy");
21 }
22
23 d_eckey = EC_KEY_new();
24 if (d_eckey == NULL) {
25 throw runtime_error(getName()+" allocation of key structure failed");
26 }
27
28 if(d_algorithm == 13) {
29 d_ecgroup = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
30 d_len = 32;
31 } else if (d_algorithm == 14) {
32 d_ecgroup = EC_GROUP_new_by_curve_name(NID_secp384r1);
33 d_len = 48;
34 } else {
35 throw runtime_error(getName()+" unknown algorithm "+std::to_string(d_algorithm));
36 }
37 if (d_ecgroup == NULL) {
38 throw runtime_error(getName()+" allocation of group structure failed");
39 }
40
41 ret = EC_KEY_set_group(d_eckey,d_ecgroup);
42 if (ret != 1) {
43 throw runtime_error(getName()+" setting key group failed");
44 }
45
46 }
47
48 ~OpenSSLECDSADNSCryptoKeyEngine()
49 {
50 EC_KEY_free(d_eckey);
51 EC_GROUP_free(d_ecgroup);
52 BN_CTX_free(d_ctx);
53 }
54
55 string getName() const { return "OpenSSL ECDSA"; }
56 int getBits() const { return d_len << 3; }
57
58 void create(unsigned int bits);
59 storvector_t convertToISCVector() const;
60 std::string hash(const std::string& hash) const;
61 std::string sign(const std::string& hash) const;
62 bool verify(const std::string& hash, const std::string& signature) const;
63 std::string getPubKeyHash() const;
64 std::string getPublicKeyString() const;
65 void fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap);
66 void fromPublicKeyString(const std::string& content);
67
68 static DNSCryptoKeyEngine* maker(unsigned int algorithm)
69 {
70 return new OpenSSLECDSADNSCryptoKeyEngine(algorithm);
71 }
72
73private:
74 unsigned int d_len;
75
76 EC_KEY *d_eckey = NULL;
77 EC_GROUP *d_ecgroup = NULL;
78 BN_CTX *d_ctx = NULL;
79};
80
81
82void OpenSSLECDSADNSCryptoKeyEngine::create(unsigned int bits)
83{
84 if (bits >> 3 != d_len) {
85 throw runtime_error(getName()+" unknown key length of "+std::to_string(bits)+" bits requested");
86 }
87
88 int res = EC_KEY_generate_key(d_eckey);
89 if (res == 0) {
90 throw runtime_error(getName()+" key generation failed");
91 }
92}
93
94
95DNSCryptoKeyEngine::storvector_t OpenSSLECDSADNSCryptoKeyEngine::convertToISCVector() const
96{
97 storvector_t storvect;
98 string algorithm;
99
100 if(d_algorithm == 13)
101 algorithm = "13 (ECDSAP256SHA256)";
102 else if(d_algorithm == 14)
103 algorithm = "14 (ECDSAP384SHA384)";
104 else
105 algorithm = " ? (?)";
106
107 storvect.push_back(make_pair("Algorithm", algorithm));
108
109 const BIGNUM *key = EC_KEY_get0_private_key(d_eckey);
110 if (key == NULL) {
111 throw runtime_error(getName()+" private key not set");
112 }
113
114 unsigned char tmp[BN_num_bytes(key)];
115 int len = BN_bn2bin(key, tmp);
116
117 string prefix;
118 if (d_len - len)
119 prefix.append(d_len - len, 0x00);
120
121 storvect.push_back(make_pair("PrivateKey", prefix + string((char*) tmp, sizeof(tmp))));
122
123 return storvect;
124}
125
126
127std::string OpenSSLECDSADNSCryptoKeyEngine::hash(const std::string& orig) const
128{
129 if(getBits() == 256) {
130 unsigned char hash[SHA256_DIGEST_LENGTH];
131 SHA256((unsigned char*) orig.c_str(), orig.length(), hash);
132 return string((char*) hash, sizeof(hash));
133 }
134 else if(getBits() == 384) {
135 unsigned char hash[SHA384_DIGEST_LENGTH];
136 SHA384((unsigned char*) orig.c_str(), orig.length(), hash);
137 return string((char*) hash, sizeof(hash));
138 }
139
140 throw runtime_error(getName()+" does not support a hash size of "+std::to_string(getBits())+" bits");
141}
142
143
144std::string OpenSSLECDSADNSCryptoKeyEngine::sign(const std::string& msg) const
145{
146 string hash = this->hash(msg);
147
148 ECDSA_SIG *signature = ECDSA_do_sign((unsigned char*) hash.c_str(), hash.length(), d_eckey);
149 if (NULL == signature) {
150 throw runtime_error(getName()+" failed to generate signature");
151 }
152
153 string ret;
154 unsigned char tmp[d_len];
155
156 int len = BN_bn2bin(signature->r, tmp);
157 if (d_len - len)
158 ret.append(d_len - len, 0x00);
159 ret.append(string((char*) tmp, len));
160
161 len = BN_bn2bin(signature->s, tmp);
162 if (d_len - len)
163 ret.append(d_len - len, 0x00);
164 ret.append(string((char*) tmp, len));
165
166 ECDSA_SIG_free(signature);
167
168 return ret;
169}
170
171
172bool OpenSSLECDSADNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const
173{
174 if (signature.length() != (d_len * 2)) {
175 throw runtime_error(getName()+" invalid signature size "+std::to_string(signature.length()));
176 }
177
178 string hash = this->hash(msg);
179
180 ECDSA_SIG *sig;
181 sig = ECDSA_SIG_new();
182 if (sig == NULL) {
183 throw runtime_error(getName()+" allocation of signature structure failed");
184 }
185
186 sig->r = BN_bin2bn((unsigned char*) signature.c_str(), d_len, sig->r);
187 sig->s = BN_bin2bn((unsigned char*) signature.c_str() + d_len, d_len, sig->s);
188 if (!sig->r || !sig->s) {
189 ECDSA_SIG_free(sig);
190 throw runtime_error(getName()+" invalid signature");
191 }
192
193 int ret = ECDSA_do_verify((unsigned char*) hash.c_str(), hash.length(), sig, d_eckey);
194
195 ECDSA_SIG_free(sig);
196
197 if (ret == -1){
198 throw runtime_error(getName()+" verify error");
199 }
200
201 return (ret == 1);
202}
203
204
205std::string OpenSSLECDSADNSCryptoKeyEngine::getPubKeyHash() const
206{
207 string pubKey = getPublicKeyString();
208 unsigned char hash[SHA_DIGEST_LENGTH];
209 SHA1((unsigned char*) pubKey.c_str(), pubKey.length(), hash);
210 return string((char*) hash, sizeof(hash));
211}
212
213
214std::string OpenSSLECDSADNSCryptoKeyEngine::getPublicKeyString() const
215{
216 unsigned char binaryPoint[(d_len * 2) + 1];
217
218 int ret = EC_POINT_point2oct(d_ecgroup, EC_KEY_get0_public_key(d_eckey), POINT_CONVERSION_UNCOMPRESSED, binaryPoint, sizeof(binaryPoint), d_ctx);
219 if (ret == 0) {
220 throw runtime_error(getName()+" exporting point to binary failed");
221 }
222
223 /* we skip the first byte as the other backends use
224 raw field elements, as opposed to the format described in
225 SEC1: "2.3.3 Elliptic-Curve-Point-to-Octet-String Conversion" */
226 return string((const char *)(binaryPoint + 1), sizeof(binaryPoint) - 1);
227}
228
229
230void OpenSSLECDSADNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, std::map<std::string, std::string>& stormap)
231{
232 drc.d_algorithm = atoi(stormap["algorithm"].c_str());
233
234 if (drc.d_algorithm != d_algorithm) {
235 throw runtime_error(getName()+" tried to feed an algorithm "+std::to_string(drc.d_algorithm)+" to a "+std::to_string(d_algorithm)+" key");
236 }
237
238 string privateKey = stormap["privatekey"];
239
240 BIGNUM *prv_key = BN_bin2bn((unsigned char*) privateKey.c_str(), privateKey.length(), NULL);
241 if (prv_key == NULL) {
242 throw runtime_error(getName()+" reading private key from binary failed");
243 }
244
245 int ret = EC_KEY_set_private_key(d_eckey, prv_key);
246 if (ret != 1) {
247 BN_free(prv_key);
248 throw runtime_error(getName()+" setting private key failed");
249 }
250
251 EC_POINT *pub_key = EC_POINT_new(d_ecgroup);
252 if (pub_key == NULL) {
253 BN_free(prv_key);
254 throw runtime_error(getName()+" allocation of public key point failed");
255 }
256
257 ret = EC_POINT_mul(d_ecgroup, pub_key, prv_key, NULL, NULL, d_ctx);
258 if (ret != 1) {
259 EC_POINT_free(pub_key);
260 BN_free(prv_key);
261 throw runtime_error(getName()+" computing public key from private failed");
262 }
263
264 BN_free(prv_key);
265
266 ret = EC_KEY_set_public_key(d_eckey, pub_key);
267 if (ret != 1) {
268 EC_POINT_free(pub_key);
269 throw runtime_error(getName()+" setting public key failed");
270 }
271
272 EC_POINT_free(pub_key);
273
274 ret = EC_KEY_check_key(d_eckey);
275 if (ret != 1) {
276 throw runtime_error(getName()+" invalid public key");
277 }
278
279}
280
281
282void OpenSSLECDSADNSCryptoKeyEngine::fromPublicKeyString(const std::string& input)
283{
284 /* uncompressed point, from SEC1:
285 "2.3.4 Octet-String-to-Elliptic-Curve-Point Conversion" */
286 string ecdsaPoint= "\x04";
287 ecdsaPoint.append(input);
288
289 EC_POINT *pub_key = EC_POINT_new(d_ecgroup);
290 if (pub_key == NULL) {
291 throw runtime_error(getName()+" allocation of point structure failed");
292 }
293
294 int ret = EC_POINT_oct2point(d_ecgroup, pub_key, (unsigned char*) ecdsaPoint.c_str(), ecdsaPoint.length(), d_ctx);
295 if (ret != 1) {
296 throw runtime_error(getName()+" reading ECP point from binary failed");
297 }
298
299 ret = EC_KEY_set_private_key(d_eckey, NULL);
300 if (ret == 1) {
301 EC_POINT_free(pub_key);
302 throw runtime_error(getName()+" setting private key failed");
303 }
304
305 ret = EC_KEY_set_public_key(d_eckey, pub_key);
306 if (ret != 1) {
307 EC_POINT_free(pub_key);
308 throw runtime_error(getName()+" setting public key failed");
309 }
310
311 EC_POINT_free(pub_key);
312
313 ret = EC_KEY_check_key(d_eckey);
314 if (ret != 1) {
315 throw runtime_error(getName()+" invalid public key");
316 }
317}
318
319
320namespace {
321 struct LoaderStruct
322 {
323 LoaderStruct()
324 {
325 DNSCryptoKeyEngine::report(13, &OpenSSLECDSADNSCryptoKeyEngine::maker);
326 DNSCryptoKeyEngine::report(14, &OpenSSLECDSADNSCryptoKeyEngine::maker);
327 }
328 } loaderOpenSSL;
329}