Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

pkcs5/pbes2/kdf/
salt.rs

1//! Stack-allocated salt storage buffer which works on `no_alloc` targets.
2// TODO(tarcieri): replace this with an `ArrayVec`-like type?
3
4use core::fmt;
5use der::{DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag, Writer};
6
7/// Salt as used by the PBES2 KDF.
8#[derive(Clone, Copy, Eq, PartialEq)]
9pub struct Salt {
10    inner: [u8; Self::MAX_LEN],
11    length: Length,
12}
13
14impl Salt {
15    /// Maximum length of a salt that can be stored.
16    pub const MAX_LEN: usize = 32;
17
18    /// Create a new salt from the given byte slice.
19    ///
20    /// # Errors
21    /// Returns [`Error`] in the event the length of `slice` exceeds [`Salt::MAX_LEN`].
22    pub fn new(slice: impl AsRef<[u8]>) -> Result<Self> {
23        let slice = slice.as_ref();
24        let length = Length::new(u32::try_from(slice.len()).map_err(|_| Self::TAG.length_error())?);
25
26        let mut inner = [0u8; Self::MAX_LEN];
27        let mut i = 0;
28
29        while i < slice.len() {
30            inner[i] = slice[i];
31            i += 1;
32        }
33
34        Ok(Self { inner, length })
35    }
36
37    /// Borrow the salt data as a byte slice.
38    #[must_use]
39    #[allow(clippy::missing_panics_doc, reason = "invariant should hold")]
40    pub fn as_bytes(&self) -> &[u8] {
41        let length = usize::try_from(self.length).expect("should be less than Self::MAX_LEN");
42        &self.inner[..length]
43    }
44
45    /// Get the length of the salt data.
46    #[must_use]
47    pub fn len(&self) -> Length {
48        self.length
49    }
50}
51
52impl AsRef<[u8]> for Salt {
53    fn as_ref(&self) -> &[u8] {
54        self.as_bytes()
55    }
56}
57
58impl<'a> DecodeValue<'a> for Salt {
59    type Error = Error;
60
61    fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> Result<Self> {
62        let length = usize::try_from(header.length())?;
63
64        if length > Self::MAX_LEN {
65            return Err(reader.error(Self::TAG.length_error()));
66        }
67
68        let mut inner = [0u8; Self::MAX_LEN];
69        reader.read_into(&mut inner[..length])?;
70
71        Ok(Self {
72            inner,
73            length: header.length(),
74        })
75    }
76}
77
78impl EncodeValue for Salt {
79    fn value_len(&self) -> Result<Length> {
80        Ok(self.length)
81    }
82
83    fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
84        writer.write(self.as_bytes())
85    }
86}
87
88impl FixedTag for Salt {
89    const TAG: Tag = Tag::OctetString;
90}
91
92impl TryFrom<&[u8]> for Salt {
93    type Error = Error;
94
95    fn try_from(slice: &[u8]) -> Result<Salt> {
96        Self::new(slice)
97    }
98}
99
100impl fmt::Debug for Salt {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        f.debug_tuple("Salt").field(&self.as_bytes()).finish()
103    }
104}