Skip to content

Commit fbae631

Browse files
committed
Support for DER and PEM serialisation of EdDSA keys
1 parent 5b248ac commit fbae631

File tree

3 files changed

+299
-5
lines changed

3 files changed

+299
-5
lines changed

src/ecdsa/eddsa.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def __init__(self, generator, private_key):
167167
self.baselen
168168
)
169169
)
170-
self.__private_key = private_key
170+
self.__private_key = bytes(private_key)
171171
self.__h = bytearray(self.curve.hash_func(private_key))
172172
self.__public_key = None
173173

@@ -176,6 +176,10 @@ def __init__(self, generator, private_key):
176176
scalar = bytes_to_int(a, "little")
177177
self.__s = scalar
178178

179+
@property
180+
def private_key(self):
181+
return self.__private_key
182+
179183
def __eq__(self, other):
180184
if isinstance(other, PrivateKey):
181185
return (

src/ecdsa/keys.py

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
from . import der
7979
from . import rfc6979
8080
from . import ellipticcurve
81-
from .curves import NIST192p, Curve
81+
from .curves import NIST192p, Curve, Ed25519, Ed448
8282
from .ecdsa import RSZeroError
8383
from .util import string_to_number, number_to_string, randrange
8484
from .util import sigencode_string, sigdecode_string, bit_length
@@ -437,6 +437,16 @@ def from_der(
437437
s2, point_str_bitstring = der.remove_sequence(s1)
438438
# s2 = oid_ecPublicKey,oid_curve
439439
oid_pk, rest = der.remove_object(s2)
440+
if oid_pk in (Ed25519.oid, Ed448.oid):
441+
if oid_pk == Ed25519.oid:
442+
curve = Ed25519
443+
else:
444+
assert oid_pk == Ed448.oid
445+
curve = Ed448
446+
point_str, empty = der.remove_bitstring(point_str_bitstring, 0)
447+
if empty:
448+
raise der.UnexpectedDER("trailing junk afer public key")
449+
return cls.from_string(point_str, curve, None)
440450
if not oid_pk == oid_ecPublicKey:
441451
raise der.UnexpectedDER(
442452
"Unexpected object identifier in DER "
@@ -647,6 +657,11 @@ def to_der(
647657
if point_encoding == "raw":
648658
raise ValueError("raw point_encoding not allowed in DER")
649659
point_str = self.to_string(point_encoding)
660+
if isinstance(self.curve.curve, CurveEdTw):
661+
return der.encode_sequence(
662+
der.encode_sequence(der.encode_oid(*self.curve.oid)),
663+
der.encode_bitstring(bytes(point_str), 0),
664+
)
650665
return der.encode_sequence(
651666
der.encode_sequence(
652667
encoded_oid_ecPublicKey,
@@ -1057,6 +1072,7 @@ def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None):
10571072
:param valid_curve_encodings: list of allowed encoding formats
10581073
for curve parameters. By default (``None``) all are supported:
10591074
``named_curve`` and ``explicit``.
1075+
Ignored for EdDSA.
10601076
:type valid_curve_encodings: :term:`set-like object`
10611077
10621078
:raises MalformedPointError: if the length of encoding doesn't match
@@ -1092,12 +1108,38 @@ def from_der(cls, string, hashfunc=sha1, valid_curve_encodings=None):
10921108

10931109
sequence, s = der.remove_sequence(s)
10941110
algorithm_oid, algorithm_identifier = der.remove_object(sequence)
1095-
curve = Curve.from_der(algorithm_identifier, valid_curve_encodings)
1111+
1112+
if algorithm_oid in (Ed25519.oid, Ed448.oid):
1113+
if algorithm_identifier:
1114+
raise der.UnexpectedDER(
1115+
"Non NULL parameters for a EdDSA key"
1116+
)
1117+
key_str_der, s = der.remove_octet_string(s)
1118+
if s:
1119+
raise der.UnexpectedDER(
1120+
"trailing junk inside the privateKey"
1121+
)
1122+
key_str, s = der.remove_octet_string(key_str_der)
1123+
if s:
1124+
raise der.UnexpectedDER(
1125+
"trailing junk after the encoded private key"
1126+
)
1127+
1128+
if algorithm_oid == Ed25519.oid:
1129+
curve = Ed25519
1130+
else:
1131+
assert algorithm_oid == Ed448.oid
1132+
curve = Ed448
1133+
1134+
return cls.from_string(key_str, curve, None)
10961135

10971136
if algorithm_oid not in (oid_ecPublicKey, oid_ecDH, oid_ecMQV):
10981137
raise der.UnexpectedDER(
10991138
"unexpected algorithm identifier '%s'" % (algorithm_oid,)
11001139
)
1140+
1141+
curve = Curve.from_der(algorithm_identifier, valid_curve_encodings)
1142+
11011143
if empty != b"":
11021144
raise der.UnexpectedDER(
11031145
"unexpected data after algorithm identifier: %s"
@@ -1166,6 +1208,8 @@ def to_string(self):
11661208
:return: raw encoding of private key
11671209
:rtype: bytes
11681210
"""
1211+
if isinstance(self.curve.curve, CurveEdTw):
1212+
return bytes(self.privkey.private_key)
11691213
secexp = self.privkey.secret_multiplier
11701214
s = number_to_string(secexp, self.privkey.order)
11711215
return s
@@ -1209,6 +1253,15 @@ def to_pem(
12091253
header,
12101254
)
12111255

1256+
def _encode_eddsa(self):
1257+
"""Create a PKCS#8 encoding of EdDSA keys."""
1258+
ec_private_key = der.encode_octet_string(self.to_string())
1259+
return der.encode_sequence(
1260+
der.encode_integer(0),
1261+
der.encode_sequence(der.encode_oid(*self.curve.oid)),
1262+
der.encode_octet_string(ec_private_key),
1263+
)
1264+
12121265
def to_der(
12131266
self,
12141267
point_encoding="uncompressed",
@@ -1224,12 +1277,15 @@ def to_der(
12241277
The public key will be included in the generated string.
12251278
12261279
:param str point_encoding: format to use for encoding public point
1227-
:param str format: either ``ssleay`` (default) or ``pkcs8``
1280+
Ignored for EdDSA
1281+
:param str format: either ``ssleay`` (default) or ``pkcs8``.
1282+
EdDSA keys require ``pkcs8``.
12281283
:param str curve_parameters_encoding: format of encoded curve
12291284
parameters, default depends on the curve, if the curve has
12301285
an associated OID, ``named_curve`` format will be used,
12311286
if no OID is associated with the curve, the fallback of
12321287
``explicit`` parameters will be used.
1288+
Ignored for EdDSA.
12331289
12341290
:return: DER encoded private key
12351291
:rtype: bytes
@@ -1239,6 +1295,10 @@ def to_der(
12391295
if point_encoding == "raw":
12401296
raise ValueError("raw encoding not allowed in DER")
12411297
assert format in ("ssleay", "pkcs8")
1298+
if isinstance(self.curve.curve, CurveEdTw):
1299+
if format != "pkcs8":
1300+
raise ValueError("Only PKCS#8 format supported for EdDSA keys")
1301+
return self._encode_eddsa()
12421302
encoded_vk = self.get_verifying_key().to_string(point_encoding)
12431303
priv_key_elems = [
12441304
der.encode_integer(1),

0 commit comments

Comments
 (0)