use crate::{
Alphabet, BCRYPT_NO_PAD, Bcrypt, CRYPT_NO_PAD, Crypt, DecodeError, DecodedBuffer, EncodeError,
EncodedBuffer, Engine, LineEnding, LineWrap, STANDARD, Standard, checked_encoded_len,
checked_wrapped_encoded_len, encoded_len, wrapped_encoded_len,
};
#[cfg(feature = "alloc")]
use crate::SecretBuffer;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Profile<A, const PAD: bool> {
engine: Engine<A, PAD>,
wrap: Option<LineWrap>,
}
impl<A, const PAD: bool> Profile<A, PAD>
where
A: Alphabet,
{
#[must_use]
pub const fn new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Self {
Self { engine, wrap }
}
#[must_use]
pub const fn checked_new(engine: Engine<A, PAD>, wrap: Option<LineWrap>) -> Option<Self> {
match wrap {
Some(wrap) if !wrap.is_valid() => None,
_ => Some(Self::new(engine, wrap)),
}
}
#[must_use]
pub const fn is_valid(&self) -> bool {
match self.wrap {
Some(wrap) => wrap.is_valid(),
None => true,
}
}
#[must_use]
pub const fn engine(&self) -> Engine<A, PAD> {
self.engine
}
#[must_use]
pub const fn is_padded(&self) -> bool {
PAD
}
#[must_use]
pub const fn is_wrapped(&self) -> bool {
self.wrap.is_some()
}
#[must_use]
pub const fn line_wrap(&self) -> Option<LineWrap> {
self.wrap
}
#[must_use]
pub const fn line_len(&self) -> Option<usize> {
match self.wrap {
Some(wrap) => Some(wrap.line_len()),
None => None,
}
}
#[must_use]
pub const fn line_ending(&self) -> Option<LineEnding> {
match self.wrap {
Some(wrap) => Some(wrap.line_ending()),
None => None,
}
}
pub const fn encoded_len(&self, input_len: usize) -> Result<usize, EncodeError> {
match self.wrap {
Some(wrap) => wrapped_encoded_len(input_len, PAD, wrap),
None => encoded_len(input_len, PAD),
}
}
#[must_use]
pub const fn checked_encoded_len(&self, input_len: usize) -> Option<usize> {
match self.wrap {
Some(wrap) => checked_wrapped_encoded_len(input_len, PAD, wrap),
None => checked_encoded_len(input_len, PAD),
}
}
pub fn decoded_len(&self, input: &[u8]) -> Result<usize, DecodeError> {
match self.wrap {
Some(wrap) => self.engine.decoded_len_wrapped(input, wrap),
None => self.engine.decoded_len(input),
}
}
pub fn validate_result(&self, input: &[u8]) -> Result<(), DecodeError> {
match self.wrap {
Some(wrap) => self.engine.validate_wrapped_result(input, wrap),
None => self.engine.validate_result(input),
}
}
#[must_use]
pub fn validate(&self, input: &[u8]) -> bool {
self.validate_result(input).is_ok()
}
pub fn encode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, EncodeError> {
match self.wrap {
Some(wrap) => self.engine.encode_slice_wrapped(input, output, wrap),
None => self.engine.encode_slice(input, output),
}
}
pub fn encode_slice_clear_tail(
&self,
input: &[u8],
output: &mut [u8],
) -> Result<usize, EncodeError> {
match self.wrap {
Some(wrap) => self
.engine
.encode_slice_wrapped_clear_tail(input, output, wrap),
None => self.engine.encode_slice_clear_tail(input, output),
}
}
pub fn encode_buffer<const CAP: usize>(
&self,
input: &[u8],
) -> Result<EncodedBuffer<CAP>, EncodeError> {
let mut output = EncodedBuffer::new();
let written = match self.encode_slice_clear_tail(input, output.as_mut_capacity()) {
Ok(written) => written,
Err(err) => {
output.clear();
return Err(err);
}
};
output.set_filled(written)?;
Ok(output)
}
#[must_use = "handle decode errors; use crate::ct for secret-bearing payloads"]
pub fn decode_slice(&self, input: &[u8], output: &mut [u8]) -> Result<usize, DecodeError> {
match self.wrap {
Some(wrap) => self.engine.decode_slice_wrapped(input, output, wrap),
None => self.engine.decode_slice(input, output),
}
}
pub fn decode_slice_clear_tail(
&self,
input: &[u8],
output: &mut [u8],
) -> Result<usize, DecodeError> {
match self.wrap {
Some(wrap) => self
.engine
.decode_slice_wrapped_clear_tail(input, output, wrap),
None => self.engine.decode_slice_clear_tail(input, output),
}
}
pub fn decode_buffer<const CAP: usize>(
&self,
input: &[u8],
) -> Result<DecodedBuffer<CAP>, DecodeError> {
let mut output = DecodedBuffer::new();
let written = match self.decode_slice_clear_tail(input, output.as_mut_capacity()) {
Ok(written) => written,
Err(err) => {
output.clear();
return Err(err);
}
};
output.set_filled(written)?;
Ok(output)
}
pub fn decode_in_place<'a>(&self, buffer: &'a mut [u8]) -> Result<&'a mut [u8], DecodeError> {
match self.wrap {
Some(wrap) => self.engine.decode_in_place_wrapped(buffer, wrap),
None => self.engine.decode_in_place(buffer),
}
}
pub fn decode_in_place_clear_tail<'a>(
&self,
buffer: &'a mut [u8],
) -> Result<&'a mut [u8], DecodeError> {
match self.wrap {
Some(wrap) => self.engine.decode_in_place_wrapped_clear_tail(buffer, wrap),
None => self.engine.decode_in_place_clear_tail(buffer),
}
}
#[cfg(feature = "alloc")]
#[must_use = "for secret-bearing payloads use encode_secret, which returns a redacted buffer with drop-time cleanup"]
pub fn encode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, EncodeError> {
match self.wrap {
Some(wrap) => self.engine.encode_wrapped_vec(input, wrap),
None => self.engine.encode_vec(input),
}
}
#[cfg(feature = "alloc")]
pub fn encode_secret(&self, input: &[u8]) -> Result<SecretBuffer, EncodeError> {
self.encode_vec(input).map(SecretBuffer::from_vec)
}
#[cfg(feature = "alloc")]
pub fn encode_string(&self, input: &[u8]) -> Result<alloc::string::String, EncodeError> {
match self.wrap {
Some(wrap) => self.engine.encode_wrapped_string(input, wrap),
None => self.engine.encode_string(input),
}
}
#[cfg(feature = "alloc")]
#[must_use = "for secret-bearing payloads use decode_secret, which returns a redacted buffer with drop-time cleanup"]
pub fn decode_vec(&self, input: &[u8]) -> Result<alloc::vec::Vec<u8>, DecodeError> {
match self.wrap {
Some(wrap) => self.engine.decode_wrapped_vec(input, wrap),
None => self.engine.decode_vec(input),
}
}
#[cfg(feature = "alloc")]
pub fn decode_secret(&self, input: &[u8]) -> Result<SecretBuffer, DecodeError> {
self.decode_vec(input).map(SecretBuffer::from_vec)
}
}
impl<A, const PAD: bool> Default for Profile<A, PAD>
where
A: Alphabet,
{
fn default() -> Self {
Self::new(Engine::new(), None)
}
}
impl<A, const PAD: bool> core::fmt::Display for Profile<A, PAD>
where
A: Alphabet,
{
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self.wrap {
Some(wrap) => write!(formatter, "padded={PAD} wrap={wrap}"),
None => write!(formatter, "padded={PAD} wrap=none"),
}
}
}
impl<A, const PAD: bool> From<Engine<A, PAD>> for Profile<A, PAD>
where
A: Alphabet,
{
fn from(engine: Engine<A, PAD>) -> Self {
Self::new(engine, None)
}
}
#[doc(alias = "ct")]
#[doc(alias = "constant_time")]
#[doc(alias = "sensitive")]
pub const MIME: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::MIME));
#[doc(alias = "ct")]
#[doc(alias = "constant_time")]
#[doc(alias = "sensitive")]
pub const PEM: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM));
#[doc(alias = "ct")]
#[doc(alias = "constant_time")]
#[doc(alias = "sensitive")]
pub const PEM_CRLF: Profile<Standard, true> = Profile::new(STANDARD, Some(LineWrap::PEM_CRLF));
#[doc(alias = "ct")]
#[doc(alias = "constant_time")]
#[doc(alias = "sensitive")]
pub const BCRYPT: Profile<Bcrypt, false> = Profile::new(BCRYPT_NO_PAD, None);
#[doc(alias = "ct")]
#[doc(alias = "constant_time")]
#[doc(alias = "sensitive")]
pub const CRYPT: Profile<Crypt, false> = Profile::new(CRYPT_NO_PAD, None);