use crate::detect::Version;
use crate::secret::Secret;
use crate::zeroize::Zeroize;
use crate::{kdf, random, Error};
use core::fmt;
use core::num::NonZeroU32;
pub struct Password {
inner: PasswordInner,
}
enum PasswordInner {
Text(Secret<String>),
Raw(Secret<Vec<u8>>),
}
impl Password {
pub fn new(text: &str) -> Result<Self, Error> {
if text.is_empty() {
return Err(Error::EmptyPassword);
}
Ok(Password { inner: PasswordInner::Text(Secret::new(text.to_owned())) })
}
pub fn from_raw_bytes(bytes: impl Into<Vec<u8>>) -> Self {
Password { inner: PasswordInner::Raw(Secret::new(bytes.into())) }
}
pub fn is_raw(&self) -> bool {
matches!(self.inner, PasswordInner::Raw(_))
}
pub fn encoded(&self, version: Version) -> Secret<Vec<u8>> {
Secret::new(match &self.inner {
PasswordInner::Text(text) if version >= Version::V3 => text.expose_secret().as_bytes().to_vec(),
PasswordInner::Text(text) => kdf::utf16le(text.expose_secret()),
PasswordInner::Raw(raw) => raw.expose_secret().clone(),
})
}
pub fn clone_secret(&self) -> Self {
Password {
inner: match &self.inner {
PasswordInner::Text(t) => PasswordInner::Text(t.clone_secret()),
PasswordInner::Raw(r) => PasswordInner::Raw(r.clone_secret()),
},
}
}
}
impl fmt::Debug for Password {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(if self.is_raw() { "Password::Raw([REDACTED])" } else { "Password::Text([REDACTED])" })
}
}
fn to_array<const N: usize>(bytes: &[u8], err: fn(usize) -> Error) -> Result<[u8; N], Error> {
bytes.try_into().map_err(|_| err(bytes.len()))
}
macro_rules! iv_type {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct $name([u8; 16]);
impl $name {
pub fn generate() -> Result<Self, Error> {
Ok($name(random::bytes()?))
}
pub fn as_bytes(&self) -> &[u8; 16] {
&self.0
}
}
impl From<[u8; 16]> for $name {
fn from(bytes: [u8; 16]) -> Self {
$name(bytes)
}
}
impl TryFrom<&[u8]> for $name {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
Ok($name(to_array(bytes, Error::InvalidIvLength)?))
}
}
impl fmt::Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($name), "("))?;
for b in &self.0 {
write!(f, "{:02x}", b)?;
}
f.write_str(")")
}
}
};
}
iv_type!(
PublicIv
);
iv_type!(
SessionIv
);
macro_rules! key_type {
($(#[$doc:meta])* $name:ident) => {
$(#[$doc])*
#[derive(Debug, PartialEq, Eq)]
pub struct $name(Secret<[u8; 32]>);
impl $name {
pub fn generate() -> Result<Self, Error> {
Ok($name(Secret::new(random::bytes()?)))
}
pub fn expose_secret(&self) -> &[u8; 32] {
self.0.expose_secret()
}
pub fn clone_secret(&self) -> Self {
$name(self.0.clone_secret())
}
}
impl From<[u8; 32]> for $name {
fn from(mut bytes: [u8; 32]) -> Self {
let key = $name(Secret::new(bytes));
bytes.zeroize();
key
}
}
impl TryFrom<&[u8]> for $name {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Error> {
Ok($name(Secret::new(to_array(bytes, Error::InvalidKeyLength)?)))
}
}
};
}
key_type!(
SessionKey
);
key_type!(
DerivedKey
);
impl DerivedKey {
pub fn derive(version: Version, password: &Password, iv: &PublicIv, iterations: Iterations) -> Result<Self, Error> {
let encoded = password.encoded(version);
let key = if version >= Version::V3 {
kdf::pbkdf2_hmac_sha512_array(encoded.expose_secret(), iv.as_bytes(), iterations.get())?
} else {
kdf::aescrypt_legacy(encoded.expose_secret(), iv.as_bytes())
};
Ok(DerivedKey::from(key))
}
}
pub const DEFAULT_ITERATIONS: u32 = 600_000;
pub const MIN_ITERATIONS: u32 = 1;
pub const MAX_ITERATIONS: u32 = 5_000_000;
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Iterations(NonZeroU32);
impl Iterations {
pub const DEFAULT: Iterations = match NonZeroU32::new(DEFAULT_ITERATIONS) {
Some(n) => Iterations(n),
None => panic!("DEFAULT_ITERATIONS is not zero"),
};
pub fn new(iterations: u32) -> Result<Self, Error> {
if iterations > MAX_ITERATIONS {
return Err(Error::InvalidIterations(iterations));
}
Self::new_unbounded(iterations)
}
pub fn new_unbounded(iterations: u32) -> Result<Self, Error> {
NonZeroU32::new(iterations).map(Iterations).ok_or(Error::InvalidIterations(iterations))
}
pub fn get(self) -> u32 {
self.0.get()
}
}
impl Default for Iterations {
fn default() -> Self {
Iterations::DEFAULT
}
}
impl TryFrom<u32> for Iterations {
type Error = Error;
fn try_from(iterations: u32) -> Result<Self, Error> {
Iterations::new(iterations)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct Limits {
pub max_iterations: u32,
pub max_header_len: usize,
pub max_extensions: usize,
}
impl Limits {
pub const DEFAULT: Limits = Limits { max_iterations: MAX_ITERATIONS, max_header_len: 1 << 20, max_extensions: 256 };
pub fn max_iterations(mut self, max: u32) -> Self {
self.max_iterations = max;
self
}
pub fn max_header_len(mut self, max: usize) -> Self {
self.max_header_len = max;
self
}
pub fn max_extensions(mut self, max: usize) -> Self {
self.max_extensions = max;
self
}
pub(crate) fn at_most_default(self) -> Limits {
Limits {
max_iterations: self.max_iterations.min(Limits::DEFAULT.max_iterations),
max_header_len: self.max_header_len.min(Limits::DEFAULT.max_header_len),
max_extensions: self.max_extensions.min(Limits::DEFAULT.max_extensions),
}
}
}
impl Default for Limits {
fn default() -> Self {
Limits::DEFAULT
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn values_validate_lengths() {
assert!(PublicIv::try_from(&[0u8; 16][..]).is_ok());
assert!(matches!(PublicIv::try_from(&[0u8; 15][..]), Err(Error::InvalidIvLength(15))));
assert!(matches!(SessionIv::try_from(&[0u8; 17][..]), Err(Error::InvalidIvLength(17))));
assert!(matches!(SessionKey::try_from(&[0u8; 16][..]), Err(Error::InvalidKeyLength(16))));
assert!(matches!(DerivedKey::try_from(&[0u8; 33][..]), Err(Error::InvalidKeyLength(33))));
assert_ne!(PublicIv::generate().unwrap(), PublicIv::generate().unwrap());
}
#[test]
fn iterations_bounds() {
assert!(matches!(Iterations::new(0), Err(Error::InvalidIterations(0))));
assert_eq!(Iterations::new(1).unwrap().get(), 1);
assert_eq!(Iterations::new(MAX_ITERATIONS).unwrap().get(), MAX_ITERATIONS);
assert!(Iterations::new(MAX_ITERATIONS + 1).is_err());
assert_eq!(Iterations::new_unbounded(u32::MAX).unwrap().get(), u32::MAX);
assert!(Iterations::new_unbounded(0).is_err());
assert_eq!(Iterations::default().get(), DEFAULT_ITERATIONS);
}
#[test]
fn passwords() {
assert!(matches!(Password::new(""), Err(Error::EmptyPassword)));
let text = Password::new("é").unwrap();
assert_eq!(text.encoded(Version::V2).expose_secret(), &vec![0xE9, 0x00]);
assert_eq!(text.encoded(Version::V3).expose_secret(), &vec![0xC3, 0xA9]);
let raw = Password::from_raw_bytes(vec![0xFF]);
assert!(raw.is_raw());
assert_eq!(raw.encoded(Version::V0).expose_secret(), &vec![0xFF]);
assert_eq!(raw.encoded(Version::V3).expose_secret(), &vec![0xFF]);
assert_eq!(format!("{:?}", text), "Password::Text([REDACTED])");
assert_eq!(format!("{:?}", DerivedKey::from([1u8; 32])), "DerivedKey(Secret([REDACTED]))");
}
#[test]
fn limits_only_lower_by_default() {
let raised = Limits::DEFAULT.max_iterations(u32::MAX).max_extensions(10_000);
assert_eq!(raised.at_most_default(), Limits::DEFAULT);
let lowered = Limits::DEFAULT.max_iterations(10);
assert_eq!(lowered.at_most_default().max_iterations, 10);
}
}