g60/
verification.rs

1use crate::decoding::{compute_chunk, compute_decoded_size};
2use crate::errors::VerificationError;
3
4/// Verifies `content` is a valid G60 encoded string.
5///
6/// # Errors
7/// An error will be thrown in the following cases:
8/// - if `encoded` is not a valid G60 encoded string.
9/// - if `encoded` is not canonical.
10pub fn verify(encoded: &str) -> Result<(), VerificationError> {
11    let bytes = encoded.as_bytes();
12
13    // Check length.
14    let last_group_length = bytes.len() - bytes.len() / 11 * 11;
15    if let 1 | 4 | 8 = last_group_length {
16        return Err(VerificationError::InvalidLength);
17    }
18
19    // Complete groups.
20    let mut chunk_index = 0;
21    for chunk in bytes.chunks_exact(11) {
22        compute_chunk(chunk_index, chunk)?;
23        chunk_index += 11;
24    }
25
26    // Last incomplete group.
27    if last_group_length != 0 {
28        let chunk = &bytes[bytes.len() - last_group_length..];
29        let decoded = compute_chunk(chunk_index, chunk)?;
30        let elements_to_write = compute_decoded_size(last_group_length);
31
32        if decoded[elements_to_write..].iter().any(|v| *v != 0) {
33            return Err(VerificationError::NotCanonical);
34        }
35    }
36
37    Ok(())
38}
39
40// ----------------------------------------------------------------------------
41// TESTS ----------------------------------------------------------------------
42// ----------------------------------------------------------------------------
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::encode;
48
49    #[test]
50    fn test_verify_ok() {
51        for length in 0..16 {
52            for byte in 0..=255 {
53                let bytes = vec![byte; length];
54                let encoded = encode(&bytes);
55                verify(&encoded).expect("The verification must succeed");
56            }
57        }
58    }
59
60    #[test]
61    fn test_verify_invalid_length() {
62        let test = "JKLMNPQRSTUx";
63        let error = verify(test).expect_err("The verification must fail");
64
65        assert_eq!(
66            error,
67            VerificationError::InvalidLength,
68            "Incorrect for '{}'",
69            test
70        );
71
72        // --------------------------------------------------------------------
73
74        let test = "JKLMNPQRSTUxxxx";
75        let error = verify(test).expect_err("The verification must fail");
76
77        assert_eq!(
78            error,
79            VerificationError::InvalidLength,
80            "Incorrect for '{}'",
81            test
82        );
83
84        // --------------------------------------------------------------------
85
86        let test = "JKLMNPQRSTUxxxxxxxx";
87        let error = verify(test).expect_err("The verification must fail");
88
89        assert_eq!(
90            error,
91            VerificationError::InvalidLength,
92            "Incorrect for '{}'",
93            test
94        );
95    }
96
97    #[test]
98    fn test_verify_invalid_characters() {
99        let test = "Hello, world!";
100        let error = verify(test).expect_err("The verification must fail");
101
102        assert_eq!(
103            error,
104            VerificationError::InvalidByte {
105                index: 5,
106                byte: b',',
107            },
108            "Incorrect for '{}'",
109            test
110        );
111
112        // --------------------------------------------------------------------
113
114        let test = "THIS IS A TEST";
115        let error = verify(test).expect_err("The verification must fail");
116
117        assert_eq!(
118            error,
119            VerificationError::InvalidByte {
120                index: 2,
121                byte: b'I',
122            },
123            "Incorrect for '{}'",
124            test
125        );
126
127        // --------------------------------------------------------------------
128
129        let test = "TESTONTEST";
130        let error = verify(test).expect_err("The verification must fail");
131
132        assert_eq!(
133            error,
134            VerificationError::InvalidByte {
135                index: 4,
136                byte: b'O',
137            },
138            "Incorrect for '{}'",
139            test
140        );
141    }
142
143    #[test]
144    fn test_not_canonical() {
145        for i in ["0f", "2F", "5y", "BU", "Gv", "Nr", "Xd"] {
146            assert_eq!(
147                verify(i),
148                Err(VerificationError::NotCanonical),
149                "Incorrect for '{}'",
150                i
151            );
152        }
153    }
154}