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}