Skip to content

Commit 872332a

Browse files
committed
Update from_pem and from_der according to review
1 parent 9d893d6 commit 872332a

File tree

2 files changed

+57
-51
lines changed

2 files changed

+57
-51
lines changed

src/ecdsa/der.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ def encode_number(n):
125125
return b("").join([int2byte(d) for d in b128_digits])
126126

127127

128+
def is_sequence(string):
129+
return string and string[:1] == b"\x30"
130+
131+
128132
def remove_constructed(string):
129133
s0 = str_idx_as_int(string, 0)
130134
if (s0 & 0xE0) != 0xA0:

src/ecdsa/keys.py

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -848,8 +848,8 @@ def from_pem(cls, string, hashfunc=sha1):
848848
Initialise from key stored in :term:`PEM` format.
849849
850850
The PEM formats supported are the un-encrypted RFC5915
851-
(the sslay format) supported by OpenSSL, and the more common RFC5958
852-
(the PKCS #8 format).
851+
(the sslay format) supported by OpenSSL, and the more common
852+
un-encrypted RFC5958 (the PKCS #8 format).
853853
854854
The legacy format files have the header with the string
855855
``BEGIN EC PRIVATE KEY``.
@@ -876,30 +876,23 @@ def from_pem(cls, string, hashfunc=sha1):
876876
if not PY2 and isinstance(string, str):
877877
string = string.encode()
878878

879-
# the privkey pem may have multiple sections, commonly it also has
880-
# "EC PARAMETERS", we need just "EC PRIVATE KEY".
881-
ec_private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----")
882-
if ec_private_key_index != -1:
883-
return cls.from_der(
884-
der.unpem(string[ec_private_key_index:]), hashfunc, pkcs8=False
885-
)
886-
887-
private_key_index = string.find(b"-----BEGIN PRIVATE KEY-----")
888-
if private_key_index != -1:
889-
return cls.from_der(
890-
der.unpem(string[private_key_index:]), hashfunc, pkcs8=True
891-
)
879+
# The privkey pem may have multiple sections, commonly it also has
880+
# "EC PARAMETERS", we need just "EC PRIVATE KEY". PKCS#8 should not
881+
# have the "EC PARAMETERS" section; it's just "PRIVATE KEY".
882+
private_key_index = string.find(b"-----BEGIN EC PRIVATE KEY-----")
883+
if private_key_index == -1:
884+
private_key_index = string.index(b"-----BEGIN PRIVATE KEY-----")
892885

893-
raise ValueError("No EC PRIVATE KEY or PRIVATE KEY section in PEM")
886+
return cls.from_der(der.unpem(string[private_key_index:]), hashfunc)
894887

895888
@classmethod
896-
def from_der(cls, string, hashfunc=sha1, pkcs8=False):
889+
def from_der(cls, string, hashfunc=sha1):
897890
"""
898891
Initialise from key stored in :term:`DER` format.
899892
900893
The DER formats supported are the un-encrypted RFC5915
901-
(the sslay format) supported by OpenSSL, and the more common RFC5958
902-
(the PKCS #8 format).
894+
(the sslay format) supported by OpenSSL, and the more common
895+
un-encrypted RFC5958 (the PKCS #8 format).
903896
904897
Both formats contain an ASN.1 object following the syntax specified
905898
in RFC5915::
@@ -911,10 +904,14 @@ def from_der(cls, string, hashfunc=sha1, pkcs8=False):
911904
publicKey [1] BIT STRING OPTIONAL
912905
}
913906
907+
`publicKey` field is ignored completely (errors, if any, in it will
908+
be undetected).
909+
914910
The only format supported for the `parameters` field is the named
915911
curve method. Explicit encoding of curve parameters is not supported.
916912
In the legacy sslay format, this implementation requires the optional
917-
`parameters` field to get the curve name.
913+
`parameters` field to get the curve name. In PKCS #8 format, the curve
914+
is part of the PrivateKeyAlgorithmIdentifier.
918915
919916
The PKCS #8 format includes this object as the `privateKey` field
920917
within a larger structure:
@@ -929,15 +926,12 @@ def from_der(cls, string, hashfunc=sha1, pkcs8=False):
929926
...
930927
}
931928
932-
`publicKey` field is ignored completely (errors, if any, in it will
933-
be undetected).
929+
The `attributes` and `publicKey` fields are completely ignored; errors
930+
in them will not be detected.
934931
935932
:param string: binary string with DER-encoded private ECDSA key
936933
:type string: bytes like object
937934
938-
:param pkcs8: whether to expect the data in PKCS #8 format
939-
:type pkcs8: boolean
940-
941935
:raises MalformedPointError: if the length of encoding doesn't match
942936
the provided curve or the encoded values is too large
943937
:raises RuntimeError: if the generation of public key from private
@@ -950,28 +944,27 @@ def from_der(cls, string, hashfunc=sha1, pkcs8=False):
950944
s = normalise_bytes(string)
951945
curve = None
952946

953-
# PKCS #8 has the algorithm identifier, including the curve name,
954-
# before the actual key. Then it contains the key data within an
955-
# octet string.
956-
if pkcs8:
957-
s, empty = der.remove_sequence(s)
958-
if empty != b(""):
959-
raise der.UnexpectedDER(
960-
"trailing junk after DER privkey: %s"
961-
% binascii.hexlify(empty)
962-
)
947+
s, empty = der.remove_sequence(s)
948+
if empty != b(""):
949+
raise der.UnexpectedDER(
950+
"trailing junk after DER privkey: %s" % binascii.hexlify(empty)
951+
)
963952

964-
version, s = der.remove_integer(s)
965-
if version != 0 and version != 1:
953+
version, s = der.remove_integer(s)
954+
955+
# At this point, PKCS #8 has a sequence containing the algorithm
956+
# identifier and the curve identifier. The sslay format instead has
957+
# an octet string containing the key data, so this is how we can
958+
# distinguish the two formats.
959+
if der.is_sequence(s):
960+
if version not in (0, 1):
966961
raise der.UnexpectedDER(
967-
"expected '0' or '1' at start of DER privkey, got %d"
962+
"expected version '0' or '1' at start of privkey, got %d"
968963
% version
969964
)
970965

971-
algorithm_identifier, s = der.remove_sequence(s)
972-
algorithm_oid, algorithm_identifier = der.remove_object(
973-
algorithm_identifier
974-
)
966+
sequence, s = der.remove_sequence(s)
967+
algorithm_oid, algorithm_identifier = der.remove_object(sequence)
975968
curve_oid, empty = der.remove_object(algorithm_identifier)
976969
curve = find_curve(curve_oid)
977970

@@ -985,19 +978,28 @@ def from_der(cls, string, hashfunc=sha1, pkcs8=False):
985978
% binascii.hexlify(empty)
986979
)
987980

988-
# We don't care about the optional fields after the key data.
981+
# Up next is an octet string containing an ECPrivateKey. Ignore
982+
# the optional "attributes" and "publicKey" fields that come after.
989983
s, _ = der.remove_octet_string(s)
990984

991-
s, empty = der.remove_sequence(s)
992-
if empty != b(""):
993-
raise der.UnexpectedDER(
994-
"trailing junk after DER privkey: %s" % binascii.hexlify(empty)
995-
)
996-
one, s = der.remove_integer(s)
997-
if one != 1:
985+
# Unpack the ECPrivateKey to get to the key data octet string,
986+
# and rejoin the sslay parsing path.
987+
s, empty = der.remove_sequence(s)
988+
if empty != b(""):
989+
raise der.UnexpectedDER(
990+
"trailing junk after DER privkey: %s"
991+
% binascii.hexlify(empty)
992+
)
993+
994+
version, s = der.remove_integer(s)
995+
996+
# The version of the ECPrivateKey must be 1.
997+
if version != 1:
998998
raise der.UnexpectedDER(
999-
"expected '1' at start of DER privkey, got %d" % one
999+
"expected version '1' at start of DER privkey, got %d"
1000+
% version
10001001
)
1002+
10011003
privkey_str, s = der.remove_octet_string(s)
10021004

10031005
if not curve:

0 commit comments

Comments
 (0)