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