#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(dead_code)]
pub(crate) enum PemKind {
Rsa,
Ec,
Ambiguous,
Unknown,
}
#[allow(dead_code)]
pub(crate) fn detect(bytes: &[u8]) -> PemKind {
for line in bytes.split(|&b| b == b'\n') {
let trimmed = trim_ascii(line);
if trimmed.is_empty() {
continue;
}
return match trimmed {
b"-----BEGIN RSA PRIVATE KEY-----" | b"-----BEGIN RSA PUBLIC KEY-----" => PemKind::Rsa,
b"-----BEGIN EC PRIVATE KEY-----" => PemKind::Ec,
b"-----BEGIN PRIVATE KEY-----" | b"-----BEGIN PUBLIC KEY-----" => PemKind::Ambiguous,
_ => PemKind::Unknown,
};
}
PemKind::Unknown
}
#[allow(dead_code)]
fn trim_ascii(mut bytes: &[u8]) -> &[u8] {
while let [first, rest @ ..] = bytes {
if first.is_ascii_whitespace() {
bytes = rest;
} else {
break;
}
}
while let [rest @ .., last] = bytes {
if last.is_ascii_whitespace() {
bytes = rest;
} else {
break;
}
}
bytes
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_detects_rsa_private() {
assert_eq!(
detect(b"-----BEGIN RSA PRIVATE KEY-----\nabc\n"),
PemKind::Rsa
);
}
#[test]
fn it_detects_rsa_public() {
assert_eq!(
detect(b"-----BEGIN RSA PUBLIC KEY-----\nabc\n"),
PemKind::Rsa
);
}
#[test]
fn it_detects_ec_private() {
assert_eq!(
detect(b"-----BEGIN EC PRIVATE KEY-----\nabc\n"),
PemKind::Ec
);
}
#[test]
fn it_detects_pkcs8_as_ambiguous() {
assert_eq!(
detect(b"-----BEGIN PRIVATE KEY-----\nabc\n"),
PemKind::Ambiguous
);
}
#[test]
fn it_detects_spki_as_ambiguous() {
assert_eq!(
detect(b"-----BEGIN PUBLIC KEY-----\nabc\n"),
PemKind::Ambiguous
);
}
#[test]
fn it_returns_unknown_for_no_header() {
assert_eq!(detect(b"no header here"), PemKind::Unknown);
}
#[test]
fn it_returns_unknown_for_unfamiliar_header() {
assert_eq!(
detect(b"-----BEGIN CERTIFICATE-----\nabc\n"),
PemKind::Unknown
);
}
#[test]
fn it_handles_leading_whitespace() {
assert_eq!(
detect(b"\n\n-----BEGIN EC PRIVATE KEY-----\nabc\n"),
PemKind::Ec
);
}
#[test]
fn it_returns_unknown_for_empty_input() {
assert_eq!(detect(b""), PemKind::Unknown);
}
#[test]
fn it_returns_unknown_for_only_whitespace() {
assert_eq!(detect(b" \n\t\n \n"), PemKind::Unknown);
}
#[test]
fn it_handles_crlf_line_endings() {
assert_eq!(
detect(b"-----BEGIN RSA PRIVATE KEY-----\r\nabc\r\n"),
PemKind::Rsa
);
}
#[test]
fn it_handles_trailing_whitespace_on_header_line() {
assert_eq!(
detect(b"-----BEGIN EC PRIVATE KEY----- \nabc\n"),
PemKind::Ec
);
}
#[test]
fn it_detects_header_without_trailing_newline() {
assert_eq!(detect(b"-----BEGIN RSA PRIVATE KEY-----"), PemKind::Rsa);
}
#[test]
fn it_skips_leading_blank_lines_with_mixed_whitespace() {
assert_eq!(
detect(b"\r\n \t\n-----BEGIN PUBLIC KEY-----\n"),
PemKind::Ambiguous
);
}
#[test]
fn it_stops_at_first_non_empty_line() {
assert_eq!(
detect(b"garbage\n-----BEGIN RSA PRIVATE KEY-----\n"),
PemKind::Unknown
);
}
}