]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/pkcs11signers.cc
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / pdns / pkcs11signers.cc
1 #include <openssl/evp.h>
2 #ifdef HAVE_CONFIG_H
3 #include "config.h"
4 #endif
5 #include <boost/assign/std/vector.hpp> // for 'operator+=()'
6 #include <boost/assign/list_of.hpp>
7
8 #include <boost/format.hpp>
9 #include <p11-kit/p11-kit.h>
10
11 #include "pdns/dnssecinfra.hh"
12 #include "pdns/logger.hh"
13 #include "pdns/pdnsexception.hh"
14 #include "pdns/sha.hh"
15 #include "pdns/lock.hh"
16
17 #ifdef HAVE_LIBCRYPTO_ECDSA
18 #include <openssl/bn.h>
19 #include <openssl/ec.h>
20 #endif
21
22 #include "misc.hh"
23 #include "pkcs11signers.hh"
24 /* TODO
25
26 - list possible tokens and supported modes
27 - Engine: <name>, Slot: <slot>, PIN: <pin>
28 - ECDSA support (how to test?)
29
30 NB! If you do use this, here is a simple way to get softhsm working
31
32 create /etc/pkcs11/modules/softhsm.module
33
34 put
35
36 module: /usr/lib/softhsm/libsofthsm.so
37 managed: yes
38
39 in it. you need to use softhsm tools to manage this all.
40
41 */
42
43 #ifdef HAVE_P11KIT1_V2
44 static CK_FUNCTION_LIST** p11_modules;
45 #endif
46
47 static constexpr const char* ECDSA256_PARAMS{"\x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"};
48 static constexpr const char* ECDSA384_PARAMS{"\x06\x05\x2b\x81\x04\x00\x22"};
49
50 // map for signing algorithms
51 static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2smech = boost::assign::map_list_of
52 (5, CKM_SHA1_RSA_PKCS)
53 (7, CKM_SHA1_RSA_PKCS)
54 (8, CKM_SHA256_RSA_PKCS)
55 (10, CKM_SHA512_RSA_PKCS)
56 (13, CKM_ECDSA)
57 (14, CKM_ECDSA);
58
59 // map for hashing algorithms
60 static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2hmech = boost::assign::map_list_of
61 (5, CKM_SHA_1)
62 (7, CKM_SHA_1)
63 (8, CKM_SHA256)
64 (10, CKM_SHA512)
65 (13, CKM_SHA256)
66 (14, CKM_SHA384);
67
68 static std::map<unsigned int,CK_MECHANISM_TYPE> dnssec2cmech = boost::assign::map_list_of
69 (5, CKM_RSA_PKCS_KEY_PAIR_GEN)
70 (7, CKM_RSA_PKCS_KEY_PAIR_GEN)
71 (8, CKM_RSA_PKCS_KEY_PAIR_GEN)
72 (10, CKM_RSA_PKCS_KEY_PAIR_GEN)
73 (13, CKM_ECDSA_KEY_PAIR_GEN)
74 (14, CKM_ECDSA_KEY_PAIR_GEN);
75
76 using CkaValueType = enum { Attribute_Byte, Attribute_Long, Attribute_String };
77
78 // Attribute handling
79 class P11KitAttribute {
80 private:
81 CK_ATTRIBUTE_TYPE type;
82 CK_BYTE ckByte{0};
83 CK_ULONG ckLong{0};
84 std::string ckString;
85 CkaValueType ckType;
86 std::unique_ptr<unsigned char[]> buffer;
87 CK_ULONG buflen;
88 protected:
89 void Init() {
90 buflen = 0;
91 };
92 public:
93 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, const std::string& value) {
94 Init();
95 this->type = type_;
96 setString(value);
97 }
98
99 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, char value) {
100 Init();
101 this->type = type_;
102 setByte(value);
103 }
104
105 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, unsigned char value) {
106 Init();
107 this->type = type_;
108 setByte(value);
109 }
110
111 P11KitAttribute(CK_ATTRIBUTE_TYPE type_, unsigned long value) {
112 Init();
113 this->type = type_;
114 setLong(value);
115 }
116
117 [[nodiscard]] CkaValueType valueType() const {
118 return ckType;
119 }
120
121 [[nodiscard]] const std::string &str() const {
122 return ckString;
123 };
124
125 [[nodiscard]] unsigned char byte() const {
126 return ckByte;
127 }
128
129 [[nodiscard]] unsigned long ulong() const {
130 return ckLong;
131 }
132
133 void setString(const std::string& value) {
134 this->ckString = value;
135 this->ckType = Attribute_String;
136 }
137
138 void setByte(char value) {
139 this->ckByte = value;
140 this->ckType = Attribute_Byte;
141 }
142
143 void setByte(unsigned char value) {
144 this->ckByte = value;
145 this->ckType = Attribute_Byte;
146 }
147
148 void setLong(unsigned long value) {
149 this->ckLong = value;
150 this->ckType = Attribute_Long;
151 }
152
153 // this bit is used for getting attribute from object
154 // we provide a pointer for GetAttributeValue to write to
155 CK_BYTE_PTR allocate(CK_ULONG amount) {
156 buffer = std::make_unique<unsigned char[]>(amount);
157 buflen = amount;
158 return buffer.get();
159 }
160
161 // and here we copy the results back and delete buffer
162 void commit(CK_ULONG amount) {
163 if (buffer) {
164 this->ckString.assign((char*)buffer.get(), amount);
165 }
166 buffer.reset();
167 buflen = 0;
168 }
169
170 // this is *writable* attribute (you write into it)
171 void wattr(CK_ATTRIBUTE_PTR attr) {
172 attr->type = type;
173 switch(ckType) {
174 case Attribute_Byte: {
175 attr->pValue = (void*)&ckByte;
176 attr->ulValueLen = 1;
177 break;
178 }
179 case Attribute_Long: {
180 attr->pValue = (void*)&ckLong;
181 attr->ulValueLen = sizeof(CK_ULONG);
182 break;
183 }
184 case Attribute_String: {
185 attr->pValue = buffer.get();
186 attr->ulValueLen = buflen;
187 }
188 };
189 };
190
191 // this is *readable* attribute (you read from it)
192 void rattr(CK_ATTRIBUTE_PTR attr) const {
193 attr->type = type;
194 switch(ckType) {
195 case Attribute_Byte: {
196 attr->pValue = (void*)&ckByte;
197 attr->ulValueLen = 1;
198 break;
199 }
200 case Attribute_Long: {
201 attr->pValue = (void*)&ckLong;
202 attr->ulValueLen = sizeof(CK_ULONG);
203 break;
204 }
205 case Attribute_String: {
206 attr->pValue = (void*)ckString.c_str();
207 attr->ulValueLen = ckString.size();
208 }
209 };
210 };
211 };
212
213
214 class Pkcs11Slot {
215 private:
216 bool d_logged_in{};
217 CK_FUNCTION_LIST* d_functions; // module functions
218 CK_SESSION_HANDLE d_session;
219 CK_SLOT_ID d_slot;
220 CK_RV d_err{};
221 std::string d_pin;
222
223 void logError(const std::string& operation) const {
224 if (d_err) {
225 std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X) (%s)") % operation % p11_kit_strerror(d_err) % d_err % p11_kit_message() );
226 g_log<<Logger::Error<< msg << endl;
227 }
228 }
229
230 public:
231 Pkcs11Slot(CK_FUNCTION_LIST* functions, const CK_SLOT_ID& slot) :
232 d_logged_in(false),
233 d_functions(functions),
234 d_slot(slot),
235 d_err(0)
236 {
237 CK_TOKEN_INFO tokenInfo;
238
239 if ((d_err = d_functions->C_OpenSession(this->d_slot, CKF_SERIAL_SESSION|CKF_RW_SESSION, 0, 0, &(this->d_session)))) {
240 logError("C_OpenSession");
241 throw PDNSException("Could not open session");
242 }
243 // check if we need to login
244 if ((d_err = d_functions->C_GetTokenInfo(d_slot, &tokenInfo)) == 0) {
245 d_logged_in = !((tokenInfo.flags & CKF_LOGIN_REQUIRED) == CKF_LOGIN_REQUIRED);
246 } else {
247 logError("C_GetTokenInfo");
248 throw PDNSException("Cannot get token info for slot " + std::to_string(slot));
249 }
250 }
251
252 bool Login(const std::string& pin, CK_USER_TYPE userType=CKU_USER) {
253 if (userType == CKU_USER && d_logged_in) {
254 return true;
255 }
256
257 auto uPin = std::make_unique<unsigned char[]>(pin.size());
258 memcpy(uPin.get(), pin.c_str(), pin.size());
259 d_err = d_functions->C_Login(this->d_session, userType, uPin.get(), pin.size());
260 logError("C_Login");
261
262 if (d_err == 0 && userType == CKU_USER) {
263 d_logged_in = true;
264 d_pin = pin;
265 }
266
267 return d_logged_in;
268 }
269
270 bool Relogin() {
271 return Login(d_pin, CKU_CONTEXT_SPECIFIC);
272 }
273
274 bool LoggedIn() const { return d_logged_in; }
275
276 CK_SESSION_HANDLE& Session() { return d_session; }
277
278 CK_FUNCTION_LIST* f() { return d_functions; }
279
280 static std::shared_ptr<LockGuarded<Pkcs11Slot>> GetSlot(const std::string& module, const string& tokenId);
281 static CK_RV HuntSlot(const string& tokenId, CK_SLOT_ID &slotId, _CK_SLOT_INFO* info, CK_FUNCTION_LIST* functions);
282 };
283
284 class Pkcs11Token {
285 private:
286 std::shared_ptr<LockGuarded<Pkcs11Slot>> d_slot;
287
288 CK_OBJECT_HANDLE d_public_key{0};
289 CK_OBJECT_HANDLE d_private_key{0};
290 CK_KEY_TYPE d_key_type{0};
291 bool d_always_auth{false};
292
293 CK_ULONG d_bits;
294 std::string d_exponent;
295 std::string d_modulus;
296 std::string d_ec_point;
297 std::string d_ecdsa_params;
298
299 std::string d_label;
300 std::string d_pub_label;
301
302 bool d_loaded;
303 CK_RV d_err;
304
305 void logError(const std::string& operation) const {
306 if (d_err) {
307 std::string msg = boost::str( boost::format("PKCS#11 operation %s failed: %s (0x%X) (%s)") % operation % p11_kit_strerror(d_err) % d_err % p11_kit_message());
308 g_log<<Logger::Error<< msg << endl;
309 }
310 }
311
312 [[nodiscard]] unsigned int ecparam2bits(const std::string& obj) const {
313 // if we can use some library to parse the EC parameters, better use it.
314 // otherwise fall back to using hardcoded primev256 and secp384r1
315 #ifdef HAVE_LIBCRYPTO_ECDSA
316 #if OPENSSL_VERSION_MAJOR >= 3
317 using Key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>;
318 #else
319 using Key = std::unique_ptr<EC_KEY, decltype(&EC_KEY_free)>;
320 using BigNum = std::unique_ptr<BIGNUM, decltype(&BN_clear_free)>;
321 #endif
322
323 unsigned int bits = 0;
324
325 // NOLINTNEXTLINE(*-cast): Using OpenSSL C APIs.
326 const auto* objCStr = reinterpret_cast<const unsigned char*>(obj.c_str());
327 #if OPENSSL_VERSION_MAJOR >= 3
328 auto key = Key(d2i_KeyParams(EVP_PKEY_EC, nullptr, &objCStr, static_cast<long>(obj.size())), EVP_PKEY_free);
329 #else
330 auto key = Key(d2i_ECParameters(nullptr, &objCStr, static_cast<long>(obj.size())), EC_KEY_free);
331 #endif
332 if (key == nullptr) {
333 throw pdns::OpenSSL::error("PKCS11", "Cannot parse EC parameters from DER");
334 }
335
336 #if OPENSSL_VERSION_MAJOR >= 3
337 bits = EVP_PKEY_get_bits(key.get());
338 #else
339 const auto* group = EC_KEY_get0_group(key.get());
340 auto order = BigNum(BN_new(), BN_clear_free);
341 if (EC_GROUP_get_order(group, order.get(), nullptr) == 1) {
342 bits = BN_num_bits(order.get());
343 }
344 #endif
345
346 if (bits == 0) {
347 throw PDNSException("Unsupported EC key");
348 }
349
350 return bits;
351 #else
352 if (d_ecdsa_params == ECDSA256_PARAMS) return 256;
353 else if (d_ecdsa_params == ECDSA384_PARAMS) return 384;
354 else throw PDNSException("Unsupported EC key");
355 #endif
356 }
357
358 public:
359 Pkcs11Token(const std::shared_ptr<LockGuarded<Pkcs11Slot>>& slot, const std::string& label, const std::string& pub_label);
360 ~Pkcs11Token();
361
362 bool Login(const std::string& pin) {
363 if (pin.empty()) return false; // no empty pin.
364 if (d_slot->lock()->Login(pin) == true) {
365 LoadAttributes();
366 }
367
368 return LoggedIn();
369 }
370
371 bool LoggedIn() {
372 if (d_loaded == false && d_slot->lock()->LoggedIn() == true) {
373 LoadAttributes();
374 }
375 return d_slot->lock()->LoggedIn();
376 }
377
378 void LoadAttributes() {
379 auto slot = d_slot->lock();
380 std::vector<P11KitAttribute> attr;
381 std::vector<CK_OBJECT_HANDLE> key;
382 attr.emplace_back(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY);
383 attr.emplace_back(CKA_LABEL, d_label);
384 FindObjects2(*slot, attr, key, 1);
385 if (key.size() == 0) {
386 g_log<<Logger::Warning<<"Cannot load PKCS#11 private key "<<d_label<<std::endl;;
387 return;
388 }
389 d_private_key = key[0];
390 attr.clear();
391 attr.emplace_back(CKA_ALWAYS_AUTHENTICATE, '\0');
392 if (GetAttributeValue2(*slot, d_private_key, attr)==0) {
393 d_always_auth = attr[0].byte() != 0;
394 }
395 attr.clear();
396 attr.emplace_back(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY);
397 attr.emplace_back(CKA_LABEL, d_pub_label);
398 FindObjects2(*slot, attr, key, 1);
399 if (key.size() == 0) {
400 g_log<<Logger::Warning<<"Cannot load PKCS#11 public key "<<d_pub_label<<std::endl;
401 return;
402 }
403 d_public_key = key[0];
404
405 attr.clear();
406 attr.emplace_back(CKA_KEY_TYPE, 0UL);
407
408 if (GetAttributeValue2(*slot, d_public_key, attr)==0) {
409 d_key_type = attr[0].ulong();
410 if (d_key_type == CKK_RSA) {
411 attr.clear();
412 attr.emplace_back(CKA_MODULUS, "");
413 attr.emplace_back(CKA_PUBLIC_EXPONENT, "");
414 attr.emplace_back(CKA_MODULUS_BITS, 0UL);
415
416 if (!GetAttributeValue2(*slot, d_public_key, attr)) {
417 d_modulus = attr[0].str();
418 d_exponent = attr[1].str();
419 d_bits = attr[2].ulong();
420 } else {
421 throw PDNSException("Cannot load attributes for PKCS#11 public key " + d_pub_label);
422 }
423 } else if (d_key_type == CKK_EC || d_key_type == CKK_ECDSA) {
424 attr.clear();
425 attr.emplace_back(CKA_ECDSA_PARAMS, "");
426 attr.emplace_back(CKA_EC_POINT, "");
427 if (!GetAttributeValue2(*slot, d_public_key, attr)) {
428 d_ecdsa_params = attr[0].str();
429 d_bits = ecparam2bits(d_ecdsa_params);
430 if (attr[1].str().length() != (d_bits*2/8 + 3)) throw PDNSException("EC Point data invalid");
431 d_ec_point = attr[1].str().substr(3);
432 } else {
433 throw PDNSException("Cannot load attributes for PKCS#11 public key " + d_pub_label);
434 }
435 } else {
436 throw PDNSException("Cannot determine type for PKCS#11 public key " + d_pub_label);
437 }
438 } else {
439 throw PDNSException("Cannot load attributes for PKCS#11 public key " + d_pub_label);
440 }
441
442 d_loaded = true;
443 }
444
445 int GenerateKeyPair(CK_MECHANISM_PTR mechanism, std::vector<P11KitAttribute>& pubAttributes, std::vector<P11KitAttribute>& privAttributes, CK_OBJECT_HANDLE_PTR pubKey, CK_OBJECT_HANDLE_PTR privKey) {
446 {
447 auto slot = d_slot->lock();
448
449 size_t k;
450 auto pubAttr = std::make_unique<CK_ATTRIBUTE[]>(pubAttributes.size());
451 auto privAttr = std::make_unique<CK_ATTRIBUTE[]>(privAttributes.size());
452
453 k = 0;
454 for(P11KitAttribute& attribute : pubAttributes) {
455 attribute.rattr(pubAttr.get()+k);
456 k++;
457 }
458
459 k = 0;
460 for(P11KitAttribute& attribute : privAttributes) {
461 attribute.rattr(privAttr.get()+k);
462 k++;
463 }
464
465 d_err = slot->f()->C_GenerateKeyPair(slot->Session(), mechanism, pubAttr.get(), pubAttributes.size(), privAttr.get(), privAttributes.size(), pubKey, privKey);
466 logError("C_GenerateKeyPair");
467 }
468
469 if (d_err == 0) LoadAttributes();
470
471 return d_err;
472 }
473
474 int Sign(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
475 CK_BYTE buffer[1024];
476 CK_ULONG buflen = sizeof buffer; // should be enough for most signatures.
477 auto slot = d_slot->lock();
478
479 if ((d_err = slot->f()->C_SignInit(slot->Session(), mechanism, d_private_key))) { logError("C_SignInit"); return d_err; }
480 // check if we need to relogin
481 if (d_always_auth) {
482 slot->Relogin();
483 }
484 // perform signature
485 d_err = slot->f()->C_Sign(slot->Session(), (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
486
487 if (!d_err) {
488 result.assign((char*)buffer, buflen);
489 }
490
491 memset(buffer,0,sizeof buffer);
492 logError("C_Sign");
493 return d_err;
494 }
495
496 int Verify(const std::string& data, const std::string& signature, CK_MECHANISM_PTR mechanism) {
497 auto slot = d_slot->lock();
498
499 if ((d_err = slot->f()->C_VerifyInit(slot->Session(), mechanism, d_public_key))) { logError("C_VerifyInit"); return d_err; }
500 // check if we need to relogin
501 if (d_always_auth) {
502 slot->Relogin();
503 }
504
505 d_err = slot->f()->C_Verify(slot->Session(), (unsigned char*)data.c_str(), data.size(), (unsigned char*)signature.c_str(), signature.size());
506 logError("C_Verify");
507 return d_err;
508 }
509
510 int Digest(const std::string& data, std::string& result, CK_MECHANISM_PTR mechanism) {
511 CK_BYTE buffer[1024];
512 CK_ULONG buflen = sizeof buffer; // should be enough for most digests
513
514 auto slot = d_slot->lock();
515 if ((d_err = slot->f()->C_DigestInit(slot->Session(), mechanism))) { logError("C_DigestInit"); return d_err; }
516 d_err = slot->f()->C_Digest(slot->Session(), (unsigned char*)data.c_str(), data.size(), buffer, &buflen);
517 if (!d_err) {
518 result.assign((char*)buffer, buflen);
519 }
520 memset(buffer,0,sizeof buffer);
521 logError("C_Digest");
522 return d_err;
523 }
524
525 int DigestInit(Pkcs11Slot& slot, CK_MECHANISM_PTR mechanism) {
526 d_err = slot.f()->C_DigestInit(slot.Session(), mechanism);
527 logError("C_DigestInit");
528 return d_err;
529 }
530
531 int DigestUpdate(Pkcs11Slot& slot, const std::string& data) {
532 d_err = slot.f()->C_DigestUpdate(slot.Session(), (unsigned char*)data.c_str(), data.size());
533 logError("C_DigestUpdate");
534 return d_err;
535 }
536
537 int DigestFinal(Pkcs11Slot& slot, std::string& result) {
538 CK_BYTE buffer[1024] = {0};
539 CK_ULONG buflen = sizeof buffer; // should be enough for most digests
540
541 d_err = slot.f()->C_DigestFinal(slot.Session(), buffer, &buflen);
542 if (!d_err) {
543 result.assign((char*)buffer, buflen);
544 }
545 memset(buffer,0,sizeof buffer);
546 logError("C_DigestFinal");
547 return d_err;
548 }
549
550 int FindObjects2(Pkcs11Slot& slot, const std::vector<P11KitAttribute>& attributes, std::vector<CK_OBJECT_HANDLE>& objects, int maxobjects) {
551 CK_RV rv;
552 size_t k;
553 unsigned long count;
554
555 auto handles = std::make_unique<CK_OBJECT_HANDLE[]>(maxobjects);
556 auto attr = std::make_unique<CK_ATTRIBUTE[]>(attributes.size());
557
558 k = 0;
559 for(const P11KitAttribute& attribute : attributes) {
560 attribute.rattr(attr.get()+k);
561 k++;
562 }
563
564 // perform search
565 d_err = slot.f()->C_FindObjectsInit(slot.Session(), attr.get(), k);
566
567 if (d_err) {
568 logError("C_FindObjectsInit");
569 return d_err;
570 }
571
572 count = maxobjects;
573 rv = d_err = slot.f()->C_FindObjects(slot.Session(), handles.get(), maxobjects, &count);
574 objects.clear();
575
576 if (!rv) {
577 for(k=0;k<count;k++) {
578 objects.push_back((handles.get())[k]);
579 }
580 }
581
582 logError("C_FindObjects");
583
584 d_err = slot.f()->C_FindObjectsFinal(slot.Session());
585 logError("C_FindObjectsFinal");
586
587 return rv;
588 }
589
590 int GetAttributeValue2(Pkcs11Slot& slot, const CK_OBJECT_HANDLE& object, std::vector<P11KitAttribute>& attributes)
591 {
592 size_t k;
593 auto attr = std::make_unique<CK_ATTRIBUTE[]>(attributes.size());
594
595 k = 0;
596 for(P11KitAttribute &attribute : attributes) {
597 attribute.wattr(attr.get()+k);
598 k++;
599 }
600
601 // round 1 - get attribute sizes
602 d_err = slot.f()->C_GetAttributeValue(slot.Session(), object, attr.get(), attributes.size());
603 logError("C_GetAttributeValue");
604 if (d_err) {
605 return d_err;
606 }
607
608 // then allocate memory
609 for(size_t idx=0; idx < attributes.size(); idx++) {
610 if (attributes[idx].valueType() == Attribute_String) {
611 (attr.get())[idx].pValue = attributes[idx].allocate((attr.get())[idx].ulValueLen);
612 }
613 }
614
615 // round 2 - get actual values
616 d_err = slot.f()->C_GetAttributeValue(slot.Session(), object, attr.get(), attributes.size());
617 logError("C_GetAttributeValue");
618
619 // copy values to map and release allocated memory
620 for(size_t idx=0; idx < attributes.size(); idx++) {
621 if (attributes[idx].valueType() == Attribute_String) {
622 attributes[idx].commit((attr.get())[idx].ulValueLen);
623 }
624 }
625
626 return d_err;
627 };
628
629 const std::string& Modulus() {
630 return d_modulus;
631 }
632
633 const std::string& Exponent() {
634 return d_exponent;
635 }
636
637 const std::string& ECPoint() {
638 return d_ec_point;
639 }
640
641 const std::string& ECParameters() {
642 return d_ecdsa_params;
643 }
644
645 CK_KEY_TYPE KeyType() {
646 return d_key_type;
647 }
648
649 CK_ULONG Bits() {
650 return d_bits;
651 }
652
653 static std::shared_ptr<Pkcs11Token> GetToken(const std::string& module, const string& tokenId, const std::string& label, const std::string& pub_label);
654 };
655
656 static std::map<std::string, std::shared_ptr<LockGuarded<Pkcs11Slot> > > pkcs11_slots;
657 static std::map<std::string, std::shared_ptr<Pkcs11Token> > pkcs11_tokens;
658
659 CK_RV Pkcs11Slot::HuntSlot(const string& tokenId, CK_SLOT_ID &slotId, _CK_SLOT_INFO* info, CK_FUNCTION_LIST* functions)
660 {
661 CK_RV err;
662 unsigned int i;
663 unsigned long slots;
664 _CK_TOKEN_INFO tinfo;
665
666 // go thru all slots
667 // this is required by certain tokens, otherwise C_GetSlotInfo will not return a token
668 err = functions->C_GetSlotList(CK_FALSE, NULL_PTR, &slots);
669 if (err) {
670 g_log<<Logger::Warning<<"C_GetSlotList(CK_FALSE, NULL_PTR, &slots) = " << err << std::endl;
671 return err;
672 }
673
674 // get the actual slot ids
675 std::vector<CK_SLOT_ID> slotIds(slots);
676 err = functions->C_GetSlotList(CK_FALSE, slotIds.data(), &slots);
677 if (err) {
678 g_log<<Logger::Warning<<"C_GetSlotList(CK_FALSE, slotIds, &slots) = " << err << std::endl;
679 return err;
680 }
681
682 // iterate all slots
683 for(i=0;i<slots;i++) {
684 slotId=slotIds[i];
685 if (slotId == static_cast<CK_SLOT_ID>(-1))
686 continue;
687 if ((err = functions->C_GetSlotInfo(slotId, info))) {
688 g_log<<Logger::Warning<<"C_GetSlotList("<<slotId<<", info) = " << err << std::endl;
689 return err;
690 }
691 if ((err = functions->C_GetTokenInfo(slotId, &tinfo))) {
692 g_log<<Logger::Warning<<"C_GetSlotList("<<slotId<<", &tinfo) = " << err << std::endl;
693 return err;
694 }
695 std::string slotName;
696 slotName.assign(reinterpret_cast<char*>(tinfo.label), 32);
697 // trim it
698 boost::trim(slotName);
699
700 if (boost::iequals(slotName, tokenId)) {
701 return 0;
702 }
703 }
704
705 // see if we can find it with slotId
706 try {
707 slotId = std::stoi(tokenId);
708 if ((err = functions->C_GetSlotInfo(slotId, info))) {
709 g_log<<Logger::Warning<<"C_GetSlotList("<<slotId<<", info) = " << err << std::endl;
710 return err;
711 }
712 g_log<<Logger::Warning<<"Specifying PKCS#11 token by SLOT ID is deprecated and should not be used"<<std::endl;
713 return 0;
714 } catch (...) {
715 return CKR_SLOT_ID_INVALID;
716 }
717 return CKR_SLOT_ID_INVALID;
718 }
719
720 std::shared_ptr<LockGuarded<Pkcs11Slot>> Pkcs11Slot::GetSlot(const std::string& module, const string& tokenId) {
721 // see if we can find module
722 std::string sidx = module;
723 sidx.append("|");
724 sidx.append(tokenId);
725 std::map<std::string, std::shared_ptr<LockGuarded<Pkcs11Slot> > >::iterator slotIter;
726 CK_RV err;
727 CK_FUNCTION_LIST* functions;
728
729 // see if we have slot
730 if ((slotIter = pkcs11_slots.find(sidx)) != pkcs11_slots.end()) {
731 return slotIter->second;
732 }
733
734 #ifdef HAVE_P11KIT1_V2
735 functions = p11_kit_module_for_name(p11_modules, module.c_str());
736 #else
737 functions = p11_kit_registered_name_to_module(module.c_str());
738 #endif
739 if (functions == nullptr) throw PDNSException("Cannot find PKCS#11 module " + module);
740 functions->C_Initialize(nullptr); // initialize the module in case it hasn't been done yet.
741
742 // try to locate a slot
743 _CK_SLOT_INFO info;
744 CK_SLOT_ID slotId;
745
746 if ((err = Pkcs11Slot::HuntSlot(tokenId, slotId, &info, functions))) {
747 throw PDNSException(std::string("Cannot find PKCS#11 token ") + tokenId + std::string(" on module ") + module + std::string(": ") + boost::str( boost::format("%s (0x%X)") % p11_kit_strerror(err) % err));
748 }
749
750 // store slot
751 pkcs11_slots[sidx] = std::make_shared<LockGuarded<Pkcs11Slot>>(Pkcs11Slot(functions, slotId));
752
753 return pkcs11_slots[sidx];
754 }
755
756 std::shared_ptr<Pkcs11Token> Pkcs11Token::GetToken(const std::string& module, const string& tokenId, const std::string& label, const std::string& pub_label) {
757 // see if we can find module
758 std::string tidx = module;
759 tidx.append("|");
760 tidx.append(tokenId);
761 tidx.append("|");
762 tidx.append(label);
763 std::map<std::string, std::shared_ptr<Pkcs11Token> >::iterator tokenIter;
764 if ((tokenIter = pkcs11_tokens.find(tidx)) != pkcs11_tokens.end()) return tokenIter->second;
765
766 std::shared_ptr<LockGuarded<Pkcs11Slot>> slot = Pkcs11Slot::GetSlot(module, tokenId);
767 pkcs11_tokens[tidx] = std::make_shared<Pkcs11Token>(slot, label, pub_label);
768 return pkcs11_tokens[tidx];
769 }
770
771 Pkcs11Token::Pkcs11Token(const std::shared_ptr<LockGuarded<Pkcs11Slot>>& slot, const std::string& label, const std::string& pub_label) :
772 d_slot(slot),
773 d_bits(0),
774 d_label(label),
775 d_pub_label(pub_label),
776 d_loaded(false),
777 d_err(0)
778 {
779 // open a session
780 if (this->d_slot->lock()->LoggedIn()) LoadAttributes();
781 }
782
783 Pkcs11Token::~Pkcs11Token() = default;
784
785 bool PKCS11ModuleSlotLogin(const std::string& module, const string& tokenId, const std::string& pin)
786 {
787 std::shared_ptr<LockGuarded<Pkcs11Slot>> slot = Pkcs11Slot::GetSlot(module, tokenId);
788 if (slot->lock()->LoggedIn()) return true; // no point failing
789 return slot->lock()->Login(pin);
790 }
791
792 PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(unsigned int algorithm): DNSCryptoKeyEngine(algorithm) {}
793 PKCS11DNSCryptoKeyEngine::~PKCS11DNSCryptoKeyEngine() = default;
794 PKCS11DNSCryptoKeyEngine::PKCS11DNSCryptoKeyEngine(const PKCS11DNSCryptoKeyEngine& orig) : DNSCryptoKeyEngine(orig.d_algorithm) {}
795
796 void PKCS11DNSCryptoKeyEngine::create(unsigned int bits) {
797 std::vector<P11KitAttribute> pubAttr;
798 std::vector<P11KitAttribute> privAttr;
799 CK_MECHANISM mech;
800 CK_OBJECT_HANDLE pubKey, privKey;
801 std::shared_ptr<Pkcs11Token> d_slot;
802 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
803 if (d_slot->LoggedIn() == false)
804 if (d_slot->Login(d_pin) == false)
805 throw PDNSException("Not logged in to token");
806
807 std::string pubExp("\000\001\000\001", 4); // 65537
808
809 try {
810 mech.mechanism = dnssec2cmech.at(d_algorithm);
811 } catch (std::out_of_range& e) {
812 throw PDNSException("pkcs11: unsupported algorithm "+std::to_string(d_algorithm)+ " for key pair generation");
813 }
814
815 mech.pParameter = nullptr;
816 mech.ulParameterLen = 0;
817
818 if (mech.mechanism == CKM_RSA_PKCS_KEY_PAIR_GEN) {
819 pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
820 pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
821 pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
822 pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
823 pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
824 pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
825 pubAttr.push_back(P11KitAttribute(CKA_MODULUS_BITS, (unsigned long)bits));
826 pubAttr.push_back(P11KitAttribute(CKA_PUBLIC_EXPONENT, pubExp));
827 pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_pub_label));
828
829 privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
830 privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_RSA));
831 privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
832 privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
833 // privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
834 privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
835 privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
836 privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
837 privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
838 privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
839 privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
840 } else if (mech.mechanism == CKM_ECDSA_KEY_PAIR_GEN) {
841 pubAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PUBLIC_KEY));
842 pubAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_ECDSA));
843 pubAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
844 pubAttr.push_back(P11KitAttribute(CKA_ENCRYPT, (char)CK_TRUE));
845 pubAttr.push_back(P11KitAttribute(CKA_VERIFY, (char)CK_TRUE));
846 pubAttr.push_back(P11KitAttribute(CKA_WRAP, (char)CK_TRUE));
847 pubAttr.push_back(P11KitAttribute(CKA_LABEL, d_pub_label));
848 if (d_algorithm == 13) pubAttr.push_back(P11KitAttribute(CKA_ECDSA_PARAMS, ECDSA256_PARAMS));
849 else if (d_algorithm == 14) pubAttr.push_back(P11KitAttribute(CKA_ECDSA_PARAMS, ECDSA384_PARAMS));
850 else throw PDNSException("pkcs11: unknown algorithm "+std::to_string(d_algorithm)+" for ECDSA key pair generation");
851
852 privAttr.push_back(P11KitAttribute(CKA_CLASS, (unsigned long)CKO_PRIVATE_KEY));
853 privAttr.push_back(P11KitAttribute(CKA_KEY_TYPE, (unsigned long)CKK_ECDSA));
854 privAttr.push_back(P11KitAttribute(CKA_TOKEN, (char)CK_TRUE));
855 privAttr.push_back(P11KitAttribute(CKA_PRIVATE, (char)CK_TRUE));
856 // privAttr.push_back(P11KitAttribute(CKA_SUBJECT, "CN=keygen"));
857 privAttr.push_back(P11KitAttribute(CKA_ID, "\x01\x02\x03\x04")); // this is mandatory if you want to export anything
858 privAttr.push_back(P11KitAttribute(CKA_SENSITIVE, (char)CK_TRUE));
859 privAttr.push_back(P11KitAttribute(CKA_DECRYPT, (char)CK_TRUE));
860 privAttr.push_back(P11KitAttribute(CKA_SIGN, (char)CK_TRUE));
861 privAttr.push_back(P11KitAttribute(CKA_UNWRAP, (char)CK_TRUE));
862 privAttr.push_back(P11KitAttribute(CKA_LABEL, d_label));
863 } else {
864 throw PDNSException("pkcs11: don't know how make key for algorithm "+std::to_string(d_algorithm));
865 }
866
867
868 if (d_slot->GenerateKeyPair(&mech, pubAttr, privAttr, &pubKey, &privKey)) {
869 throw PDNSException("Keypair generation failed");
870 }
871 };
872
873 std::string PKCS11DNSCryptoKeyEngine::sign(const std::string& msg) const {
874 std::string result;
875 std::shared_ptr<Pkcs11Token> d_slot;
876 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
877 if (d_slot->LoggedIn() == false)
878 if (d_slot->Login(d_pin) == false)
879 throw PDNSException("Not logged in to token");
880
881 CK_MECHANISM mech;
882 mech.mechanism = dnssec2smech[d_algorithm];
883 mech.pParameter = nullptr;
884 mech.ulParameterLen = 0;
885
886 if (mech.mechanism == CKM_ECDSA) {
887 if (d_slot->Sign(this->hash(msg), result, &mech)) throw PDNSException("Could not sign data");
888 } else {
889 if (d_slot->Sign(msg, result, &mech)) throw PDNSException("Could not sign data");
890 }
891 return result;
892 };
893
894 std::string PKCS11DNSCryptoKeyEngine::hash(const std::string& msg) const {
895 std::string result;
896 CK_MECHANISM mech;
897 mech.mechanism = dnssec2hmech[d_algorithm];
898 mech.pParameter = nullptr;
899 mech.ulParameterLen = 0;
900 std::shared_ptr<Pkcs11Token> d_slot;
901 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
902 if (d_slot->LoggedIn() == false)
903 if (d_slot->Login(d_pin) == false)
904 throw PDNSException("Not logged in to token");
905
906 if (d_slot->Digest(msg, result, &mech)) {
907 g_log<<Logger::Error<<"Could not digest using PKCS#11 token - using software workaround"<<endl;
908 // FINE! I'll do this myself, then, shall I?
909 switch(d_algorithm) {
910 case 5: {
911 return pdns::sha1sum(msg);
912 }
913 case 8: {
914 return pdns::sha256sum(msg);
915 }
916 case 10: {
917 return pdns::sha512sum(msg);
918 }
919 case 13: {
920 return pdns::sha256sum(msg);
921 }
922 case 14: {
923 return pdns::sha384sum(msg);
924 }
925 };
926 };
927 return result;
928 };
929
930 bool PKCS11DNSCryptoKeyEngine::verify(const std::string& msg, const std::string& signature) const {
931 std::shared_ptr<Pkcs11Token> d_slot;
932 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
933 if (d_slot->LoggedIn() == false)
934 if (d_slot->Login(d_pin) == false)
935 throw PDNSException("Not logged in to token");
936
937 CK_MECHANISM mech;
938 mech.mechanism = dnssec2smech[d_algorithm];
939 mech.pParameter = nullptr;
940 mech.ulParameterLen = 0;
941 if (mech.mechanism == CKM_ECDSA) {
942 return (d_slot->Verify(this->hash(msg), signature, &mech)==0);
943 } else {
944 return (d_slot->Verify(msg, signature, &mech) == 0);
945 }
946 };
947
948 std::string PKCS11DNSCryptoKeyEngine::getPublicKeyString() const {
949 std::string result("");
950 std::shared_ptr<Pkcs11Token> d_slot;
951 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
952 if (d_slot->LoggedIn() == false)
953 if (d_slot->Login(d_pin) == false)
954 throw PDNSException("Not logged in to token");
955
956 if (d_slot->KeyType() == CKK_RSA) {
957 if (d_slot->Exponent().length() < 255) {
958 result.assign(1, (char) (unsigned int) d_slot->Exponent().length());
959 } else {
960 result.assign(1, 0);
961 uint16_t len=htons(d_slot->Exponent().length());
962 result.append((char*)&len, 2);
963 }
964 result.append(d_slot->Exponent());
965 result.append(d_slot->Modulus());
966 } else {
967 result.append(d_slot->ECPoint());
968 }
969 return result;
970 };
971
972 int PKCS11DNSCryptoKeyEngine::getBits() const {
973 std::shared_ptr<Pkcs11Token> d_slot;
974 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
975 if (d_slot->LoggedIn() == false)
976 if (d_slot->Login(d_pin) == false)
977 throw PDNSException("Not logged in to token");
978
979 return d_slot->Bits();
980 };
981
982 DNSCryptoKeyEngine::storvector_t PKCS11DNSCryptoKeyEngine::convertToISCVector() const {
983 auto storvect = storvector_t{
984 {"Algorithm", std::to_string(d_algorithm)},
985 {"Engine", d_module},
986 {"Slot", d_slot_id},
987 {"PIN", d_pin},
988 {"Label", d_label},
989 {"PubLabel", d_pub_label},
990 };
991 return storvect;
992 };
993
994 void PKCS11DNSCryptoKeyEngine::fromISCMap(DNSKEYRecordContent& drc, stormap_t& stormap) {
995 pdns::checked_stoi_into(drc.d_algorithm, stormap["algorithm"]);
996 d_module = stormap["engine"];
997 d_slot_id = stormap["slot"];
998 boost::trim(d_slot_id);
999 d_pin = stormap["pin"];
1000 d_label = stormap["label"];
1001 if (stormap.find("publabel") != stormap.end())
1002 d_pub_label = stormap["publabel"];
1003 else
1004 d_pub_label = d_label;
1005 // validate parameters
1006
1007 std::shared_ptr<Pkcs11Token> d_slot;
1008 d_slot = Pkcs11Token::GetToken(d_module, d_slot_id, d_label, d_pub_label);
1009 if (d_pin != "" && d_slot->LoggedIn() == false)
1010 if (d_slot->Login(d_pin) == false)
1011 throw PDNSException("Could not log in to token (PIN wrong?)");
1012 };
1013
1014 std::unique_ptr<DNSCryptoKeyEngine> PKCS11DNSCryptoKeyEngine::maker(unsigned int algorithm)
1015 {
1016 return make_unique<PKCS11DNSCryptoKeyEngine>(algorithm);
1017 }
1018
1019 // this is called during program startup
1020 namespace {
1021 static struct LoaderStruct
1022 {
1023 LoaderStruct()
1024 {
1025 #ifdef HAVE_P11KIT1_V2
1026 p11_modules = p11_kit_modules_load_and_initialize(0);
1027 #else
1028 p11_kit_initialize_registered();
1029 #endif
1030 };
1031 ~LoaderStruct() {
1032 #ifdef HAVE_P11KIT1_V2
1033 p11_kit_modules_release(p11_modules);
1034 #else
1035 p11_kit_finalize_registered();
1036 #endif
1037 };
1038 } loaderPkcs11;
1039 }