Skip to content

Commit 494212a

Browse files
author
Danielle Madeley
committed
Elliptic curve support
1 parent 014e252 commit 494212a

10 files changed

+298
-33
lines changed

README.rst

+32-3
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ Diffie-Hellman
8888

8989
with token.open() as session:
9090
# Given shared Diffie-Hellman parameters
91-
parameters = session.create_object({
92-
Attribute.CLASS: ObjectClass.DOMAIN_PARAMETERS,
93-
Attribute.KEY_TYPE: KeyType.DH,
91+
parameters = session.create_domain_parameters(KeyType.DH, {
9492
Attribute.PRIME: prime, # Diffie-Hellman parameters
9593
Attribute.BASE: base,
9694
})
@@ -109,6 +107,36 @@ Diffie-Hellman
109107
mechanism_param=other_value)
110108

111109

110+
Elliptic-Curve Diffie-Hellman
111+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112+
113+
::
114+
115+
import pkcs11
116+
117+
lib = pkcs11.lib(os.environ['PKCS11_MODULE'])
118+
token = lib.get_token(token_label='DEMO')
119+
120+
with token.open() as session:
121+
# Given DER encocded EC parameters, e.g. from
122+
# openssl ecparam -outform der -name <named curve>
123+
parameters = session.create_domain_parameters(KeyType.EC, {
124+
Attribute.EC_PARAMS: ecparams,
125+
})
126+
127+
# Generate a DH key pair from the public parameters
128+
public, private = parameters.generate_keypair()
129+
130+
# Share the public half of it with our other party.
131+
_network_.write(public[Attribute.EC_POINT])
132+
# And get their shared value
133+
other_value = _network_.read()
134+
135+
# Derive a shared session key
136+
session_key = private.derive_key(
137+
KeyType.AES, 128,
138+
mechanism_param=(KDF.NULL, None, other_value))
139+
112140
Tested Compatibility
113141
--------------------
114142

@@ -134,6 +162,7 @@ Mechanisms:
134162
* AES
135163
* RSA
136164
* Diffie-Hellman
165+
* ECDH
137166

138167
Operations:
139168

pkcs11/_pkcs11.pyx

+42-4
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,16 @@ class Session(types.Session):
187187

188188
return Object._make(self, new)
189189

190+
def create_domain_parameters(self, key_type, attrs, local=False):
191+
attrs = dict(attrs)
192+
attrs[Attribute.CLASS] = ObjectClass.DOMAIN_PARAMETERS
193+
attrs[Attribute.KEY_TYPE] = key_type
194+
195+
if local:
196+
return DomainParameters(self, None, attrs)
197+
else:
198+
return self.create_object(attrs)
199+
190200
def generate_key(self, key_type, key_length,
191201
id=None, label=None,
192202
store=True, capabilities=None,
@@ -453,11 +463,13 @@ class DomainParameters(types.DomainParameters):
453463
Attribute.VERIFY: MechanismFlag.VERIFY & capabilities,
454464
}
455465

456-
# Copy in our domain parameters
466+
# Copy in our domain parameters.
467+
# Not all parameters are appropriate for all domains.
457468
for attribute in (
458469
Attribute.BASE,
459470
Attribute.PRIME,
460471
Attribute.SUBPRIME,
472+
Attribute.EC_PARAMS,
461473
):
462474
try:
463475
public_template_[attribute] = self[attribute]
@@ -849,11 +861,37 @@ class DeriveMixin(types.DeriveMixin):
849861
raise ArgumentsBad("No default capabilities for this key "
850862
"type. Please specify `capabilities`.")
851863

852-
cdef CK_MECHANISM mech = \
853-
_make_CK_MECHANISM(self.key_type, DEFAULT_DERIVE_MECHANISMS,
854-
mechanism, mechanism_param)
864+
cdef CK_MECHANISM mech
865+
cdef CK_ECDH1_DERIVE_PARAMS ecdh1_params
855866
cdef CK_OBJECT_HANDLE key
856867

868+
# Do the mechanism param ourselves so we can handle complex mechanism
869+
# params like ECDH1.
870+
# FIXME: this won't scale, we need a better solution than this!
871+
mech = _make_CK_MECHANISM(self.key_type,
872+
DEFAULT_DERIVE_MECHANISMS,
873+
mechanism, None)
874+
875+
if mech.mechanism == Mechanism.ECDH1_DERIVE:
876+
mech.pParameter = &ecdh1_params
877+
mech.ulParameterLen = sizeof(CK_ECDH1_DERIVE_PARAMS)
878+
879+
(kdf, shared_data, public_data) = mechanism_param
880+
ecdh1_params.kdf = kdf
881+
882+
if shared_data is None:
883+
ecdh1_params.pSharedData = NULL
884+
ecdh1_params.ulSharedDataLen = 0
885+
else:
886+
ecdh1_params.pSharedData = shared_data
887+
ecdh1_params.ulSharedDataLen = len(shared_data)
888+
889+
ecdh1_params.pPublicData = public_data
890+
ecdh1_params.ulPublicDataLen = len(public_data)
891+
else:
892+
mech.pParameter = <CK_CHAR *> mechanism_param
893+
mech.ulParameterLen = len(mechanism_param)
894+
857895
# Build attributes
858896
template_ = {
859897
Attribute.CLASS: ObjectClass.SECRET_KEY,

pkcs11/_pkcs11_defn.pxd

+9-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ cdef extern from '../extern/pkcs11.h':
1010
ctypedef unsigned char CK_CHAR
1111
ctypedef unsigned long int CK_ULONG
1212
ctypedef CK_ULONG CK_ATTRIBUTE_TYPE
13+
ctypedef CK_ULONG CK_EC_KDF_TYPE
1314
ctypedef CK_ULONG CK_FLAGS
1415
ctypedef CK_ULONG CK_MECHANISM_TYPE
15-
ctypedef CK_ULONG CK_OBJECT_HANDLE;
16+
ctypedef CK_ULONG CK_OBJECT_HANDLE
1617
ctypedef CK_ULONG CK_SESSION_HANDLE
1718
ctypedef CK_ULONG CK_SLOT_ID
1819

@@ -203,6 +204,13 @@ cdef extern from '../extern/pkcs11.h':
203204
void *pValue
204205
CK_ULONG ulValueLen
205206

207+
ctypedef struct CK_ECDH1_DERIVE_PARAMS:
208+
CK_EC_KDF_TYPE kdf
209+
CK_ULONG ulSharedDataLen
210+
CK_BYTE *pSharedData
211+
CK_ULONG ulPublicDataLen
212+
CK_BYTE *pPublicData
213+
206214
CK_RV C_Initialize(void *)
207215
CK_RV C_Finalize(void *)
208216
CK_RV C_GetInfo(CK_INFO *info)

pkcs11/_utils.pyx

+12-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ cdef CK_ULONG_buffer(length):
1919

2020

2121
cdef CK_MECHANISM _make_CK_MECHANISM(key_type, default_map,
22-
mechanism=None, param=b'') except *:
22+
mechanism=None, param=None) except *:
2323
"""Build a CK_MECHANISM."""
2424

2525
if mechanism is None:
@@ -34,8 +34,17 @@ cdef CK_MECHANISM _make_CK_MECHANISM(key_type, default_map,
3434

3535
cdef CK_MECHANISM mech
3636
mech.mechanism = mechanism.value
37-
mech.pParameter = <CK_CHAR *> param if param is not None else NULL
38-
mech.ulParameterLen = len(param) if param is not None else 0
37+
38+
if param is None:
39+
mech.pParameter = NULL
40+
mech.ulParameterLen = 0
41+
42+
elif isinstance(param, bytes):
43+
mech.pParameter = <CK_CHAR *> param
44+
mech.ulParameterLen = len(param)
45+
46+
else:
47+
raise ArgumentsBad("Unexpected argument to mechanism_param")
3948

4049
return mech
4150

pkcs11/constants.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,18 @@ class Attribute(IntEnum):
180180
MODIFIABLE = 0x00000170
181181
"""Object can be modified (:class:`bool`)."""
182182

183-
ECDSA_PARAMS = 0x00000180
184183
EC_PARAMS = 0x00000180
184+
"""
185+
DER-encoded ANSI X9.62 Elliptic-Curve domain parameters.
186+
187+
These can be output by OpenSSL.
188+
189+
::
190+
191+
openssl ecparam -outform der -name <curve name> | base64
192+
193+
Or packed using :mod:`pyasn1`.
194+
"""
185195

186196
EC_POINT = 0x00000181
187197

pkcs11/defaults.py

+11-10
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,21 @@
1515
KeyType.AES: Mechanism.AES_KEY_GEN,
1616
KeyType.RSA: Mechanism.RSA_PKCS_KEY_PAIR_GEN,
1717
KeyType.DH: Mechanism.DH_PKCS_KEY_PAIR_GEN,
18+
KeyType.EC: Mechanism.EC_KEY_PAIR_GEN,
1819
}
1920
"""
2021
Default mechanisms for generating keys.
2122
"""
2223

23-
_DEFAULT_CAPS = \
24-
MechanismFlag.ENCRYPT | \
25-
MechanismFlag.DECRYPT | \
26-
MechanismFlag.SIGN | \
27-
MechanismFlag.VERIFY | \
28-
MechanismFlag.WRAP | \
29-
MechanismFlag.UNWRAP
24+
_ENCRYPTION = MechanismFlag.ENCRYPT | MechanismFlag.DECRYPT
25+
_SIGNING = MechanismFlag.SIGN | MechanismFlag.VERIFY
26+
_WRAPPING = MechanismFlag.WRAP | MechanismFlag.UNWRAP
3027

3128
DEFAULT_KEY_CAPABILITIES = {
32-
KeyType.AES: _DEFAULT_CAPS,
33-
KeyType.RSA: _DEFAULT_CAPS,
34-
KeyType.DH: _DEFAULT_CAPS | MechanismFlag.DERIVE,
29+
KeyType.AES: _ENCRYPTION | _SIGNING | _WRAPPING,
30+
KeyType.RSA: _ENCRYPTION | _SIGNING | _WRAPPING,
31+
KeyType.DH: MechanismFlag.DERIVE,
32+
KeyType.EC: _SIGNING | MechanismFlag.DERIVE,
3533
}
3634
"""
3735
Default capabilities for generating keys.
@@ -63,6 +61,7 @@
6361

6462
DEFAULT_DERIVE_MECHANISMS = {
6563
KeyType.DH: Mechanism.DH_PKCS_DERIVE,
64+
KeyType.EC: Mechanism.ECDH1_DERIVE,
6665
}
6766
"""
6867
Default mechanisms for key derivation
@@ -95,6 +94,8 @@ def _enum(type_):
9594
Attribute.CLASS: _enum(ObjectClass),
9695
Attribute.DECRYPT: _bool,
9796
Attribute.DERIVE: _bool,
97+
Attribute.EC_PARAMS: _bytes,
98+
Attribute.EC_POINT: _bytes,
9899
Attribute.ENCRYPT: _bool,
99100
Attribute.EXTRACTABLE: _bool,
100101
Attribute.ID: _bytes,

pkcs11/mechanisms.py

+60-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ class KeyType(IntEnum):
2323
of the PKCS#11 specification for valid :class:`Mechanism` and
2424
:class:`pkcs11.constants.Attribute` types.
2525
"""
26-
ECDSA = 0x00000003
2726
EC = 0x00000003
27+
"""
28+
See the `Elliptic Curve section
29+
<http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/csd01/pkcs11-curr-v2.40-csd01.html#_Toc372721391>`_
30+
of the PKCS#11 specification for valid :class:`Mechanism` and
31+
:class:`pkcs11.constants.Attribute` types.
32+
"""
2833
X9_42_DH = 0x00000004
2934
KEA = 0x00000005
3035
GENERIC_SECRET = 0x00000010
@@ -120,7 +125,22 @@ class Mechanism(IntEnum):
120125
DSA_SHA384 = 0x00000015
121126
DSA_SHA512 = 0x00000016
122127
DH_PKCS_KEY_PAIR_GEN = 0x00000020
128+
"""
129+
Default mechanism for generating :attr:`KeyType.DH` keypairs from
130+
:class:`pkcs11.DomainParameters`.
131+
132+
Requires :class:`pkcs11.DomainParameters` of
133+
:attr:`pkcs11.constants.Attribute.BASE` and
134+
:attr:`pkcs11.constants.Attribute.PRIME`.
135+
"""
123136
DH_PKCS_DERIVE = 0x00000021
137+
"""
138+
Default mechanism for deriving shared keys from :attr:`KeyType.DH` private
139+
keys.
140+
141+
Takes the other participant's public key
142+
:attr:`pkcs11.constants.Attribute.VALUE` as the `mechanism_param`.
143+
"""
124144

125145
X9_42_DH_KEY_PAIR_GEN = 0x00000030
126146
X9_42_DH_DERIVE = 0x00000031
@@ -375,8 +395,14 @@ class Mechanism(IntEnum):
375395
BATON_SHUFFLE = 0x00001035
376396
BATON_WRAP = 0x00001036
377397

378-
ECDSA_KEY_PAIR_GEN = 0x00001040
379398
EC_KEY_PAIR_GEN = 0x00001040
399+
"""
400+
Default mechanism for generating :attr:`KeyType.EC` keypairs from
401+
:class:`pkcs11.DomainParameters`.
402+
403+
Requires :class:`pkcs11.DomainParameters` of
404+
:attr:`pkcs11.constants.Attribute.EC_PARAMS`.
405+
"""
380406

381407
ECDSA = 0x00001041
382408
ECDSA_SHA1 = 0x00001042
@@ -386,6 +412,19 @@ class Mechanism(IntEnum):
386412
ECDSA_SHA512 = 0x00001046
387413

388414
ECDH1_DERIVE = 0x00001050
415+
"""
416+
Default mechanism for deriving shared keys from :attr:`KeyType.EC` private
417+
keys.
418+
419+
Takes a tuple of:
420+
421+
* key derivation function (:class:`pkcs11.mechanisms.KDF`);
422+
* shared value (:class:`bytes`); and
423+
* other participant's :attr:`pkcs11.constants.Attribute.EC_POINT`
424+
(:class:`bytes`)
425+
426+
as the `mechanism_param`.
427+
"""
389428
ECDH1_COFACTOR_DERIVE = 0x00001051
390429
ECMQV_DERIVE = 0x00001052
391430

@@ -464,3 +503,22 @@ class Mechanism(IntEnum):
464503

465504
def __repr__(self):
466505
return '<Mechanism.%s>' % self.name
506+
507+
508+
class KDF(IntEnum):
509+
"""
510+
Key Derivation Functions.
511+
"""
512+
NULL = 0x00000001
513+
SHA1 = 0x00000002
514+
515+
SHA1_ASN1 = 0x00000003
516+
SHA1_CONCATENATE = 0x00000004
517+
SHA224 = 0x00000005
518+
SHA256 = 0x00000006
519+
SHA384 = 0x00000007
520+
SHA512 = 0x00000008
521+
CPDIVERSIFY = 0x00000009
522+
523+
def __repr__(self):
524+
return '<KDF.%s>' % self.name

0 commit comments

Comments
 (0)