Skip to main content

stateset_crypto/
encoding.rs

1use crate::CryptoError;
2
3/// Encode u32 as big-endian bytes
4#[must_use]
5pub const fn u32_be(n: u32) -> [u8; 4] {
6    n.to_be_bytes()
7}
8
9/// Encode u64 as big-endian bytes
10#[must_use]
11pub const fn u64_be(n: u64) -> [u8; 8] {
12    n.to_be_bytes()
13}
14
15/// Encode string with length prefix (VES `ENC_STR`)
16#[must_use]
17pub fn encode_string(s: &str) -> Vec<u8> {
18    let bytes = s.as_bytes();
19    let mut result = Vec::with_capacity(4 + bytes.len());
20    result.extend_from_slice(&u32_be(bytes.len() as u32));
21    result.extend_from_slice(bytes);
22    result
23}
24
25/// Convert UUID string to 16-byte array
26///
27/// # Errors
28///
29/// Returns [`CryptoError::InvalidUuid`] if the UUID string is not a valid
30/// 32-hex-digit UUID (with or without dashes).
31pub fn uuid_to_bytes(uuid: &str) -> Result<[u8; 16], CryptoError> {
32    let hex_str: String = uuid.chars().filter(|c| *c != '-').collect();
33    if hex_str.len() != 32 {
34        return Err(CryptoError::InvalidUuid(uuid.to_string()));
35    }
36    let mut bytes = [0u8; 16];
37    hex::decode_to_slice(&hex_str, &mut bytes)
38        .map_err(|_| CryptoError::InvalidUuid(uuid.to_string()))?;
39    Ok(bytes)
40}
41
42/// Convert hex string (with or without 0x prefix) to bytes
43///
44/// # Errors
45///
46/// Returns [`CryptoError::InvalidHex`] if the hex string is invalid.
47pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>, CryptoError> {
48    let hex_str = hex.strip_prefix("0x").unwrap_or(hex);
49    hex::decode(hex_str).map_err(|e| CryptoError::InvalidHex(e.to_string()))
50}
51
52/// Convert bytes to hex string with 0x prefix
53#[must_use]
54pub fn bytes_to_hex(bytes: &[u8]) -> String {
55    format!("0x{}", hex::encode(bytes))
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn u32_be_zero() {
64        assert_eq!(u32_be(0), [0, 0, 0, 0]);
65    }
66
67    #[test]
68    fn u32_be_one() {
69        assert_eq!(u32_be(1), [0, 0, 0, 1]);
70    }
71
72    #[test]
73    fn u32_be_max() {
74        assert_eq!(u32_be(u32::MAX), [0xFF, 0xFF, 0xFF, 0xFF]);
75    }
76
77    #[test]
78    fn u64_be_zero() {
79        assert_eq!(u64_be(0), [0, 0, 0, 0, 0, 0, 0, 0]);
80    }
81
82    #[test]
83    fn u64_be_one() {
84        assert_eq!(u64_be(1), [0, 0, 0, 0, 0, 0, 0, 1]);
85    }
86
87    #[test]
88    fn encode_string_empty() {
89        assert_eq!(encode_string(""), vec![0, 0, 0, 0]);
90    }
91
92    #[test]
93    fn encode_string_hello() {
94        let result = encode_string("hello");
95        assert_eq!(&result[..4], &[0, 0, 0, 5]);
96        assert_eq!(&result[4..], b"hello");
97    }
98
99    #[test]
100    fn uuid_to_bytes_valid() {
101        let bytes = uuid_to_bytes("550e8400-e29b-41d4-a716-446655440000").unwrap();
102        assert_eq!(bytes[0], 0x55);
103        assert_eq!(bytes.len(), 16);
104    }
105
106    #[test]
107    fn uuid_to_bytes_invalid_length() {
108        assert!(uuid_to_bytes("not-a-uuid").is_err());
109    }
110
111    #[test]
112    fn hex_to_bytes_with_prefix() {
113        let bytes = hex_to_bytes("0xdeadbeef").unwrap();
114        assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
115    }
116
117    #[test]
118    fn hex_to_bytes_without_prefix() {
119        let bytes = hex_to_bytes("deadbeef").unwrap();
120        assert_eq!(bytes, vec![0xDE, 0xAD, 0xBE, 0xEF]);
121    }
122
123    #[test]
124    fn bytes_to_hex_roundtrip() {
125        let original = vec![0xDE, 0xAD, 0xBE, 0xEF];
126        let hex_str = bytes_to_hex(&original);
127        assert_eq!(hex_str, "0xdeadbeef");
128        let back = hex_to_bytes(&hex_str).unwrap();
129        assert_eq!(back, original);
130    }
131}