1use core::fmt;
5use der::{DecodeValue, EncodeValue, Error, FixedTag, Header, Length, Reader, Result, Tag, Writer};
6
7#[derive(Clone, Copy, Eq, PartialEq)]
9pub struct Salt {
10 inner: [u8; Self::MAX_LEN],
11 length: Length,
12}
13
14impl Salt {
15 pub const MAX_LEN: usize = 32;
17
18 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 #[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 #[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}