otp_std/secret/
length.rs

1//! Secret lengths.
2
3use const_macros::{const_ok, const_try};
4
5#[cfg(not(feature = "unsafe-length"))]
6use const_macros::const_early;
7
8use miette::Diagnostic;
9
10#[cfg(feature = "serde")]
11use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
12
13use thiserror::Error;
14
15#[cfg(not(feature = "unsafe-length"))]
16use crate::macros::errors;
17
18use crate::algorithm::Algorithm;
19
20/// The default (and recommended) secret length.
21pub const DEFAULT: usize = 20;
22
23/// The minimum allowed secret length.
24#[cfg(not(feature = "unsafe-length"))]
25pub const MIN: usize = 16;
26
27/// Represents errors returned when unsafe lengths are used.
28#[cfg(not(feature = "unsafe-length"))]
29#[derive(Debug, Error, Diagnostic)]
30#[error("expected length of at least `{MIN}`, got `{length}`")]
31#[diagnostic(
32    code(otp_std::secret::length),
33    help("make sure the secret length is at least `{MIN}`")
34)]
35pub struct Error {
36    /// The unsafe length.
37    pub length: usize,
38}
39
40#[cfg(not(feature = "unsafe-length"))]
41impl Error {
42    /// Constructs [`Self`].
43    pub const fn new(length: usize) -> Self {
44        Self { length }
45    }
46}
47
48/// Represents the absence of errors returned when the `unsafe-length` feature is enabled.
49#[cfg(feature = "unsafe-length")]
50#[derive(Debug, Error, Diagnostic)]
51pub enum Error {}
52
53/// Represents OTP secret lengths.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
55pub struct Length {
56    value: usize,
57}
58
59#[cfg(feature = "serde")]
60impl Serialize for Length {
61    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
62        self.get().serialize(serializer)
63    }
64}
65
66#[cfg(feature = "serde")]
67impl<'de> Deserialize<'de> for Length {
68    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
69        let value = usize::deserialize(deserializer)?;
70
71        Self::new(value).map_err(de::Error::custom)
72    }
73}
74
75impl TryFrom<usize> for Length {
76    type Error = Error;
77
78    fn try_from(value: usize) -> Result<Self, Self::Error> {
79        Self::new(value)
80    }
81}
82
83impl From<Length> for usize {
84    fn from(length: Length) -> Self {
85        length.get()
86    }
87}
88
89impl Default for Length {
90    fn default() -> Self {
91        Self::DEFAULT
92    }
93}
94
95#[cfg(not(feature = "unsafe-length"))]
96errors! {
97    Type = Error,
98    Hack = $,
99    error => new(length),
100}
101
102impl Length {
103    /// Constructs [`Self`], if possible.
104    ///
105    /// # Errors
106    ///
107    /// See [`check`] for more information.
108    ///
109    /// [`check`]: Self::check
110    pub const fn new(value: usize) -> Result<Self, Error> {
111        const_try!(Self::check(value));
112
113        // SAFETY: the value is in the valid range for `Self`
114        Ok(unsafe { Self::new_unchecked(value) })
115    }
116
117    /// Similar to [`new`], but the error is discarded.
118    ///
119    /// [`new`]: Self::new
120    pub const fn new_ok(value: usize) -> Option<Self> {
121        const_ok!(Self::new(value))
122    }
123
124    /// Checks if the provided value is valid for [`Self`].
125    ///
126    /// # Errors
127    ///
128    /// This function never fails when the `unsafe-length` feature is enabled.
129    /// Otherwise, it returns an error containing the unsafe value provided.
130    #[allow(unused_variables)]
131    pub const fn check(value: usize) -> Result<(), Error> {
132        #[cfg(not(feature = "unsafe-length"))]
133        const_early!(value < MIN => error!(value));
134
135        Ok(())
136    }
137
138    /// Constructs [`Self`] without checking the length.
139    ///
140    /// # Safety
141    ///
142    /// The caller must ensure that the given value is valid for [`Self`].
143    ///
144    /// This invariant can be checked using [`check`].
145    ///
146    /// [`check`]: Self::check
147    pub const unsafe fn new_unchecked(value: usize) -> Self {
148        Self { value }
149    }
150
151    /// Returns the recommended length for the given [`Algorithm`].
152    pub const fn recommended_for(algorithm: Algorithm) -> Self {
153        // SAFETY: the length is known to be valid for `Self`
154        // regardless of the `unsafe-length` feature.
155        unsafe { Self::new_unchecked(algorithm.recommended_length()) }
156    }
157
158    /// Returns the length value.
159    pub const fn get(self) -> usize {
160        self.value
161    }
162
163    /// The minimum [`Self`] value.
164    #[cfg(not(feature = "unsafe-length"))]
165    pub const MIN: Self = Self::new_ok(MIN).unwrap();
166
167    /// The default [`Self`] value.
168    pub const DEFAULT: Self = Self::new_ok(DEFAULT).unwrap();
169}