Skip to content

Commit 54689d5

Browse files
committed
ssh-key: use pem-rfc7468 for private key PEM parser
Now that `pem-rfc7468` supports a buffered Base64 decoder (#406), it's possible to use it for parsing PEM-formatted OpenSSH private keys. This commit switches to using `pem-rfc7468` for PEM parsing.
1 parent cd23518 commit 54689d5

6 files changed

Lines changed: 41 additions & 100 deletions

File tree

Cargo.lock

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

ssh-key/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rust-version = "1.57"
1717

1818
[dependencies]
1919
base64ct = { version = "=1.4.0-pre.0", path = "../base64ct" }
20+
pem-rfc7468 = { version = "0.4.0-pre.0", path = "../pem-rfc7468" }
2021
zeroize = { version = "1", default-features = false }
2122

2223
# optional dependencies

ssh-key/src/base64.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,6 @@ impl<'i> Decoder<'i> {
3232
})
3333
}
3434

35-
/// Create a new decoder for a byte slice containing Base64 which
36-
/// line wraps at the given line length.
37-
pub fn new_wrapped(input: &'i [u8], line_width: usize) -> Result<Self> {
38-
Ok(Self {
39-
inner: base64ct::Decoder::new_wrapped(input, line_width)?,
40-
})
41-
}
42-
4335
/// Decode as much Base64 as is needed to exactly fill `out`.
4436
///
4537
/// # Returns
@@ -146,6 +138,12 @@ impl<'i> Decoder<'i> {
146138
}
147139
}
148140

141+
impl<'i> From<base64ct::Decoder<'i, base64ct::Base64>> for Decoder<'i> {
142+
fn from(decoder: base64ct::Decoder<'i, base64ct::Base64>) -> Decoder<'i> {
143+
Decoder { inner: decoder }
144+
}
145+
}
146+
149147
/// Encoder trait.
150148
pub(crate) trait Encode: Sized {
151149
/// Get the length of this type encoded in bytes, prior to Base64 encoding.

ssh-key/src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ impl From<core::str::Utf8Error> for Error {
8585
}
8686
}
8787

88+
impl From<pem_rfc7468::Error> for Error {
89+
fn from(_: pem_rfc7468::Error) -> Error {
90+
Error::Pem
91+
}
92+
}
93+
8894
#[cfg(feature = "alloc")]
8995
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
9096
impl From<alloc::string::FromUtf8Error> for Error {

ssh-key/src/private.rs

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ mod dsa;
99
#[cfg(feature = "ecdsa")]
1010
mod ecdsa;
1111
mod ed25519;
12-
mod openssh;
1312
#[cfg(feature = "alloc")]
1413
mod rsa;
1514

@@ -27,10 +26,14 @@ use crate::{
2726
public, Algorithm, CipherAlg, Error, KdfAlg, KdfOptions, Result,
2827
};
2928
use core::str::FromStr;
29+
use pem_rfc7468::{self as pem, PemLabel};
3030

3131
#[cfg(feature = "alloc")]
3232
use alloc::string::String;
3333

34+
/// Line width used by the PEM encoding of OpenSSH private keys
35+
const PEM_LINE_WIDTH: usize = 70;
36+
3437
/// SSH private key.
3538
#[derive(Clone, Debug)]
3639
pub struct PrivateKey {
@@ -64,23 +67,25 @@ impl PrivateKey {
6467
/// -----BEGIN OPENSSH PRIVATE KEY-----
6568
/// ```
6669
pub fn from_openssh(input: impl AsRef<[u8]>) -> Result<Self> {
67-
let encapsulation = openssh::Encapsulation::decode(input.as_ref())?;
68-
let mut decoder = base64::Decoder::new_wrapped(
69-
encapsulation.base64_data,
70-
openssh::Encapsulation::LINE_WIDTH,
71-
)?;
70+
let pem_decoder = pem::Decoder::new_wrapped(input.as_ref(), PEM_LINE_WIDTH)?;
71+
72+
if pem_decoder.type_label() != Self::TYPE_LABEL {
73+
return Err(Error::Pem);
74+
}
75+
76+
let mut base64_decoder = base64::Decoder::from(pem_decoder.into_base64_decoder());
7277

7378
let mut auth_magic = [0u8; Self::AUTH_MAGIC.len()];
74-
decoder.decode_into(&mut auth_magic)?;
79+
base64_decoder.decode_into(&mut auth_magic)?;
7580

7681
if auth_magic != Self::AUTH_MAGIC {
7782
return Err(Error::FormatEncoding);
7883
}
7984

80-
let cipher_alg = CipherAlg::decode(&mut decoder)?;
81-
let kdf_alg = KdfAlg::decode(&mut decoder)?;
82-
let kdf_options = KdfOptions::decode(&mut decoder)?;
83-
let nkeys = decoder.decode_u32()? as usize;
85+
let cipher_alg = CipherAlg::decode(&mut base64_decoder)?;
86+
let kdf_alg = KdfAlg::decode(&mut base64_decoder)?;
87+
let kdf_options = KdfOptions::decode(&mut base64_decoder)?;
88+
let nkeys = base64_decoder.decode_u32()? as usize;
8489

8590
// TODO(tarcieri): support more than one key?
8691
if nkeys != 1 {
@@ -89,26 +94,26 @@ impl PrivateKey {
8994

9095
for _ in 0..nkeys {
9196
// TODO(tarcieri): validate decoded length
92-
let _len = decoder.decode_u32()? as usize;
93-
let _pubkey = public::KeyData::decode(&mut decoder)?;
97+
let _len = base64_decoder.decode_u32()? as usize;
98+
let _pubkey = public::KeyData::decode(&mut base64_decoder)?;
9499
}
95100

96101
// Begin decoding unencrypted list of N private keys
97102
// See OpenSSH PROTOCOL.key § 3
98103
// TODO(tarcieri): validate decoded length
99-
let _len = decoder.decode_u32()? as usize;
100-
let checkint1 = decoder.decode_u32()?;
101-
let checkint2 = decoder.decode_u32()?;
104+
let _len = base64_decoder.decode_u32()? as usize;
105+
let checkint1 = base64_decoder.decode_u32()?;
106+
let checkint2 = base64_decoder.decode_u32()?;
102107

103108
if checkint1 != checkint2 {
104109
// TODO(tarcieri): treat this as a cryptographic error?
105110
return Err(Error::FormatEncoding);
106111
}
107112

108-
let key_data = KeypairData::decode(&mut decoder)?;
113+
let key_data = KeypairData::decode(&mut base64_decoder)?;
109114

110115
#[cfg(feature = "alloc")]
111-
let comment = decoder.decode_string()?;
116+
let comment = base64_decoder.decode_string()?;
112117

113118
// TODO(tarcieri): parse/validate padding bytes?
114119
Ok(Self {
@@ -135,6 +140,10 @@ impl FromStr for PrivateKey {
135140
}
136141
}
137142

143+
impl PemLabel for PrivateKey {
144+
const TYPE_LABEL: &'static str = "OPENSSH PRIVATE KEY";
145+
}
146+
138147
/// Private key data.
139148
#[derive(Clone, Debug)]
140149
#[non_exhaustive]

ssh-key/src/private/openssh.rs

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

0 commit comments

Comments
 (0)