Skip to main content

defuse_crypto/
fmt.rs

1/// Helper trait to encode/decode bytes as `<CURVE>:<base58>`
2pub trait TypedCurve {
3    /// Used as prefix: `<CURVE>:...`
4    const CURVE_TYPE: &str;
5
6    /// Encodes bytes to string as `<CURVE>:<base58>`
7    #[inline]
8    fn to_base58(bytes: impl AsRef<[u8]>) -> String {
9        format!(
10            "{}:{}",
11            Self::CURVE_TYPE,
12            bs58::encode(bytes.as_ref()).into_string()
13        )
14    }
15
16    /// Decodes bytes from string as `<CURVE>:<base58>`
17    fn parse_base58<const N: usize>(s: impl AsRef<str>) -> Result<[u8; N], ParseCurveError> {
18        let s = s.as_ref();
19        let data = if let Some((curve, data)) = s.split_once(':') {
20            if !curve.eq_ignore_ascii_case(Self::CURVE_TYPE) {
21                return Err(ParseCurveError::WrongCurveType);
22            }
23            data
24        } else {
25            s
26        };
27        checked_base58_decode_array(data)
28    }
29}
30
31/// An error returned from [`TypedCurve::parse_base58`]
32#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
33pub enum ParseCurveError {
34    #[error("wrong curve type")]
35    WrongCurveType,
36    #[error("base58: {0}")]
37    Base58(#[from] bs58::decode::Error),
38    #[error("invalid length")]
39    InvalidLength,
40}
41
42/// Base-58 decode bytes array of a an exact size.
43/// If decoded length doesn't match `N`, an error will be returned.
44///
45/// # Examples
46///
47/// ```rust
48/// # use defuse_crypto::fmt::{checked_base58_decode_array, ParseCurveError};
49/// # use hex_literal::hex;
50/// assert_eq!(
51///     checked_base58_decode_array::<8>("he11owor1d")?,
52///     hex!("04305e2b2473f058"),
53/// );
54/// checked_base58_decode_array::<7>("he11owor1d").expect_err("buffer too small");
55/// checked_base58_decode_array::<9>("he11owor1d").expect_err("buffer too large");
56/// # Ok::<(), ParseCurveError>(())
57/// ```
58#[inline]
59pub fn checked_base58_decode_array<const N: usize>(
60    input: impl AsRef<[u8]>,
61) -> Result<[u8; N], ParseCurveError> {
62    let mut output = [0u8; N];
63    let n = bs58::decode(input.as_ref())
64        // NOTE: `.into_array_const()` doesn't return an error on insufficient
65        // input length and pads the array with zeros
66        .onto(&mut output)?;
67    if n != N {
68        return Err(ParseCurveError::InvalidLength);
69    }
70    Ok(output)
71}