Skip to main content

ps_hash_core/
constants.rs

1/// Size of the digest, in bytes.
2pub const DIGEST_SIZE: usize = 32;
3
4/// Size of the packed data-length field, in bytes.
5pub const SIZE_SIZE: usize = std::mem::size_of::<u16>();
6
7/// Number of byte errors the Reed-Solomon codec can correct.
8pub const PARITY: u8 = 7;
9
10/// Size of the Reed-Solomon parity block, in bytes.
11pub const PARITY_SIZE: usize = 2 * PARITY as usize;
12
13/// Offset of the parity block within the internal representation.
14pub const PARITY_OFFSET: usize = DIGEST_SIZE + SIZE_SIZE;
15
16/// Size of the internal representation, in bytes.
17///
18/// The layout is byte-aligned throughout:
19///
20/// | offset | size | content                                    |
21/// |--------|------|--------------------------------------------|
22/// | 0..32  | 32 B | digest                                     |
23/// | 32..34 | 2 B  | data length, as a [`ps_pint16::PackedInt`] |
24/// | 34..48 | 14 B | Reed-Solomon parity                        |
25pub const HASH_SIZE_BIN: usize = PARITY_OFFSET + PARITY_SIZE;
26
27/// Size of the truncated binary representation, in bytes.
28///
29/// The omitted trailing parity bytes are restored by [`Hash::validate`],
30/// which spends all but one byte of the correction budget: a compact hash
31/// still tolerates one corrupted byte.
32///
33/// [`Hash::validate`]: crate::Hash::validate
34pub const HASH_SIZE_COMPACT: usize = MIN_RECOVERABLE_BIN + 1;
35
36/// Size of the Crockford Base32 representation, in characters.
37pub const HASH_SIZE_CROCKFORD: usize = encoded_size(CROCKFORD_BITS);
38
39/// Size of the base64url representation, in characters.
40pub const HASH_SIZE_BASE64: usize = encoded_size(BASE64_BITS);
41
42/// Shortest binary input [`Hash::validate`] accepts.
43///
44/// [`Hash::validate`]: crate::Hash::validate
45pub const MIN_RECOVERABLE_BIN: usize = HASH_SIZE_BIN - PARITY as usize;
46
47/// Shortest Crockford Base32 input [`Hash::validate`] accepts.
48///
49/// [`Hash::validate`]: crate::Hash::validate
50pub const MIN_RECOVERABLE_CROCKFORD: usize =
51    HASH_SIZE_CROCKFORD - recoverable_truncation(CROCKFORD_BITS);
52
53/// Shortest base64url input [`Hash::validate`] accepts.
54///
55/// [`Hash::validate`]: crate::Hash::validate
56pub const MIN_RECOVERABLE_BASE64: usize = HASH_SIZE_BASE64 - recoverable_truncation(BASE64_BITS);
57
58/// Bits carried by one Crockford Base32 character.
59const CROCKFORD_BITS: usize = 5;
60
61/// Bits carried by one base64url character.
62const BASE64_BITS: usize = 6;
63
64/// Number of characters needed to carry [`HASH_SIZE_BIN`] bytes.
65const fn encoded_size(bits_per_char: usize) -> usize {
66    (HASH_SIZE_BIN * 8).div_ceil(bits_per_char)
67}
68
69/// Number of trailing characters that may be dropped while the resulting loss
70/// stays within the [`PARITY`]-byte correction budget.
71const fn recoverable_truncation(bits_per_char: usize) -> usize {
72    PARITY as usize * 8 / bits_per_char
73}
74
75/// [`Hash::validate`] dispatches on input length alone, so the ranges accepted
76/// for the three representations must not overlap.
77///
78/// [`Hash::validate`]: crate::Hash::validate
79const _: () = {
80    assert!(MIN_RECOVERABLE_BIN <= HASH_SIZE_BIN);
81    assert!(HASH_SIZE_BIN < MIN_RECOVERABLE_BASE64);
82
83    assert!(MIN_RECOVERABLE_BASE64 <= HASH_SIZE_BASE64);
84    assert!(HASH_SIZE_BASE64 < MIN_RECOVERABLE_CROCKFORD);
85
86    assert!(MIN_RECOVERABLE_CROCKFORD <= HASH_SIZE_CROCKFORD);
87};
88
89/// The compact representation is accepted through the binary range, so it must
90/// itself be recoverable.
91const _: () = {
92    assert!(HASH_SIZE_COMPACT >= MIN_RECOVERABLE_BIN);
93    assert!(HASH_SIZE_COMPACT <= HASH_SIZE_BIN);
94};
95
96/// Both encodings must carry the whole internal representation.
97const _: () = {
98    assert!(HASH_SIZE_CROCKFORD * CROCKFORD_BITS >= HASH_SIZE_BIN * 8);
99    assert!(HASH_SIZE_BASE64 * BASE64_BITS >= HASH_SIZE_BIN * 8);
100};
101
102#[cfg(test)]
103mod tests {
104    use super::{
105        DIGEST_SIZE, HASH_SIZE_BASE64, HASH_SIZE_BIN, HASH_SIZE_COMPACT, HASH_SIZE_CROCKFORD,
106        MIN_RECOVERABLE_BASE64, MIN_RECOVERABLE_BIN, MIN_RECOVERABLE_CROCKFORD, PARITY,
107        PARITY_OFFSET, PARITY_SIZE, SIZE_SIZE,
108    };
109
110    /// Pins every derived constant, so that a change to [`PARITY`] cannot
111    /// silently alter the wire format.
112    #[test]
113    fn constants_are_consistent() {
114        assert_eq!(DIGEST_SIZE, 32);
115        assert_eq!(SIZE_SIZE, 2);
116        assert_eq!(PARITY, 7);
117        assert_eq!(PARITY_SIZE, 14);
118        assert_eq!(PARITY_OFFSET, 34);
119        assert_eq!(HASH_SIZE_BIN, 48);
120        assert_eq!(HASH_SIZE_COMPACT, 42);
121        assert_eq!(HASH_SIZE_CROCKFORD, 77);
122        assert_eq!(HASH_SIZE_BASE64, 64);
123        assert_eq!(MIN_RECOVERABLE_BIN, 41);
124        assert_eq!(MIN_RECOVERABLE_CROCKFORD, 66);
125        assert_eq!(MIN_RECOVERABLE_BASE64, 55);
126    }
127}