2 * Copyright (C) 2010 Martin Willi
3 * Copyright (C) 2010 revosec AG
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "pkcs11_private_key.h"
18 #include "pkcs11_library.h"
19 #include "pkcs11_manager.h"
22 #include <threading/mutex.h>
24 typedef struct private_pkcs11_private_key_t private_pkcs11_private_key_t
;
27 * Private data of an pkcs11_private_key_t object.
29 struct private_pkcs11_private_key_t
{
32 * Public pkcs11_private_key_t interface.
34 pkcs11_private_key_t
public;
39 pkcs11_library_t
*lib
;
44 CK_SESSION_HANDLE session
;
47 * Mutex to lock session
52 * Key object on the token
54 CK_OBJECT_HANDLE object
;
57 * Key requires reauthentication for each signature/decryption
62 * Keyid of the key we use
64 identification_t
*keyid
;
67 * Associated public key
72 * References to this key
77 METHOD(private_key_t
, get_type
, key_type_t
,
78 private_pkcs11_private_key_t
*this)
80 return this->pubkey
->get_type(this->pubkey
);
83 METHOD(private_key_t
, get_keysize
, int,
84 private_pkcs11_private_key_t
*this)
86 return this->pubkey
->get_keysize(this->pubkey
);
92 CK_MECHANISM_PTR
pkcs11_signature_scheme_to_mech(signature_scheme_t scheme
)
95 signature_scheme_t scheme
;
96 CK_MECHANISM mechanism
;
98 {SIGN_RSA_EMSA_PKCS1_NULL
, {CKM_RSA_PKCS
, NULL
, 0}},
99 {SIGN_RSA_EMSA_PKCS1_SHA1
, {CKM_SHA1_RSA_PKCS
, NULL
, 0}},
100 {SIGN_RSA_EMSA_PKCS1_SHA256
, {CKM_SHA256_RSA_PKCS
, NULL
, 0}},
101 {SIGN_RSA_EMSA_PKCS1_SHA384
, {CKM_SHA384_RSA_PKCS
, NULL
, 0}},
102 {SIGN_RSA_EMSA_PKCS1_SHA512
, {CKM_SHA512_RSA_PKCS
, NULL
, 0}},
103 {SIGN_RSA_EMSA_PKCS1_MD5
, {CKM_MD5_RSA_PKCS
, NULL
, 0}},
107 for (i
= 0; i
< countof(mappings
); i
++)
109 if (mappings
[i
].scheme
== scheme
)
111 return &mappings
[i
].mechanism
;
120 CK_MECHANISM_PTR
pkcs11_encryption_scheme_to_mech(encryption_scheme_t scheme
)
123 encryption_scheme_t scheme
;
124 CK_MECHANISM mechanism
;
126 {ENCRYPT_RSA_PKCS1
, {CKM_RSA_PKCS
, NULL
, 0}},
127 {ENCRYPT_RSA_OAEP_SHA1
, {CKM_RSA_PKCS_OAEP
, NULL
, 0}},
131 for (i
= 0; i
< countof(mappings
); i
++)
133 if (mappings
[i
].scheme
== scheme
)
135 return &mappings
[i
].mechanism
;
142 * Reauthenticate to do a signature
144 static bool reauth(private_pkcs11_private_key_t
*this)
146 enumerator_t
*enumerator
;
147 shared_key_t
*shared
;
150 bool found
= FALSE
, success
= FALSE
;
152 enumerator
= lib
->credmgr
->create_shared_enumerator(lib
->credmgr
,
153 SHARED_PIN
, this->keyid
, NULL
);
154 while (enumerator
->enumerate(enumerator
, &shared
, NULL
, NULL
))
157 pin
= shared
->get_key(shared
);
158 rv
= this->lib
->f
->C_Login(this->session
, CKU_CONTEXT_SPECIFIC
,
165 DBG1(DBG_CFG
, "reauthentication login failed: %N", ck_rv_names
, rv
);
167 enumerator
->destroy(enumerator
);
171 DBG1(DBG_CFG
, "private key requires reauthentication, but no PIN found");
177 METHOD(private_key_t
, sign
, bool,
178 private_pkcs11_private_key_t
*this, signature_scheme_t scheme
,
179 chunk_t data
, chunk_t
*signature
)
181 CK_MECHANISM_PTR mechanism
;
186 mechanism
= pkcs11_signature_scheme_to_mech(scheme
);
189 DBG1(DBG_LIB
, "signature scheme %N not supported",
190 signature_scheme_names
, scheme
);
193 this->mutex
->lock(this->mutex
);
194 rv
= this->lib
->f
->C_SignInit(this->session
, mechanism
, this->object
);
195 if (this->reauth
&& !reauth(this))
201 this->mutex
->unlock(this->mutex
);
202 DBG1(DBG_LIB
, "C_SignInit() failed: %N", ck_rv_names
, rv
);
205 len
= (get_keysize(this) + 7) / 8;
207 rv
= this->lib
->f
->C_Sign(this->session
, data
.ptr
, data
.len
, buf
, &len
);
208 this->mutex
->unlock(this->mutex
);
211 DBG1(DBG_LIB
, "C_Sign() failed: %N", ck_rv_names
, rv
);
215 *signature
= chunk_create(buf
, len
);
219 METHOD(private_key_t
, decrypt
, bool,
220 private_pkcs11_private_key_t
*this, encryption_scheme_t scheme
,
221 chunk_t crypt
, chunk_t
*plain
)
223 CK_MECHANISM_PTR mechanism
;
228 mechanism
= pkcs11_encryption_scheme_to_mech(scheme
);
231 DBG1(DBG_LIB
, "encryption scheme %N not supported",
232 encryption_scheme_names
, scheme
);
235 this->mutex
->lock(this->mutex
);
236 rv
= this->lib
->f
->C_DecryptInit(this->session
, mechanism
, this->object
);
237 if (this->reauth
&& !reauth(this))
243 this->mutex
->unlock(this->mutex
);
244 DBG1(DBG_LIB
, "C_DecryptInit() failed: %N", ck_rv_names
, rv
);
247 len
= (get_keysize(this) + 7) / 8;
249 rv
= this->lib
->f
->C_Decrypt(this->session
, crypt
.ptr
, crypt
.len
, buf
, &len
);
250 this->mutex
->unlock(this->mutex
);
253 DBG1(DBG_LIB
, "C_Decrypt() failed: %N", ck_rv_names
, rv
);
257 *plain
= chunk_create(buf
, len
);
261 METHOD(private_key_t
, get_public_key
, public_key_t
*,
262 private_pkcs11_private_key_t
*this)
264 return this->pubkey
->get_ref(this->pubkey
);
267 METHOD(private_key_t
, get_fingerprint
, bool,
268 private_pkcs11_private_key_t
*this, cred_encoding_type_t type
,
269 chunk_t
*fingerprint
)
271 return this->pubkey
->get_fingerprint(this->pubkey
, type
, fingerprint
);
274 METHOD(private_key_t
, get_encoding
, bool,
275 private_pkcs11_private_key_t
*this, cred_encoding_type_t type
,
281 METHOD(private_key_t
, get_ref
, private_key_t
*,
282 private_pkcs11_private_key_t
*this)
285 return &this->public.key
;
288 METHOD(private_key_t
, destroy
, void,
289 private_pkcs11_private_key_t
*this)
291 if (ref_put(&this->ref
))
295 this->pubkey
->destroy(this->pubkey
);
297 this->mutex
->destroy(this->mutex
);
298 this->keyid
->destroy(this->keyid
);
299 this->lib
->f
->C_CloseSession(this->session
);
305 * Find the PKCS#11 library by its friendly name
307 static pkcs11_library_t
* find_lib(char *module
)
309 pkcs11_manager_t
*manager
;
310 enumerator_t
*enumerator
;
311 pkcs11_library_t
*p11
, *found
= NULL
;
314 manager
= lib
->get(lib
, "pkcs11-manager");
319 enumerator
= manager
->create_token_enumerator(manager
);
320 while (enumerator
->enumerate(enumerator
, &p11
, &slot
))
322 if (streq(module
, p11
->get_name(p11
)))
328 enumerator
->destroy(enumerator
);
333 * Find the PKCS#11 lib having a keyid, and optionally a slot
335 static pkcs11_library_t
* find_lib_by_keyid(chunk_t keyid
, int *slot
)
337 pkcs11_manager_t
*manager
;
338 enumerator_t
*enumerator
;
339 pkcs11_library_t
*p11
, *found
= NULL
;
342 manager
= lib
->get(lib
, "pkcs11-manager");
347 enumerator
= manager
->create_token_enumerator(manager
);
348 while (enumerator
->enumerate(enumerator
, &p11
, ¤t
))
350 if (*slot
== -1 || *slot
== current
)
352 /* we look for a public key, it is usually readable without login */
353 CK_OBJECT_CLASS
class = CKO_PUBLIC_KEY
;
354 CK_ATTRIBUTE tmpl
[] = {
355 {CKA_CLASS
, &class, sizeof(class)},
356 {CKA_ID
, keyid
.ptr
, keyid
.len
},
358 CK_OBJECT_HANDLE object
;
359 CK_SESSION_HANDLE session
;
363 rv
= p11
->f
->C_OpenSession(current
, CKF_SERIAL_SESSION
, NULL
, NULL
,
367 DBG1(DBG_CFG
, "opening PKCS#11 session failed: %N",
371 keys
= p11
->create_object_enumerator(p11
, session
,
372 tmpl
, countof(tmpl
), NULL
, 0);
373 if (keys
->enumerate(keys
, &object
))
375 DBG1(DBG_CFG
, "found key on PKCS#11 token '%s':%d",
376 p11
->get_name(p11
), current
);
381 p11
->f
->C_CloseSession(session
);
388 enumerator
->destroy(enumerator
);
393 * Find the key on the token
395 static bool find_key(private_pkcs11_private_key_t
*this, chunk_t keyid
)
397 CK_OBJECT_CLASS
class = CKO_PRIVATE_KEY
;
398 CK_ATTRIBUTE tmpl
[] = {
399 {CKA_CLASS
, &class, sizeof(class)},
400 {CKA_ID
, keyid
.ptr
, keyid
.len
},
402 CK_OBJECT_HANDLE object
;
404 CK_BBOOL reauth
= FALSE
;
405 CK_ATTRIBUTE attr
[] = {
406 {CKA_KEY_TYPE
, &type
, sizeof(type
)},
407 {CKA_MODULUS
, NULL
, 0},
408 {CKA_PUBLIC_EXPONENT
, NULL
, 0},
409 {CKA_ALWAYS_AUTHENTICATE
, &reauth
, sizeof(reauth
)},
411 enumerator_t
*enumerator
;
412 chunk_t modulus
, pubexp
;
413 int count
= countof(attr
);
415 /* do not use CKA_ALWAYS_AUTHENTICATE if not supported */
416 if (!(this->lib
->get_features(this->lib
) & PKCS11_ALWAYS_AUTH_KEYS
))
420 enumerator
= this->lib
->create_object_enumerator(this->lib
,
421 this->session
, tmpl
, countof(tmpl
), attr
, count
);
422 if (enumerator
->enumerate(enumerator
, &object
))
427 if (attr
[1].ulValueLen
== -1 || attr
[2].ulValueLen
== -1)
429 DBG1(DBG_CFG
, "reading modulus/exponent from PKCS#1 failed");
432 modulus
= chunk_create(attr
[1].pValue
, attr
[1].ulValueLen
);
433 pubexp
= chunk_create(attr
[2].pValue
, attr
[2].ulValueLen
);
434 this->pubkey
= lib
->creds
->create(lib
->creds
, CRED_PUBLIC_KEY
,
435 KEY_RSA
, BUILD_RSA_MODULUS
, modulus
,
436 BUILD_RSA_PUB_EXP
, pubexp
, BUILD_END
);
439 DBG1(DBG_CFG
, "extracting public key from PKCS#11 RSA "
440 "private key failed");
442 this->reauth
= reauth
;
443 this->object
= object
;
446 DBG1(DBG_CFG
, "PKCS#11 key type %d not supported", type
);
450 enumerator
->destroy(enumerator
);
451 return this->pubkey
!= NULL
;
455 * Find a PIN and try to log in
457 static bool login(private_pkcs11_private_key_t
*this, int slot
)
459 enumerator_t
*enumerator
;
460 shared_key_t
*shared
;
463 CK_SESSION_INFO info
;
464 bool found
= FALSE
, success
= FALSE
;
466 rv
= this->lib
->f
->C_GetSessionInfo(this->session
, &info
);
469 DBG1(DBG_CFG
, "C_GetSessionInfo failed: %N", ck_rv_names
, rv
);
472 if (info
.state
!= CKS_RO_PUBLIC_SESSION
&&
473 info
.state
!= CKS_RW_PUBLIC_SESSION
)
474 { /* already logged in with another session, skip */
478 enumerator
= lib
->credmgr
->create_shared_enumerator(lib
->credmgr
,
479 SHARED_PIN
, this->keyid
, NULL
);
480 while (enumerator
->enumerate(enumerator
, &shared
, NULL
, NULL
))
483 pin
= shared
->get_key(shared
);
484 rv
= this->lib
->f
->C_Login(this->session
, CKU_USER
, pin
.ptr
, pin
.len
);
490 DBG1(DBG_CFG
, "login to '%s':%d failed: %N",
491 this->lib
->get_name(this->lib
), slot
, ck_rv_names
, rv
);
493 enumerator
->destroy(enumerator
);
497 DBG1(DBG_CFG
, "no PIN found for PKCS#11 key %Y", this->keyid
);
506 pkcs11_private_key_t
*pkcs11_private_key_connect(key_type_t type
, va_list args
)
508 private_pkcs11_private_key_t
*this;
510 chunk_t keyid
= chunk_empty
;
516 switch (va_arg(args
, builder_part_t
))
518 case BUILD_PKCS11_KEYID
:
519 keyid
= va_arg(args
, chunk_t
);
521 case BUILD_PKCS11_SLOT
:
522 slot
= va_arg(args
, int);
524 case BUILD_PKCS11_MODULE
:
525 module
= va_arg(args
, char*);
542 .get_type
= _get_type
,
545 .get_keysize
= _get_keysize
,
546 .get_public_key
= _get_public_key
,
547 .equals
= private_key_equals
,
548 .belongs_to
= private_key_belongs_to
,
549 .get_fingerprint
= _get_fingerprint
,
550 .has_fingerprint
= private_key_has_fingerprint
,
551 .get_encoding
= _get_encoding
,
559 if (module
&& slot
!= -1)
561 this->lib
= find_lib(module
);
564 DBG1(DBG_CFG
, "PKCS#11 module '%s' not found", module
);
571 this->lib
= find_lib_by_keyid(keyid
, &slot
);
574 DBG1(DBG_CFG
, "no PKCS#11 module found having a keyid %#B", &keyid
);
580 rv
= this->lib
->f
->C_OpenSession(slot
, CKF_SERIAL_SESSION
,
581 NULL
, NULL
, &this->session
);
584 DBG1(DBG_CFG
, "opening private key session on '%s':%d failed: %N",
585 module
, slot
, ck_rv_names
, rv
);
590 this->mutex
= mutex_create(MUTEX_TYPE_DEFAULT
);
591 this->keyid
= identification_create_from_encoding(ID_KEY_ID
, keyid
);
593 if (!login(this, slot
))
599 if (!find_key(this, keyid
))
605 return &this->public;