Skip to main content

md_codec/
bch.rs

1//! BIP 93 codex32 BCH primitives for HRP `"md"` (regular code only).
2//!
3//! Extracted from the v0.x `encoding` module; v0.11 needs only the regular-code
4//! checksum + verify (long code dropped along with v0.x).
5
6/// BCH(93,80,8) generator polynomial coefficients (5 × 65-bit).
7pub const GEN_REGULAR: [u128; 5] = [
8    0x19dc500ce73fde210,
9    0x1bfae00def77fe529,
10    0x1fbd920fffe7bee52,
11    0x1739640bdeee3fdad,
12    0x07729a039cfc75f5a,
13];
14
15/// MD-domain target residue (NUMS-style, top 65 bits of
16/// `SHA-256("shibbolethnums")`).
17pub const MD_REGULAR_CONST: u128 = 0x0815c07747a3392e7;
18
19const POLYMOD_INIT: u128 = 0x23181b3;
20const REGULAR_SHIFT: u32 = 60;
21const REGULAR_MASK: u128 = 0x0fffffffffffffff;
22
23fn polymod_step(residue: u128, value: u128) -> u128 {
24    let b = residue >> REGULAR_SHIFT;
25    let mut new_residue = ((residue & REGULAR_MASK) << 5) ^ value;
26    for (i, &g) in GEN_REGULAR.iter().enumerate() {
27        if (b >> i) & 1 != 0 {
28            new_residue ^= g;
29        }
30    }
31    new_residue
32}
33
34/// Run the BCH polymod over `values` starting from `POLYMOD_INIT`.
35///
36/// Returns the final residue; callers XOR against the per-HRP target
37/// constant (e.g. [`MD_REGULAR_CONST`]) to produce a checksum or to
38/// verify one. Inputs are 5-bit symbols (`u8` in `0..32`); larger
39/// values are reduced modulo 32 by the underlying step.
40pub fn polymod_run(values: &[u8]) -> u128 {
41    let mut residue = POLYMOD_INIT;
42    for &v in values {
43        residue = polymod_step(residue, v as u128);
44    }
45    residue
46}
47
48/// BIP 173-style HRP expansion: `[c >> 5 for c in hrp] ++ [0] ++ [c & 31 for c in hrp]`.
49pub fn hrp_expand(hrp: &str) -> Vec<u8> {
50    let bytes = hrp.as_bytes();
51    let mut out = Vec::with_capacity(bytes.len() * 2 + 1);
52    for &c in bytes {
53        out.push(c >> 5);
54    }
55    out.push(0);
56    for &c in bytes {
57        out.push(c & 31);
58    }
59    out
60}
61
62/// 13-symbol regular-code BCH checksum over `hrp_expand(hrp) || data || [0; 13]`.
63pub fn bch_create_checksum_regular(hrp: &str, data: &[u8]) -> [u8; 13] {
64    let mut input = hrp_expand(hrp);
65    input.extend_from_slice(data);
66    input.extend(std::iter::repeat_n(0, 13));
67    let polymod = polymod_run(&input) ^ MD_REGULAR_CONST;
68    let mut out = [0u8; 13];
69    for (i, slot) in out.iter_mut().enumerate() {
70        *slot = ((polymod >> (5 * (12 - i))) & 0x1F) as u8;
71    }
72    out
73}
74
75/// Verify a regular-code BCH checksum over the data-part-with-checksum.
76pub fn bch_verify_regular(hrp: &str, data_with_checksum: &[u8]) -> bool {
77    if data_with_checksum.len() < 13 {
78        return false;
79    }
80    let mut input = hrp_expand(hrp);
81    input.extend_from_slice(data_with_checksum);
82    polymod_run(&input) == MD_REGULAR_CONST
83}