Skip to content

Commit f7e7c3d

Browse files
DoumanAshtarcieri
andauthored
Implement SecretKey::from_pem to decode variety of keys (#2387)
As suggested in RustCrypto/signatures#1305 created API to decode SecretKey from variety of PEM encodings --------- Co-authored-by: Tony Arcieri <bascule@gmail.com>
1 parent b397bd3 commit f7e7c3d

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

elliptic-curve/src/secret_key.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,88 @@ where
293293
}
294294
}
295295

296+
#[cfg(feature = "pem")]
297+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
298+
pub enum PemParseError {
299+
///Indicates invalid PEM string
300+
Pem(pem_rfc7468::Error),
301+
///Indicates invalid pkcs8 EC key
302+
Pkcs8(::pkcs8::Error),
303+
///Indicates invalid Sec1 EC key
304+
Sec1(::sec1::Error),
305+
///Unable to recognize document label
306+
UnknownLabel,
307+
}
308+
309+
#[cfg(feature = "pem")]
310+
impl From<pem_rfc7468::Error> for PemParseError {
311+
#[inline(always)]
312+
fn from(error: pem_rfc7468::Error) -> Self {
313+
Self::Pem(error)
314+
}
315+
}
316+
317+
#[cfg(feature = "pem")]
318+
impl From<::pkcs8::Error> for PemParseError {
319+
#[inline(always)]
320+
fn from(error: ::pkcs8::Error) -> Self {
321+
Self::Pkcs8(error)
322+
}
323+
}
324+
325+
#[cfg(feature = "pem")]
326+
impl From<::sec1::Error> for PemParseError {
327+
#[inline(always)]
328+
fn from(error: ::sec1::Error) -> Self {
329+
Self::Sec1(error)
330+
}
331+
}
332+
333+
#[cfg(feature = "pem")]
334+
impl fmt::Display for PemParseError {
335+
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
336+
match self {
337+
Self::Pem(error) => fmt.write_fmt(format_args!("Failed to parse PEM: {error}")),
338+
Self::UnknownLabel => fmt.write_str("Unrecognized key label"),
339+
Self::Pkcs8(error) => fmt.write_fmt(format_args!("Faoled to parse Pkcs8 key: {error}")),
340+
Self::Sec1(error) => fmt.write_fmt(format_args!("Faoled to parse SEC1 key: {error}")),
341+
}
342+
}
343+
}
344+
345+
#[cfg(feature = "pem")]
346+
impl core::error::Error for PemParseError {}
347+
348+
#[cfg(feature = "pem")]
349+
impl<C> SecretKey<C>
350+
where
351+
C: AssociatedOid + Curve + ValidatePublicKey,
352+
FieldBytesSize<C>: ModulusSize,
353+
{
354+
/// Parse [`SecretKey`] from PEM-encoded private key.
355+
///
356+
/// Supported formats:
357+
/// - `SEC1` - requires feature `sec1`
358+
/// - `PKCS #8` - requires feature `pkcs8`
359+
///
360+
/// # Errors
361+
/// - If `pem` is not valid PEM encoded private key
362+
/// - If label within `pem` is not known valid label
363+
/// - If label is valid, but unable to decode DER content of the PEM file
364+
#[cfg(feature = "pem")]
365+
pub fn from_pem(pem: &str) -> ::core::result::Result<Self, PemParseError> {
366+
let label = pem_rfc7468::decode_label(pem.as_bytes()).map_err(PemParseError::Pem)?;
367+
368+
if ::pkcs8::PrivateKeyInfoRef::validate_pem_label(label).is_ok() {
369+
return ::pkcs8::DecodePrivateKey::from_pkcs8_pem(pem).map_err(PemParseError::Pkcs8);
370+
} else if ::sec1::EcPrivateKey::validate_pem_label(label).is_ok() {
371+
return ::sec1::DecodeEcPrivateKey::from_sec1_pem(pem).map_err(PemParseError::Sec1);
372+
}
373+
374+
Err(PemParseError::UnknownLabel)
375+
}
376+
}
377+
296378
impl<C> ConstantTimeEq for SecretKey<C>
297379
where
298380
C: Curve,

elliptic-curve/tests/pkcs8.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,34 @@ const EXAMPLE_SCALAR: [u8; 32] =
2323
hex!("AABBCCDDEEFF0000000000000000000000000000000000000000000000000001");
2424

2525
/// Example PKCS#8 private key
26-
fn example_private_key() -> der::SecretDocument {
26+
fn example_private_key_der() -> der::SecretDocument {
2727
SecretKey::from_slice(&EXAMPLE_SCALAR)
2828
.unwrap()
2929
.to_pkcs8_der()
3030
.unwrap()
3131
}
3232

33+
#[cfg(feature = "pem")]
34+
/// Example PKCS#8 private key
35+
fn example_private_key_pem() -> impl AsRef<str> {
36+
SecretKey::from_slice(&EXAMPLE_SCALAR)
37+
.unwrap()
38+
.to_pkcs8_pem(Default::default())
39+
.unwrap()
40+
}
41+
3342
#[test]
3443
fn decode_pkcs8_private_key_from_der() {
35-
dbg!(example_private_key().as_bytes());
36-
let secret_key = SecretKey::from_pkcs8_der(example_private_key().as_bytes()).unwrap();
44+
dbg!(example_private_key_der().as_bytes());
45+
let secret_key = SecretKey::from_pkcs8_der(example_private_key_der().as_bytes()).unwrap();
46+
assert_eq!(secret_key.to_bytes().as_slice(), &EXAMPLE_SCALAR);
47+
}
48+
49+
#[cfg(feature = "pem")]
50+
#[test]
51+
fn decode_pkcs8_private_key_from_pem() {
52+
dbg!(example_private_key_pem().as_ref());
53+
let secret_key = SecretKey::from_pem(example_private_key_pem().as_ref()).unwrap();
3754
assert_eq!(secret_key.to_bytes().as_slice(), &EXAMPLE_SCALAR);
3855
}
3956

0 commit comments

Comments
 (0)