Skip to content

Commit df9cbe3

Browse files
author
Danielle Madeley
committed
Clean up encoding of EC_POINT for key loading vs derivation based on the standard
1 parent 079f496 commit df9cbe3

File tree

4 files changed

+89
-12
lines changed

4 files changed

+89
-12
lines changed

README.rst

+4-3
Original file line numberDiff line numberDiff line change
@@ -306,10 +306,11 @@ Python version:
306306
* 3.5 (with `aenum`)
307307
* 3.6
308308

309-
PKCS#11 version:
309+
PKCS#11 versions:
310310

311-
* 2.2
312-
* 2.4
311+
* 2.11
312+
* 2.20
313+
* 2.40
313314

314315
Feel free to send pull requests for any functionality that's not exposed. The
315316
code is designed to be readable and expose the PKCS #11 spec in a

docs/opensc.rst

+65-4
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ exporting keys.
9595
RSA
9696
~~~
9797

98-
PyCrypto example:
98+
`PyCrypto` example:
9999

100100
::
101101

@@ -123,7 +123,7 @@ PyCrypto example:
123123
ECDSA
124124
~~~~~
125125

126-
oscrypto example:
126+
`oscrypto` example:
127127

128128
::
129129

@@ -147,12 +147,73 @@ oscrypto example:
147147
key = load_public_key(encode_ec_public_key(pub))
148148
ecdsa_verify(key, signature, b'Data to sign', 'sha1')
149149

150+
ECDH
151+
~~~~
152+
153+
Smartcard-HSM can generate a shared key via ECDH key exchange.
154+
155+
.. warning::
156+
157+
Where possible, e.g. over networks, you should use ephemeral keys,
158+
to allow for perfect forward secrecy. Smartcard HSM's ECDH is only useful
159+
when need to repeatedly retrieve the same shared secret, e.g. encrypting
160+
files in a hybrid cryptosystem.
161+
162+
`cryptography` example:
163+
164+
::
165+
166+
from cryptography.hazmat.backends import default_backend
167+
from cryptography.hazmat.primitives.asymmetric import ec
168+
from cryptography.hazmat.primitives.serialization import \
169+
Encoding, PublicFormat, load_der_public_key
170+
171+
# Retrieve our keypair, with our public key encoded for interchange
172+
alice_priv = self.session.get_key(key_type=KeyType.EC,
173+
object_class=ObjectClass.PRIVATE_KEY)
174+
alice_pub = self.session.get_key(key_type=KeyType.EC,
175+
object_class=ObjectClass.PUBLIC_KEY)
176+
alice_pub = encode_ec_public_key(alice_pub)
177+
178+
# Bob generates a keypair, with their public key encoded for
179+
# interchange
180+
bob_priv = ec.generate_private_key(ec.SECP256R1,
181+
default_backend())
182+
bob_pub = bob_priv.public_key().public_bytes(
183+
Encoding.DER,
184+
PublicFormat.SubjectPublicKeyInfo,
185+
)
186+
187+
# Bob converts Alice's key to internal format and generates their
188+
# shared key
189+
bob_shared_key = bob_priv.exchange(
190+
ec.ECDH(),
191+
load_der_public_key(alice_pub, default_backend()),
192+
)
193+
194+
key = alice_priv.derive_key(
195+
KeyType.GENERIC_SECRET, 256,
196+
mechanism_param=(
197+
KDF.NULL, None,
198+
# SmartcardHSM doesn't accept DER-encoded EC_POINTs for derivation
199+
decode_ec_public_key(bob_pub, encode_ec_point=False)
200+
[Attribute.EC_POINT],
201+
),
202+
)
203+
alice_shared_key = key[Attribute.VALUE]
204+
205+
When decoding the other user's `EC_POINT` for passing into the key derivation
206+
the standard says to pass a raw octet string (set `encode_ec_point` to False),
207+
however some PKCS #11 implementations require a DER-encoded octet string
208+
(i.e. the format of the :attr:`pkcs11.constants.Attribute.EC_POINT` attribute).
209+
150210
Encrypting Files
151211
----------------
152212

153213
The device only supports asymmetric mechanisms. To do file encryption, you
154-
will need to generate AES keys locally, which you can encrypt with the
155-
public key (this is how the Nitrokey storage key works).
214+
will need to generate AES keys locally, which you can encrypt with your RSA
215+
public key (this is how the Nitrokey storage key works); or by using ECDH
216+
to generate a shared secret from a locally generated public key.
156217

157218
Debugging
158219
---------

pkcs11/util/ec.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -35,21 +35,32 @@ def encode_named_curve_parameters(oid):
3535
return encoder.encode(ecParams)
3636

3737

38-
def decode_ec_public_key(der):
38+
def decode_ec_public_key(der, encode_ec_point=True):
3939
"""
4040
Decode a DER-encoded EC public key as stored by OpenSSL into a dictionary
4141
of attributes able to be passed to :meth:`pkcs11.Session.create_object`.
4242
43+
.. note:: encode_ec_point
44+
45+
For use as an attribute `EC_POINT` should be DER-encoded (True).
46+
47+
For key derivation implementations can vary. Since v2.30 the
48+
specification says implementations MUST accept a raw `EC_POINT` for
49+
ECDH (False), however not all implementations follow this yet.
50+
4351
:param bytes der: DER-encoded key
52+
:param encode_ec_point: See text.
4453
:rtype: dict(Attribute,*)
4554
"""
4655
asn1, _ = decoder.decode(der, asn1Spec=rfc3280.SubjectPublicKeyInfo())
4756

4857
assert asn1['algorithm']['algorithm'] == id_ecPublicKey, \
4958
"Wrong algorithm, not an EC key!"
5059

51-
ecpoint = \
52-
encoder.encode(OctetString(value=asn1['subjectPublicKey'].asOctets()))
60+
ecpoint = asn1['subjectPublicKey'].asOctets()
61+
62+
if encode_ec_point:
63+
ecpoint = encoder.encode(OctetString(value=ecpoint))
5364

5465
return {
5566
Attribute.KEY_TYPE: KeyType.EC,
@@ -63,6 +74,7 @@ def encode_ec_public_key(key):
6374
"""
6475
Encode a DER-encoded EC public key as stored by OpenSSL.
6576
77+
6678
:param PublicKey key: RSA public key
6779
:rtype: bytes
6880
"""

tests/test_public_key_external.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
encode_named_curve_parameters,
88
)
99

10-
from . import TestCase, requires
10+
from . import TestCase, requires, Is
1111

1212

1313
class ExternalPublicKeyTests(TestCase):
@@ -101,7 +101,10 @@ def test_ecdh(self):
101101
KeyType.GENERIC_SECRET, 256,
102102
mechanism_param=(
103103
KDF.NULL, None,
104-
decode_ec_public_key(bob_pub)[Attribute.EC_POINT],
104+
# N.B. it seems like SoftHSMv2 requires an EC_POINT to be
105+
# DER-encoded, which is not what the spec says
106+
decode_ec_public_key(bob_pub, encode_ec_point=Is.softhsm2)
107+
[Attribute.EC_POINT],
105108
),
106109
template={
107110
Attribute.SENSITIVE: False,

0 commit comments

Comments
 (0)