Skip to content

Commit 7818f0b

Browse files
committed
support explicit curves mapping to named curves
We now support loading EC keys with explicit encodings of named curves. This requires that the explicit curve encoding match exactly (but allows the omission of the seed parameter to maintain compatibility with the -no_seed OpenSSL flag). This is only supported for secp256r1, secp384r1, secp521r1, and secp256k1 at this time. This is not arbitrary explicit curve support; we still have no plans to implement that.
1 parent 43fd312 commit 7818f0b

File tree

9 files changed

+286
-10
lines changed

9 files changed

+286
-10
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ Changelog
7373
``ecdsa_deterministic`` in :meth:`~cryptography.x509.CertificateBuilder.sign`,
7474
:meth:`~cryptography.x509.CertificateRevocationListBuilder.sign`
7575
and :meth:`~cryptography.x509.CertificateSigningRequestBuilder.sign`.
76+
* Added support for loading elliptic curve keys that contain explicit encodings
77+
of the curves ``secp256r1``, ``secp384r1``, ``secp521r1``, and ``secp256k1``.
7678

7779
.. _v44-0-3:
7880

src/rust/cryptography-key-parsing/src/ec.rs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// for complete details.
44

55
use cryptography_x509::common::EcParameters;
6+
use cryptography_x509::ec_constants;
67

78
use crate::{KeyParsingError, KeyParsingResult};
89

@@ -57,9 +58,34 @@ pub(crate) fn ec_params_to_group(
5758
Ok(openssl::ec::EcGroup::from_curve_name(curve_nid)
5859
.map_err(|_| KeyParsingError::UnsupportedEllipticCurve(curve_oid.clone()))?)
5960
}
60-
EcParameters::ImplicitCurve(_) | EcParameters::SpecifiedCurve(_) => {
61-
Err(KeyParsingError::ExplicitCurveUnsupported)
61+
EcParameters::SpecifiedCurve(params) => {
62+
// We do not support arbitrary explicit curves. Instead we map values
63+
// to named curves. This currently supports only P256, P384,
64+
// P521, and SECP256K1. No binary curves are supported. Everything must
65+
// match, except the seed may be omitted on NIST curves since OpenSSL
66+
// has supported a -no_seed option for over 20 years and I don't want to
67+
// figure out whether anyone uses that or not. No one should be using
68+
// explicit curve encoding anyway. Curves were meant to be named!
69+
let curve_nid = match params {
70+
&ec_constants::P256_DOMAIN | &ec_constants::P256_DOMAIN_NO_SEED => {
71+
openssl::nid::Nid::X9_62_PRIME256V1
72+
}
73+
&ec_constants::P384_DOMAIN | &ec_constants::P384_DOMAIN_NO_SEED => {
74+
openssl::nid::Nid::SECP384R1
75+
}
76+
&ec_constants::P521_DOMAIN | &ec_constants::P521_DOMAIN_NO_SEED => {
77+
openssl::nid::Nid::SECP521R1
78+
}
79+
&ec_constants::SECP256K1_DOMAIN => openssl::nid::Nid::SECP256K1,
80+
_ => return Err(KeyParsingError::ExplicitCurveUnsupported),
81+
};
82+
Ok(
83+
openssl::ec::EcGroup::from_curve_name(curve_nid).map_err(|_| {
84+
KeyParsingError::UnsupportedEllipticCurve(cryptography_x509::oid::EC_SECP256R1)
85+
})?,
86+
)
6287
}
88+
EcParameters::ImplicitCurve(_) => Err(KeyParsingError::ExplicitCurveUnsupported),
6389
}
6490
}
6591

src/rust/cryptography-x509/src/common.rs

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,59 @@ pub const PSS_SHA512_MASK_GEN_ALG: MaskGenAlgorithm<'_> = MaskGenAlgorithm {
447447
pub enum EcParameters<'a> {
448448
NamedCurve(asn1::ObjectIdentifier),
449449
ImplicitCurve(asn1::Null),
450-
SpecifiedCurve(asn1::Sequence<'a>),
450+
SpecifiedCurve(SpecifiedECDomain<'a>),
451+
}
452+
453+
// From RFC 3279 Section 2.3.5 and RFC 5480 Appendix A
454+
// SpecifiedECDomain ::= SEQUENCE {
455+
// version ECPVer,
456+
// fieldID FieldID,
457+
// curve Curve,
458+
// base ECPoint,
459+
// order INTEGER,
460+
// cofactor INTEGER OPTIONAL
461+
// }
462+
#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, Clone, Eq, PartialEq, Debug)]
463+
pub struct SpecifiedECDomain<'a> {
464+
pub version: u8,
465+
pub field_id: FieldID<'a>,
466+
pub curve: Curve<'a>,
467+
pub base: &'a [u8], // ECPoint can be compressed or uncompressed
468+
pub order: asn1::BigUint<'a>,
469+
pub cofactor: Option<u8>,
470+
}
471+
472+
// From RFC 3279 Section 2.3.5
473+
// FieldID ::= SEQUENCE {
474+
// fieldType FIELD-TYPE.&id({SupportedFieldTypes}),
475+
// parameters FIELD-TYPE.&Type({SupportedFieldTypes}{@fieldType})
476+
// }
477+
#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, Clone, PartialEq, Eq, Debug)]
478+
pub struct FieldID<'a> {
479+
pub field_type: asn1::DefinedByMarker<asn1::ObjectIdentifier>,
480+
#[defined_by(field_type)]
481+
pub parameters: FieldParameters<'a>,
482+
}
483+
484+
#[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, Hash, Clone, PartialEq, Eq, Debug)]
485+
pub enum FieldParameters<'a> {
486+
#[defined_by(oid::PRIME_FIELD_OID)]
487+
PrimeField(asn1::BigUint<'a>),
488+
#[defined_by(oid::CHARACTERISTIC_TWO_FIELD_OID)]
489+
CharacteristicTwo(asn1::Tlv<'a>),
490+
}
491+
492+
// From RFC 3279 Section 2.3.5
493+
// Curve ::= SEQUENCE {
494+
// a FieldElement,
495+
// b FieldElement,
496+
// seed BIT STRING OPTIONAL
497+
// }
498+
#[derive(asn1::Asn1Read, asn1::Asn1Write, Hash, Clone, PartialEq, Eq, Debug)]
499+
pub struct Curve<'a> {
500+
pub a: &'a [u8], // FieldElement
501+
pub b: &'a [u8], // FieldElement
502+
pub seed: Option<asn1::BitString<'a>>,
451503
}
452504

453505
// From RFC 4055 section 3.1:
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
// This file is dual licensed under the terms of the Apache License, Version
2+
// 2.0, and the BSD License. See the LICENSE file in the root of this repository
3+
// for complete details.
4+
5+
use crate::common::{Curve, FieldID, FieldParameters, SpecifiedECDomain};
6+
7+
const P256_FIELD: FieldID<'static> = FieldID {
8+
field_type: asn1::DefinedByMarker::marker(),
9+
parameters: FieldParameters::PrimeField(asn1::BigUint::new(b"\x00\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap()),
10+
};
11+
const P256_CURVE_A: &[u8; 32] = b"\xff\xff\xff\xff\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc";
12+
const P256_CURVE_B: &[u8; 32] = b"\x5a\xc6\x35\xd8\xaa\x3a\x93\xe7\xb3\xeb\xbd\x55\x76\x98\x86\xbc\x65\x1d\x06\xb0\xcc\x53\xb0\xf6\x3b\xce\x3c\x3e\x27\xd2\x60\x4b";
13+
const P256_SEED: asn1::BitString<'static> = asn1::BitString::new(
14+
b"\xc4\x9d\x36\x08\x86\xe7\x04\x93\x6a\x66\x78\xe1\x13\x9d\x26\xb7\x81\x9f\x7e\x90",
15+
0,
16+
)
17+
.unwrap();
18+
const P256_UNCOMPRESSED_BASE: &[u8; 65] = b"\x04\x6b\x17\xd1\xf2\xe1\x2c\x42\x47\xf8\xbc\xe6\xe5\x63\xa4\x40\xf2\x77\x03\x7d\x81\x2d\xeb\x33\xa0\xf4\xa1\x39\x45\xd8\x98\xc2\x96\x4f\xe3\x42\xe2\xfe\x1a\x7f\x9b\x8e\xe7\xeb\x4a\x7c\x0f\x9e\x16\x2b\xce\x33\x57\x6b\x31\x5e\xce\xcb\xb6\x40\x68\x37\xbf\x51\xf5";
19+
const P256_ORDER: asn1::BigUint<'static> = asn1::BigUint::new(b"\x00\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xbc\xe6\xfa\xad\xa7\x17\x9e\x84\xf3\xb9\xca\xc2\xfc\x63\x25\x51").unwrap();
20+
21+
pub const P256_DOMAIN: SpecifiedECDomain<'static> = SpecifiedECDomain {
22+
version: 1,
23+
field_id: P256_FIELD,
24+
curve: Curve {
25+
a: P256_CURVE_A,
26+
b: P256_CURVE_B,
27+
seed: Some(P256_SEED),
28+
},
29+
base: P256_UNCOMPRESSED_BASE,
30+
order: P256_ORDER,
31+
cofactor: Some(1),
32+
};
33+
34+
pub const P256_DOMAIN_NO_SEED: SpecifiedECDomain<'static> = SpecifiedECDomain {
35+
version: 1,
36+
field_id: P256_FIELD,
37+
curve: Curve {
38+
a: P256_CURVE_A,
39+
b: P256_CURVE_B,
40+
seed: None,
41+
},
42+
base: P256_UNCOMPRESSED_BASE,
43+
order: P256_ORDER,
44+
cofactor: Some(1),
45+
};
46+
47+
const P384_FIELD: FieldID<'static> = FieldID {
48+
field_type: asn1::DefinedByMarker::marker(),
49+
parameters: FieldParameters::PrimeField(asn1::BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff").unwrap()),
50+
};
51+
const P384_CURVE_A: &[u8; 48] = b"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xfc";
52+
const P384_CURVE_B: &[u8; 48] = b"\xb3\x31\x2f\xa7\xe2\x3e\xe7\xe4\x98\x8e\x05\x6b\xe3\xf8\x2d\x19\x18\x1d\x9c\x6e\xfe\x81\x41\x12\x03\x14\x08\x8f\x50\x13\x87\x5a\xc6\x56\x39\x8d\x8a\x2e\xd1\x9d\x2a\x85\xc8\xed\xd3\xec\x2a\xef";
53+
const P384_SEED: asn1::BitString<'static> = asn1::BitString::new(
54+
b"\xa3\x35\x92\x6a\xa3\x19\xa2\x7a\x1d\x00\x89\x6a\x67\x73\xa4\x82\x7a\xcd\xac\x73",
55+
0,
56+
)
57+
.unwrap();
58+
const P384_UNCOMPRESSED_BASE: &[u8; 97] = b"\x04\xaa\x87\xca\x22\xbe\x8b\x05\x37\x8e\xb1\xc7\x1e\xf3\x20\xad\x74\x6e\x1d\x3b\x62\x8b\xa7\x9b\x98\x59\xf7\x41\xe0\x82\x54\x2a\x38\x55\x02\xf2\x5d\xbf\x55\x29\x6c\x3a\x54\x5e\x38\x72\x76\x0a\xb7\x36\x17\xde\x4a\x96\x26\x2c\x6f\x5d\x9e\x98\xbf\x92\x92\xdc\x29\xf8\xf4\x1d\xbd\x28\x9a\x14\x7c\xe9\xda\x31\x13\xb5\xf0\xb8\xc0\x0a\x60\xb1\xce\x1d\x7e\x81\x9d\x7a\x43\x1d\x7c\x90\xea\x0e\x5f";
59+
const P384_ORDER: asn1::BigUint<'static> = asn1::BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc7\x63\x4d\x81\xf4\x37\x2d\xdf\x58\x1a\x0d\xb2\x48\xb0\xa7\x7a\xec\xec\x19\x6a\xcc\xc5\x29\x73").unwrap();
60+
61+
pub const P384_DOMAIN: SpecifiedECDomain<'static> = SpecifiedECDomain {
62+
version: 1,
63+
field_id: P384_FIELD,
64+
curve: Curve {
65+
a: P384_CURVE_A,
66+
b: P384_CURVE_B,
67+
seed: Some(P384_SEED),
68+
},
69+
base: P384_UNCOMPRESSED_BASE,
70+
order: P384_ORDER,
71+
cofactor: Some(1),
72+
};
73+
74+
pub const P384_DOMAIN_NO_SEED: SpecifiedECDomain<'static> = SpecifiedECDomain {
75+
version: 1,
76+
field_id: P384_FIELD,
77+
curve: Curve {
78+
a: P384_CURVE_A,
79+
b: P384_CURVE_B,
80+
seed: None,
81+
},
82+
base: P384_UNCOMPRESSED_BASE,
83+
order: P384_ORDER,
84+
cofactor: Some(1),
85+
};
86+
87+
const P521_FIELD: FieldID<'static> = FieldID {
88+
field_type: asn1::DefinedByMarker::marker(),
89+
parameters: FieldParameters::PrimeField(asn1::BigUint::new(b"\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap()),
90+
};
91+
const P521_CURVE_A: &[u8; 66] = b"\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc";
92+
const P521_CURVE_B: &[u8; 66] = b"\x00\x51\x95\x3e\xb9\x61\x8e\x1c\x9a\x1f\x92\x9a\x21\xa0\xb6\x85\x40\xee\xa2\xda\x72\x5b\x99\xb3\x15\xf3\xb8\xb4\x89\x91\x8e\xf1\x09\xe1\x56\x19\x39\x51\xec\x7e\x93\x7b\x16\x52\xc0\xbd\x3b\xb1\xbf\x07\x35\x73\xdf\x88\x3d\x2c\x34\xf1\xef\x45\x1f\xd4\x6b\x50\x3f\x00";
93+
const P521_SEED: asn1::BitString<'static> = asn1::BitString::new(
94+
b"\xd0\x9e\x88\x00\x29\x1c\xb8\x53\x96\xcc\x67\x17\x39\x32\x84\xaa\xa0\xda\x64\xba",
95+
0,
96+
)
97+
.unwrap();
98+
const P521_UNCOMPRESSED_BASE: &[u8; 133] = b"\x04\x00\xc6\x85\x8e\x06\xb7\x04\x04\xe9\xcd\x9e\x3e\xcb\x66\x23\x95\xb4\x42\x9c\x64\x81\x39\x05\x3f\xb5\x21\xf8\x28\xaf\x60\x6b\x4d\x3d\xba\xa1\x4b\x5e\x77\xef\xe7\x59\x28\xfe\x1d\xc1\x27\xa2\xff\xa8\xde\x33\x48\xb3\xc1\x85\x6a\x42\x9b\xf9\x7e\x7e\x31\xc2\xe5\xbd\x66\x01\x18\x39\x29\x6a\x78\x9a\x3b\xc0\x04\x5c\x8a\x5f\xb4\x2c\x7d\x1b\xd9\x98\xf5\x44\x49\x57\x9b\x44\x68\x17\xaf\xbd\x17\x27\x3e\x66\x2c\x97\xee\x72\x99\x5e\xf4\x26\x40\xc5\x50\xb9\x01\x3f\xad\x07\x61\x35\x3c\x70\x86\xa2\x72\xc2\x40\x88\xbe\x94\x76\x9f\xd1\x66\x50";
99+
const P521_ORDER: asn1::BigUint<'static> = asn1::BigUint::new(b"\x01\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfa\x51\x86\x87\x83\xbf\x2f\x96\x6b\x7f\xcc\x01\x48\xf7\x09\xa5\xd0\x3b\xb5\xc9\xb8\x89\x9c\x47\xae\xbb\x6f\xb7\x1e\x91\x38\x64\x09").unwrap();
100+
101+
pub const P521_DOMAIN: SpecifiedECDomain<'static> = SpecifiedECDomain {
102+
version: 1,
103+
field_id: P521_FIELD,
104+
curve: Curve {
105+
a: P521_CURVE_A,
106+
b: P521_CURVE_B,
107+
seed: Some(P521_SEED),
108+
},
109+
base: P521_UNCOMPRESSED_BASE,
110+
order: P521_ORDER,
111+
cofactor: Some(1),
112+
};
113+
114+
pub const P521_DOMAIN_NO_SEED: SpecifiedECDomain<'static> = SpecifiedECDomain {
115+
version: 1,
116+
field_id: P521_FIELD,
117+
curve: Curve {
118+
a: P521_CURVE_A,
119+
b: P521_CURVE_B,
120+
seed: None,
121+
},
122+
base: P521_UNCOMPRESSED_BASE,
123+
order: P521_ORDER,
124+
cofactor: Some(1),
125+
};
126+
127+
// There is no seed for this curve so we don't need to create two domains like the previous curves
128+
pub const SECP256K1_DOMAIN: SpecifiedECDomain<'static> = SpecifiedECDomain {
129+
version: 1,
130+
field_id: FieldID {
131+
field_type: asn1::DefinedByMarker::marker(),
132+
parameters: FieldParameters::PrimeField(asn1::BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xfc\x2f").unwrap()),
133+
},
134+
curve: Curve {
135+
a: b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
136+
b: b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x07",
137+
seed: None,
138+
},
139+
base: b"\x04\x79\xbe\x66\x7e\xf9\xdc\xbb\xac\x55\xa0\x62\x95\xce\x87\x0b\x07\x02\x9b\xfc\xdb\x2d\xce\x28\xd9\x59\xf2\x81\x5b\x16\xf8\x17\x98\x48\x3a\xda\x77\x26\xa3\xc4\x65\x5d\xa4\xfb\xfc\x0e\x11\x08\xa8\xfd\x17\xb4\x48\xa6\x85\x54\x19\x9c\x47\xd0\x8f\xfb\x10\xd4\xb8",
140+
order: asn1::BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xba\xae\xdc\xe6\xaf\x48\xa0\x3b\xbf\xd2\x5e\x8c\xd0\x36\x41\x41").unwrap(),
141+
cofactor: Some(1),
142+
};

src/rust/cryptography-x509/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod certificate;
1010
pub mod common;
1111
pub mod crl;
1212
pub mod csr;
13+
pub mod ec_constants;
1314
pub mod extensions;
1415
pub mod name;
1516
pub mod ocsp_req;

src/rust/cryptography-x509/src/oid.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,6 @@ pub const HMAC_WITH_SHA224_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 1
172172
pub const HMAC_WITH_SHA256_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 2, 9);
173173
pub const HMAC_WITH_SHA384_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 2, 10);
174174
pub const HMAC_WITH_SHA512_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 113549, 2, 11);
175+
176+
pub const PRIME_FIELD_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 1, 1);
177+
pub const CHARACTERISTIC_TWO_FIELD_OID: asn1::ObjectIdentifier = asn1::oid!(1, 2, 840, 10045, 1, 2);

src/rust/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl From<cryptography_key_parsing::KeyParsingError> for CryptographyError {
6464
}
6565
cryptography_key_parsing::KeyParsingError::ExplicitCurveUnsupported => {
6666
CryptographyError::Py(pyo3::exceptions::PyValueError::new_err(
67-
"ECDSA keys with explicit parameters are unsupported at this time",
67+
"ECDSA keys with explicit parameters are only supported when they map to secp256r1, secp384r1, secp521r1, or secp256k1. No custom curves are supported.",
6868
))
6969
}
7070
cryptography_key_parsing::KeyParsingError::UnsupportedKeyType(oid) => {

tests/hazmat/primitives/test_ec.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,8 @@ def test_public_bytes_from_derived_public_key(self, backend):
10711071
parsed_public = serialization.load_pem_public_key(pem, backend)
10721072
assert parsed_public
10731073

1074-
def test_load_private_key_explicit_parameters(self):
1074+
def test_load_private_key_unsupported_explicit_parameters(self):
1075+
# This vector is P256 except the prime field value is wrong
10751076
with pytest.raises(ValueError, match="explicit parameters"):
10761077
load_vectors_from_file(
10771078
os.path.join(
@@ -1084,6 +1085,7 @@ def test_load_private_key_explicit_parameters(self):
10841085
)
10851086

10861087
with pytest.raises(ValueError, match="explicit parameters"):
1088+
# This vector encodes SECT233R1 explicitly
10871089
load_vectors_from_file(
10881090
os.path.join(
10891091
"asymmetric",
@@ -1096,6 +1098,54 @@ def test_load_private_key_explicit_parameters(self):
10961098
mode="rb",
10971099
)
10981100

1101+
@pytest.mark.parametrize(
1102+
("curve", "file"),
1103+
[
1104+
# secp256k1 has no seed value
1105+
(ec.SECP256K1, "secp256k1-explicit-no-seed.pem"),
1106+
(ec.SECP256R1, "secp256r1-explicit-seed.pem"),
1107+
(ec.SECP256R1, "secp256r1-explicit-no-seed.pem"),
1108+
(ec.SECP384R1, "secp384r1-explicit-seed.pem"),
1109+
(ec.SECP384R1, "secp384r1-explicit-no-seed.pem"),
1110+
(ec.SECP521R1, "secp521r1-explicit-seed.pem"),
1111+
(ec.SECP521R1, "secp521r1-explicit-no-seed.pem"),
1112+
],
1113+
)
1114+
def test_load_private_key_explicit_parameters(self, curve, file, backend):
1115+
_skip_curve_unsupported(backend, curve())
1116+
key = load_vectors_from_file(
1117+
os.path.join("asymmetric", "EC", file),
1118+
lambda pemfile: serialization.load_pem_private_key(
1119+
pemfile.read(), password=None
1120+
),
1121+
mode="rb",
1122+
)
1123+
assert isinstance(key, ec.EllipticCurvePrivateKey)
1124+
assert isinstance(key.curve, curve)
1125+
1126+
@pytest.mark.parametrize(
1127+
("curve", "file"),
1128+
[
1129+
# secp256k1 has no seed value
1130+
(ec.SECP256K1, "secp256k1-pub-explicit-no-seed.pem"),
1131+
(ec.SECP256R1, "secp256r1-pub-explicit-seed.pem"),
1132+
(ec.SECP256R1, "secp256r1-pub-explicit-no-seed.pem"),
1133+
(ec.SECP384R1, "secp384r1-pub-explicit-seed.pem"),
1134+
(ec.SECP384R1, "secp384r1-pub-explicit-no-seed.pem"),
1135+
(ec.SECP521R1, "secp521r1-pub-explicit-seed.pem"),
1136+
(ec.SECP521R1, "secp521r1-pub-explicit-no-seed.pem"),
1137+
],
1138+
)
1139+
def test_load_public_key_explicit_parameters(self, curve, file, backend):
1140+
_skip_curve_unsupported(backend, curve())
1141+
key = load_vectors_from_file(
1142+
os.path.join("asymmetric", "EC", file),
1143+
lambda pemfile: serialization.load_pem_public_key(pemfile.read()),
1144+
mode="rb",
1145+
)
1146+
assert isinstance(key, ec.EllipticCurvePublicKey)
1147+
assert isinstance(key.curve, curve)
1148+
10991149
def test_load_private_key_unsupported_curve(self):
11001150
with pytest.raises((ValueError, exceptions.UnsupportedAlgorithm)):
11011151
load_vectors_from_file(

tests/x509/test_x509.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5899,11 +5899,11 @@ def test_load_ecdsa_no_named_curve(self, backend):
58995899
os.path.join("x509", "custom", "ec_no_named_curve.pem"),
59005900
x509.load_pem_x509_certificate,
59015901
)
5902-
# This test can trigger three different value errors depending
5903-
# on OpenSSL/BoringSSL and versions. Match on the text to ensure
5904-
# we are getting the right error.
5905-
with pytest.raises(ValueError, match="explicit parameters"):
5906-
cert.public_key()
5902+
# We map explicit parameters to known curves and this cert
5903+
# contains explicit params for P256, so it should load.
5904+
pk = cert.public_key()
5905+
assert isinstance(pk, ec.EllipticCurvePublicKey)
5906+
assert isinstance(pk.curve, ec.SECP256R1)
59075907

59085908
def test_verify_directly_issued_by_ec(self):
59095909
issuer_private_key = ec.generate_private_key(ec.SECP256R1())

0 commit comments

Comments
 (0)