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