Skip to main content

iroh_http_core/
encoding.rs

1//! Base32 encoding utilities and node-id parsing.
2//!
3//! All base32 uses lowercase RFC 4648 without padding, the canonical form for
4//! node identifiers throughout the crate.
5
6use crate::CoreError;
7
8/// Encode bytes as lowercase RFC 4648 base32 (no padding).
9pub fn base32_encode(bytes: &[u8]) -> String {
10    base32::encode(base32::Alphabet::Rfc4648Lower { padding: false }, bytes)
11}
12
13/// Decode an RFC 4648 base32 string (no padding, case-insensitive) to bytes.
14pub(crate) fn base32_decode(s: &str) -> Result<Vec<u8>, String> {
15    base32::decode(base32::Alphabet::Rfc4648Lower { padding: false }, s)
16        .ok_or_else(|| format!("invalid base32 string: {s}"))
17}
18
19/// Parse a base32 node-id string into an `iroh::PublicKey`.
20pub(crate) fn parse_node_id(s: &str) -> Result<iroh::PublicKey, CoreError> {
21    let bytes = base32_decode(s).map_err(CoreError::invalid_input)?;
22    let arr: [u8; 32] = bytes
23        .try_into()
24        .map_err(|_| CoreError::invalid_input("node-id must be 32 bytes"))?;
25    iroh::PublicKey::from_bytes(&arr).map_err(|e| CoreError::invalid_input(e.to_string()))
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn base32_round_trip() {
34        let original: Vec<u8> = (0..32).collect();
35        let encoded = base32_encode(&original);
36        let decoded = base32_decode(&encoded).unwrap();
37        assert_eq!(decoded, original);
38    }
39
40    #[test]
41    fn base32_empty() {
42        let encoded = base32_encode(&[]);
43        assert_eq!(encoded, "");
44        let decoded = base32_decode("").unwrap();
45        assert!(decoded.is_empty());
46    }
47
48    #[test]
49    fn base32_decode_invalid_char() {
50        let result = base32_decode("!!!invalid!!!");
51        assert!(result.is_err());
52    }
53
54    #[test]
55    fn parse_node_id_invalid_base32() {
56        let result = parse_node_id("!!!not-base32!!!");
57        assert!(result.is_err());
58    }
59
60    #[test]
61    fn parse_node_id_wrong_length() {
62        let result = parse_node_id("aa");
63        assert!(result.is_err());
64    }
65}