use crate::extractors::pem;
use crate::signatures::common::{SignatureError, SignatureResult, CONFIDENCE_HIGH};
use base64::prelude::BASE64_STANDARD;
use base64::Engine;
pub const PEM_PUBLIC_KEY_DESCRIPTION: &str = "PEM public key";
pub const PEM_PRIVATE_KEY_DESCRIPTION: &str = "PEM private key";
pub const PEM_CERTIFICATE_DESCRIPTION: &str = "PEM certificate";
pub fn pem_public_key_magic() -> Vec<Vec<u8>> {
vec![b"-----BEGIN PUBLIC KEY-----".to_vec()]
}
pub fn pem_private_key_magic() -> Vec<Vec<u8>> {
vec![
b"-----BEGIN PRIVATE KEY-----".to_vec(),
b"-----BEGIN EC PRIVATE KEY-----".to_vec(),
b"-----BEGIN RSA PRIVATE KEY-----".to_vec(),
b"-----BEGIN DSA PRIVATE KEY-----".to_vec(),
b"-----BEGIN OPENSSH PRIVATE KEY-----".to_vec(),
]
}
pub fn pem_certificate_magic() -> Vec<Vec<u8>> {
vec![b"-----BEGIN CERTIFICATE-----".to_vec()]
}
pub fn pem_parser(file_data: &[u8], offset: usize) -> Result<SignatureResult, SignatureError> {
const MIN_PEM_LEN: usize = 26;
let mut result = SignatureResult {
offset,
confidence: CONFIDENCE_HIGH,
..Default::default()
};
let mut public_magics: Vec<Vec<u8>> = vec![];
let mut private_magics: Vec<Vec<u8>> = vec![];
let mut certificate_magics: Vec<Vec<u8>> = vec![];
for public_magic in pem_public_key_magic() {
public_magics.push(public_magic[0..MIN_PEM_LEN].to_vec());
}
for private_magic in pem_private_key_magic() {
private_magics.push(private_magic[0..MIN_PEM_LEN].to_vec());
}
for cert_magic in pem_certificate_magic() {
certificate_magics.push(cert_magic[0..MIN_PEM_LEN].to_vec());
}
if let Some(pem_magic) = file_data.get(offset..offset + MIN_PEM_LEN) {
if public_magics.contains(&pem_magic.to_vec()) {
result.description = PEM_PUBLIC_KEY_DESCRIPTION.to_string();
} else if private_magics.contains(&pem_magic.to_vec()) {
result.description = PEM_PRIVATE_KEY_DESCRIPTION.to_string();
} else if certificate_magics.contains(&pem_magic.to_vec()) {
result.description = PEM_CERTIFICATE_DESCRIPTION.to_string();
} else {
return Err(SignatureError);
}
let dry_run = pem::pem_carver(file_data, offset, None, None);
if dry_run.success {
if let Some(pem_size) = dry_run.size {
if decode_pem_data(&file_data[offset..offset + pem_size]).is_ok() {
if offset == 0 && pem_size == file_data.len() {
result.extraction_declined = true;
}
result.size = pem_size;
return Ok(result);
}
}
}
}
Err(SignatureError)
}
fn decode_pem_data(pem_file_data: &[u8]) -> Result<usize, SignatureError> {
const DELIM: &str = "--";
if let Ok(pem_file_string) = String::from_utf8(pem_file_data.to_vec()) {
let mut delim_count: usize = 0;
let mut base64_string: String = "".to_string();
for line in pem_file_string.lines() {
if line.starts_with(DELIM) {
delim_count += 1;
if delim_count == 2 {
break;
} else {
continue;
}
}
base64_string.push_str(line);
}
if !base64_string.is_empty() {
if let Ok(decoded_data) = BASE64_STANDARD.decode(&base64_string) {
return Ok(decoded_data.len());
}
}
}
Err(SignatureError)
}