Skip to content

Commit 13a4d00

Browse files
committed
pem: use local PEM parser and generator, strict is strict
1 parent 6d81094 commit 13a4d00

File tree

11 files changed

+276
-140
lines changed

11 files changed

+276
-140
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ argon2 = ["dep:argon2"]
2020
async = ["dep:futures"]
2121
cbor = ["dep:thiserror"]
2222
cose = ["cbor", "xhpke"]
23-
eddsa = ["dep:ed25519-dalek", "dep:sha2", "dep:signature"]
23+
eddsa = ["pem", "dep:ed25519-dalek", "dep:sha2", "dep:signature"]
2424
hkdf = ["dep:hkdf", "dep:sha2"]
2525
mldsa = ["pem", "dep:der", "dep:ml-dsa", "dep:pkcs8", "dep:sha2", "dep:spki", "dep:subtle", "dep:zeroize"]
26-
pem = ["dep:pem"]
26+
pem = ["dep:base64"]
2727
rand = []
2828
rsa = ["pem", "dep:rsa", "dep:signature"]
2929
stream = ["dep:age-core", "dep:chacha20poly1305", "dep:pin-project", "dep:zeroize"]
@@ -34,6 +34,7 @@ xhpke = ["pem", "xdsa", "dep:generic-array", "dep:hpke", "dep:rand", "dep:rand_c
3434
[dependencies]
3535
age-core = { version = "0.11.0", optional = true }
3636
argon2 = { version = "0.5.3", optional = true }
37+
base64 = { version = "0.22.1", optional = true }
3738
bcder = { version = "0.7.5", optional = true }
3839
bytes = { version = "1.10.1", optional = true }
3940
chacha20poly1305 = { version = "0.10.1", optional = true }
@@ -47,7 +48,6 @@ hkdf = { version = "0.12.4", optional = true }
4748
getrandom02 = { package = "getrandom", version = "0.2", features = ["js"] }
4849
getrandom = { version = "0.3.3", features = ["wasm_js"] }
4950
hpke = { version = "0.13.0", features = ["std"], optional = true }
50-
pem = { version = "3.0.5", optional = true }
5151
pin-project = { version = "1.1.9", optional = true }
5252
pkcs8 = { version = "0.10.2", features = ["std"], optional = true }
5353
rand = { version = "0.9.2", optional = true }

src/eddsa/mod.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
//! https://datatracker.ietf.org/doc/html/rfc8032
1010
1111
use ed25519_dalek::ed25519::signature::rand_core::OsRng;
12-
use ed25519_dalek::pkcs8::spki::der::pem::LineEnding;
1312
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
1413
use ed25519_dalek::{Signature, SignatureError, Signer, Verifier};
1514
use sha2::Digest;
1615
use std::error::Error;
1716

17+
use crate::pem;
18+
1819
/// SecretKey contains an Ed25519 private key usable for signing.
1920
#[derive(Clone)]
2021
pub struct SecretKey {
@@ -44,9 +45,12 @@ impl SecretKey {
4445
}
4546

4647
/// from_pem parses a PEM string into a private key.
47-
pub fn from_pem(pem: &str) -> Result<Self, Box<dyn Error>> {
48-
let inner = ed25519_dalek::SigningKey::from_pkcs8_pem(pem)?;
49-
Ok(Self { inner })
48+
pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
49+
let (kind, data) = pem::decode(pem_str.as_bytes())?;
50+
if kind != "PRIVATE KEY" {
51+
return Err(format!("invalid PEM tag {}", kind).into());
52+
}
53+
Self::from_der(&data)
5054
}
5155

5256
/// to_bytes converts a private key into a 32-byte array.
@@ -61,7 +65,7 @@ impl SecretKey {
6165

6266
/// to_pem serializes a private key into a PEM string.
6367
pub fn to_pem(&self) -> String {
64-
self.inner.to_pkcs8_pem(LineEnding::LF).unwrap().to_string()
68+
pem::encode("PRIVATE KEY", &self.to_der())
6569
}
6670

6771
/// public_key retrieves the public counterpart of the secret key.
@@ -103,9 +107,12 @@ impl PublicKey {
103107
}
104108

105109
/// from_pem parses a PEM string into a public key.
106-
pub fn from_pem(pem: &str) -> Result<Self, Box<dyn Error>> {
107-
let inner = ed25519_dalek::VerifyingKey::from_public_key_pem(pem)?;
108-
Ok(Self { inner })
110+
pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
111+
let (kind, data) = pem::decode(pem_str.as_bytes())?;
112+
if kind != "PUBLIC KEY" {
113+
return Err(format!("invalid PEM tag {}", kind).into());
114+
}
115+
Self::from_der(&data)
109116
}
110117

111118
/// to_bytes converts a public key into a 32-byte array.
@@ -120,10 +127,7 @@ impl PublicKey {
120127

121128
/// to_pem serializes a public key into a PEM string.
122129
pub fn to_pem(&self) -> String {
123-
self.inner
124-
.to_public_key_pem(LineEnding::LF)
125-
.unwrap()
126-
.to_string()
130+
pem::encode("PUBLIC KEY", &self.to_der())
127131
}
128132

129133
/// fingerprint returns a 256bit unique identified for this key. For Ed25519,

src/internal/mod.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/internal/pem.rs

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ pub mod cose;
2424
#[cfg(feature = "eddsa")]
2525
pub mod eddsa;
2626

27-
pub(crate) mod internal;
28-
2927
#[cfg(feature = "mldsa")]
3028
pub mod mldsa;
3129

30+
#[cfg(feature = "pem")]
31+
pub mod pem;
32+
3233
#[cfg(feature = "rsa")]
3334
pub mod rsa;
3435

src/mldsa/mod.rs

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ use std::error::Error;
2020
use subtle::ConstantTimeEq;
2121
use zeroize::Zeroize;
2222

23+
use crate::pem;
24+
2325
/// OpenSSL ML-DSA-65 private key inner structure.
2426
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
2527
struct MlDsa65PrivateKeyInner {
@@ -94,12 +96,12 @@ impl SecretKey {
9496
/// from_pem parses a PEM string into a private key.
9597
pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
9698
// Crack open the PEM to get to the private key info
97-
let res = crate::internal::pem::parse(pem_str)?;
98-
if res.tag() != "PRIVATE KEY" {
99-
return Err(format!("invalid PEM tag {}", res.tag()).into());
99+
let (kind, data) = pem::decode(pem_str.as_bytes())?;
100+
if kind != "PRIVATE KEY" {
101+
return Err(format!("invalid PEM tag {}", kind).into());
100102
}
101103
// Parse the DER content
102-
Self::from_der(res.contents())
104+
Self::from_der(&data)
103105
}
104106

105107
/// to_bytes returns the 32-byte seed of the private key.
@@ -136,11 +138,7 @@ impl SecretKey {
136138

137139
/// to_pem serializes a private key into a PEM string.
138140
pub fn to_pem(&self) -> String {
139-
let der = self.to_der();
140-
pem::encode_config(
141-
&pem::Pem::new("PRIVATE KEY", der),
142-
pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
143-
)
141+
pem::encode("PRIVATE KEY", &self.to_der())
144142
}
145143

146144
/// public_key retrieves the public counterpart of the secret key.
@@ -200,11 +198,11 @@ impl PublicKey {
200198

201199
/// from_pem parses a PEM string into a public key.
202200
pub fn from_pem(pem_str: &str) -> Result<Self, Box<dyn Error>> {
203-
let res = crate::internal::pem::parse(pem_str)?;
204-
if res.tag() != "PUBLIC KEY" {
205-
return Err(format!("invalid PEM tag {}", res.tag()).into());
201+
let (kind, data) = pem::decode(pem_str.as_bytes())?;
202+
if kind != "PUBLIC KEY" {
203+
return Err(format!("invalid PEM tag {}", kind).into());
206204
}
207-
Self::from_der(res.contents())
205+
Self::from_der(&data)
208206
}
209207

210208
/// to_bytes converts a public key into a 1952-byte array.
@@ -233,11 +231,7 @@ impl PublicKey {
233231

234232
/// to_pem serializes a public key into a PEM string.
235233
pub fn to_pem(&self) -> String {
236-
let der = self.to_der();
237-
pem::encode_config(
238-
&pem::Pem::new("PUBLIC KEY", der),
239-
pem::EncodeConfig::new().set_line_ending(pem::LineEnding::LF),
240-
)
234+
pem::encode("PUBLIC KEY", &self.to_der())
241235
}
242236

243237
/// fingerprint returns a 256bit unique identified for this key. For ML-DSA,

0 commit comments

Comments
 (0)