use crate::{Error, Result};
use alloc::{borrow::ToOwned, string::String, vec::Vec};
use base64ct::{Base64, Encoding};
use core::str;
use zeroize::Zeroizing;
pub(crate) struct Boundary {
pre: &'static str,
post: &'static str,
}
#[cfg(feature = "pkcs5")]
pub(crate) const ENCRYPTED_PRIVATE_KEY_BOUNDARY: Boundary = Boundary {
pre: "-----BEGIN ENCRYPTED PRIVATE KEY-----\n",
post: "\n-----END ENCRYPTED PRIVATE KEY-----",
};
pub(crate) const PRIVATE_KEY_BOUNDARY: Boundary = Boundary {
pre: "-----BEGIN PRIVATE KEY-----\n",
post: "\n-----END PRIVATE KEY-----",
};
pub(crate) const PUBLIC_KEY_BOUNDARY: Boundary = Boundary {
pre: "-----BEGIN PUBLIC KEY-----\n",
post: "\n-----END PUBLIC KEY-----",
};
const CHUNK_SIZE: usize = 64;
pub(crate) fn decode(s: &str, boundary: Boundary) -> Result<Zeroizing<Vec<u8>>> {
let s = s.trim_end();
let s = s.strip_prefix(boundary.pre).ok_or(Error::Decode)?;
let s = s.strip_suffix(boundary.post).ok_or(Error::Decode)?;
let mut s = Zeroizing::new(s.to_owned());
s.retain(|c| !c.is_whitespace());
Base64::decode_vec(&*s)
.map(Zeroizing::new)
.map_err(|_| Error::Decode)
}
pub(crate) fn encode(data: &[u8], boundary: Boundary) -> String {
let mut output = String::new();
output.push_str(boundary.pre);
let b64 = Zeroizing::new(Base64::encode_string(data));
let chunks = b64.as_bytes().chunks(CHUNK_SIZE);
let nchunks = chunks.len();
for (i, chunk) in chunks.enumerate() {
let line = str::from_utf8(chunk).expect("malformed Base64");
output.push_str(line);
if i < nchunks.checked_sub(1).expect("unexpected Base64 chunks") {
output.push('\n');
} else if line.len() % 4 != 0 {
for _ in 0..(4 - (line.len() % 4)) {
output.push('=');
}
}
}
output.push_str(boundary.post);
output
}