ms_codec/codex32/checksum.rs
1// Vendored from `codex32` v0.1.0 (crates.io checksum
2// d230935faa4d0521349d228f39aba4ff489cf2a8bcab4d84e31f4cbd6fe918e9), CC0-1.0,
3// by Andrew Poelstra. Inlined into ms-codec (Cycle-B, shape A) to own the
4// Zeroize/Drop/redacting-Debug secret-hygiene fixes (FOLLOWUP
5// codex32-upstream-dormant-vendor-vs-accept-decision).
6//
7// Copied from the upstream runtime modules (lib.rs / field.rs / checksum.rs).
8// The ONLY substantive edits are: (1) Zeroize/ZeroizeOnDrop + a redacting Debug
9// on `Codex32String` (Phase 2); (2) module-routing of `use` paths to fit the
10// inlined submodule (`crate::field` -> `super::field` in checksum.rs, since the
11// codex32 crate root is now the `codex32` submodule); (3) crate-local lint
12// `#![allow(..)]`s (the crate denies missing_docs / runs -D-warnings clippy; the
13// upstream copy predates both). rustfmt also normalized a few cosmetic spots
14// (import order, one array literal, one fn-signature wrap) — NONE touch encoding
15// logic. The ENCODING (from_seed/from_string/interpolate_at/checksum/field) is
16// behaviorally UNCHANGED; the wire-byte-identity invariant is proven by
17// tests/codex32_vendor_parity.rs. The upstream CC0 LICENSE is retained verbatim
18// alongside as src/codex32/LICENSE.
19//
20// Rust Codex32 Library and Reference Implementation
21// Written in 2023 by
22// Andrew Poelstra <apoelstra@wpsoftware.net>
23//
24// To the extent possible under law, the author(s) have dedicated all
25// copyright and related and neighboring rights to this software to
26// the public domain worldwide. This software is distributed without
27// any warranty.
28//
29// You should have received a copy of the CC0 Public Domain Dedication
30// along with this software.
31// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
32//
33
34//! Checksums
35//!
36//! Validates specific checksums
37//!
38
39use super::field::Fe; // vendored: was `crate::field::Fe` upstream (crate root = the codex32 module here)
40use super::{Case, Error};
41
42/// An engine which consumes one GF32 character at a time, and produces
43/// a residue modulo some generator
44#[derive(Clone, PartialEq, Eq, Debug)]
45pub struct Engine {
46 case: Option<Case>,
47 generator: Vec<Fe>,
48 residue: Vec<Fe>,
49 target: Vec<Fe>,
50}
51
52impl Engine {
53 // An engine which computes the normal codex32 checksum
54 pub fn new_codex32_short() -> Engine {
55 Engine {
56 case: None,
57 #[rustfmt::skip]
58 generator: vec![
59 Fe::E, Fe::M, Fe::_3, Fe::G, Fe::Q, Fe::E,
60 Fe::E, Fe::E, Fe::L, Fe::M, Fe::C, Fe::S,
61 Fe::S,
62 ],
63 #[rustfmt::skip]
64 residue: vec![
65 Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q,
66 Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q,
67 Fe::P,
68 ],
69 #[rustfmt::skip]
70 target: vec![
71 Fe::S, Fe::E, Fe::C, Fe::R, Fe::E, Fe::T,
72 Fe::S, Fe::H, Fe::A, Fe::R, Fe::E, Fe::_3,
73 Fe::_2,
74 ],
75 }
76 }
77
78 // An engine which computes the "long" codex32 checksum
79 pub fn new_codex32_long() -> Engine {
80 // hyk9x4hx4ef6e20p
81 Engine {
82 case: None,
83 #[rustfmt::skip]
84 generator: vec![
85 Fe::_0, Fe::_2, Fe::E, Fe::_6, Fe::F, Fe::E,
86 Fe::_4, Fe::X, Fe::H, Fe::_4, Fe::X, Fe::_9,
87 Fe::K, Fe::Y, Fe::H,
88 ],
89 #[rustfmt::skip]
90 residue: vec![
91 Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q,
92 Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q, Fe::Q,
93 Fe::Q, Fe::Q, Fe::P,
94 ],
95 #[rustfmt::skip]
96 target: vec![
97 Fe::S, Fe::E, Fe::C, Fe::R, Fe::E, Fe::T,
98 Fe::S, Fe::H, Fe::A, Fe::R, Fe::E, Fe::_3,
99 Fe::_2, Fe::E, Fe::X,
100 ],
101 }
102 }
103
104 /// When computing checksums of "diffs" you do may want to set
105 /// the highest-degree coefficient of the polynomial to 1.
106 ///
107 /// If you do not know exactly why you are using this function,
108 /// you should not use it.
109 pub fn force_residue_to_zero(&mut self) {
110 self.residue = vec![Fe::Q; self.residue.len()];
111 }
112
113 /// Extracts the residue from a checksum engine
114 pub fn into_residue(self) -> Vec<Fe> {
115 self.residue
116 }
117
118 /// Determines whether the residue matches the target value
119 /// for the checksum
120 ///
121 /// If you need the actual residue, e.g. for error correction,
122 /// call the `into_residue` function (which will consume the
123 /// engine).
124 pub fn is_valid(&self) -> bool {
125 self.residue == self.target
126 }
127
128 /// Initializes the checksum engine by loading an HRP into it
129 pub fn input_hrp(&mut self, hrp: &str) -> Result<(), Error> {
130 for ch in hrp.chars() {
131 self.set_check_case(ch)?;
132 self.input_fe(Fe::from_int(u32::from(ch.to_ascii_lowercase()) >> 5)?);
133 }
134 self.input_fe(Fe::Q);
135 for ch in hrp.chars() {
136 self.input_fe(Fe::from_int(u32::from(ch.to_ascii_lowercase()) & 0x1f)?);
137 }
138 Ok(())
139 }
140
141 /// Adds a single character to the checksum engine
142 pub fn input_char(&mut self, c: char) -> Result<(), Error> {
143 self.set_check_case(c)?;
144 self.input_fe(Fe::from_char(c)?);
145 Ok(())
146 }
147
148 /// Adds an entire string to the engine, counting each character as a data character
149 /// (not an HRP).
150 pub fn input_data_str(&mut self, s: &str) -> Result<(), Error> {
151 for ch in s.chars() {
152 self.input_char(ch)?;
153 }
154 Ok(())
155 }
156
157 /// Adds the target residue to the end of the input string
158 pub fn input_own_target(&mut self) {
159 let fuck_the_borrow_checker = self.target.clone();
160 for u in fuck_the_borrow_checker {
161 self.input_fe(u);
162 }
163 }
164
165 /// Helper function to check that the whole input has consistent case
166 fn set_check_case(&mut self, c: char) -> Result<(), Error> {
167 if !c.is_ascii() {
168 Err(Error::InvalidChar(c))
169 } else if c.is_numeric() {
170 // numbers don't affect case, nor are they affected by case
171 Ok(())
172 } else {
173 let is_lower = c.is_ascii_lowercase();
174 match (self.case, is_lower) {
175 (Some(Case::Lower), true) | (Some(Case::Upper), false) => Ok(()),
176 (Some(case @ Case::Lower), false) | (Some(case @ Case::Upper), true) => {
177 Err(Error::InvalidCase(case, c))
178 }
179 (None, true) => {
180 self.case = Some(Case::Lower);
181 Ok(())
182 }
183 (None, false) => {
184 self.case = Some(Case::Upper);
185 Ok(())
186 }
187 }
188 }
189 }
190
191 /// Adds a single field element to the checksum engine
192 ///
193 /// This is where the real magic happens.
194 #[rustfmt::skip]
195 pub fn input_fe(&mut self, e: Fe) {
196 let res_len = self.residue.len(); // needed for borrowck
197 // Store current coefficient of x^{n-1}, which will become
198 // x^n (and get reduced)
199 let xn = self.residue[0];
200 // Simply shift x^0 through x^{n-1} up one, and set x^0 to the new input
201 for i in 1..res_len {
202 self.residue[i - 1] = self.residue[i];
203 }
204 self.residue[res_len - 1] = e;
205 // Then reduce x^n mod the generator.
206 for i in 0..res_len {
207 self.residue[i] += self.generator[i] * xn;
208 }
209 }
210}