ms-codec 0.7.0

Reference implementation of the ms1 (HRP `ms`) backup format — BIP-93 codex32 directly applied to secret material (BIP-39 entropy, BIP-32 master seed, xpriv).
Documentation
// Vendored from `codex32` v0.1.0 (crates.io checksum
// d230935faa4d0521349d228f39aba4ff489cf2a8bcab4d84e31f4cbd6fe918e9), CC0-1.0,
// by Andrew Poelstra. Inlined into ms-codec (Cycle-B, shape A) to own the
// Zeroize/Drop/redacting-Debug secret-hygiene fixes (FOLLOWUP
// codex32-upstream-dormant-vendor-vs-accept-decision).
//
// Copied from the upstream runtime modules (lib.rs / field.rs / checksum.rs).
// The ONLY substantive edits are: (1) Zeroize/ZeroizeOnDrop + a redacting Debug
// on `Codex32String` (Phase 2); (2) module-routing of `use` paths to fit the
// inlined submodule (`crate::field` -> `super::field` in checksum.rs, since the
// codex32 crate root is now the `codex32` submodule); (3) crate-local lint
// `#![allow(..)]`s (the crate denies missing_docs / runs -D-warnings clippy; the
// upstream copy predates both). rustfmt also normalized a few cosmetic spots
// (import order, one array literal, one fn-signature wrap) — NONE touch encoding
// logic. The ENCODING (from_seed/from_string/interpolate_at/checksum/field) is
// behaviorally UNCHANGED; the wire-byte-identity invariant is proven by
// tests/codex32_vendor_parity.rs. The upstream CC0 LICENSE is retained verbatim
// alongside as src/codex32/LICENSE.
//
// Rust Codex32 Library and Reference Implementation
// Written in 2023 by
//   Andrew Poelstra <apoelstra@wpsoftware.net>
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! Field Implementation
//!
//! Implements GF32 arithmetic, defined and encoded as in BIP-0173 "bech32"
//!

use std::{
    convert::{TryFrom, TryInto},
    fmt, num, ops, str,
};

/// Locarithm table of each bech32 element, as a power of alpha = Z.
///
/// Includes Q as 0 but this is false; you need to exclude Q because
/// it has no discrete log. If we could have a 1-indexed array that
/// would panic on a 0 index that would be better.
#[rustfmt::skip]
const LOG: [isize; 32] = [
     0,  0,  1, 14,  2, 28, 15, 22,
     3,  5, 29, 26, 16,  7, 23, 11, 
     4, 25,  6, 10, 30, 13, 27, 21,
    17, 18,  8, 19, 24,  9, 12, 20,
];

/// Mapping of powers of 2 to the numeric value of the element
#[rustfmt::skip]
const LOG_INV: [u8; 31] = [
     1,  2,  4,  8, 16,  9, 18, 13,
    26, 29, 19, 15, 30, 21,  3,  6,
    12, 24, 25, 27, 31, 23,  7, 14,
    28, 17, 11, 22,  5, 10, 20,
];

/// Mapping from numeric value to bech32 character
#[rustfmt::skip]
const CHARS_LOWER: [char; 32] = [
    'q', 'p', 'z', 'r', 'y', '9', 'x', '8', //  +0
    'g', 'f', '2', 't', 'v', 'd', 'w', '0', //  +8
    's', '3', 'j', 'n', '5', '4', 'k', 'h', // +16
    'c', 'e', '6', 'm', 'u', 'a', '7', 'l', // +24
];

/// Mapping from bech32 character (either case) to numeric value
#[rustfmt::skip]
const CHARS_INV: [i8; 128] = [
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    15, -1, 10, 17, 21, 20, 26, 30,  7,  5, -1, -1, -1, -1, -1, -1,
    -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,
     1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1,
    -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,
     1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1,
];

/// Field-related error
#[derive(Debug)]
pub enum Error {
    /// Tried to decode a GF32 element from a string, but got more than one character
    ExtraChar(char),
    /// Tried to interpret an integer as a GF32 element but it could not be
    /// converted to an u8.
    NotAByte(num::TryFromIntError),
    /// Tried to interpret a byte as a GF32 element but its numeric value was
    /// outside of [0, 32).
    InvalidByte(u8),
}

/// An element of GF32
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Fe(u8);

impl ops::Add for Fe {
    type Output = Fe;
    fn add(self, other: Fe) -> Fe {
        Fe(self.0 ^ other.0)
    }
}

impl ops::AddAssign for Fe {
    fn add_assign(&mut self, other: Fe) {
        *self = *self + other;
    }
}

// Subtraction is the same as addition in a char-2 field
impl ops::Sub for Fe {
    type Output = Fe;
    fn sub(self, other: Fe) -> Fe {
        self + other
    }
}

impl ops::SubAssign for Fe {
    fn sub_assign(&mut self, other: Fe) {
        *self = *self - other;
    }
}

impl ops::Mul for Fe {
    type Output = Fe;
    fn mul(self, other: Fe) -> Fe {
        if self.0 == 0 || other.0 == 0 {
            Fe(0)
        } else {
            let log1 = LOG[self.0 as usize];
            let log2 = LOG[other.0 as usize];
            Fe(LOG_INV[((log1 + log2) % 31) as usize])
        }
    }
}

impl ops::MulAssign for Fe {
    fn mul_assign(&mut self, other: Fe) {
        *self = *self * other;
    }
}

impl ops::Div for Fe {
    type Output = Fe;
    fn div(self, other: Fe) -> Fe {
        if self.0 == 0 {
            Fe(0)
        } else if other.0 == 0 {
            panic!("Attempt to divide {} by 0 in GF32", self);
        } else {
            let log1 = LOG[self.0 as usize];
            let log2 = LOG[other.0 as usize];
            Fe(LOG_INV[((31 + log1 - log2) % 31) as usize])
        }
    }
}

impl ops::DivAssign for Fe {
    fn div_assign(&mut self, other: Fe) {
        *self = *self / other;
    }
}

impl Fe {
    // These are a little gratuitous for a reference implementation,
    // but it makes me happy to do it
    pub const Q: Fe = Fe(0);
    pub const P: Fe = Fe(1);
    #[allow(dead_code)]
    pub const Z: Fe = Fe(2);
    pub const R: Fe = Fe(3);
    pub const Y: Fe = Fe(4);
    pub const _9: Fe = Fe(5);
    pub const X: Fe = Fe(6);
    pub const _8: Fe = Fe(7);
    pub const G: Fe = Fe(8);
    pub const F: Fe = Fe(9);
    pub const _2: Fe = Fe(10);
    pub const T: Fe = Fe(11);
    #[allow(dead_code)]
    pub const V: Fe = Fe(12);
    #[allow(dead_code)]
    pub const D: Fe = Fe(13);
    #[allow(dead_code)]
    pub const W: Fe = Fe(14);
    pub const _0: Fe = Fe(15);
    pub const S: Fe = Fe(16);
    pub const _3: Fe = Fe(17);
    #[allow(dead_code)]
    pub const J: Fe = Fe(18);
    #[allow(dead_code)]
    pub const N: Fe = Fe(19);
    pub const _5: Fe = Fe(20);
    pub const _4: Fe = Fe(21);
    pub const K: Fe = Fe(22);
    pub const H: Fe = Fe(23);
    pub const C: Fe = Fe(24);
    pub const E: Fe = Fe(25);
    pub const _6: Fe = Fe(26);
    pub const M: Fe = Fe(27);
    #[allow(dead_code)]
    pub const U: Fe = Fe(28);
    pub const A: Fe = Fe(29);
    pub const _7: Fe = Fe(30);
    pub const L: Fe = Fe(31);

    /// Iterator over all field elements, in alphabetical order
    pub fn iter_alpha() -> impl Iterator<Item = Fe> {
        [
            Fe::A,
            Fe::C,
            Fe::D,
            Fe::E,
            Fe::F,
            Fe::G,
            Fe::H,
            Fe::J,
            Fe::K,
            Fe::L,
            Fe::M,
            Fe::N,
            Fe::P,
            Fe::Q,
            Fe::R,
            Fe::S,
            Fe::T,
            Fe::U,
            Fe::V,
            Fe::W,
            Fe::X,
            Fe::Y,
            Fe::Z,
            Fe::_0,
            Fe::_2,
            Fe::_3,
            Fe::_4,
            Fe::_5,
            Fe::_6,
            Fe::_7,
            Fe::_8,
            Fe::_9,
        ]
        .iter()
        .copied()
    }

    /// Creates a field element from an integer type
    pub fn from_u8(byte: u8) -> Result<Fe, super::Error> {
        if byte < 32 {
            Ok(Fe(byte))
        } else {
            Err(super::Error::Field(Error::InvalidByte(byte)))
        }
    }

    /// Creates a field element from an integer type
    pub fn from_int<I>(i: I) -> Result<Fe, super::Error>
    where
        I: TryInto<u8, Error = num::TryFromIntError>,
    {
        i.try_into()
            .map_err(|e| super::Error::Field(Error::NotAByte(e)))
            .and_then(Self::from_u8)
    }

    /// Creates a field element from a single bech32 character
    pub fn from_char(c: char) -> Result<Fe, super::Error> {
        let byte = i8::try_from(u32::from(c)).map_err(|_| super::Error::InvalidChar(c))?;
        let byte = byte as u8; // cast guaranteed to be ok since we started with an unsigned value
        let u5 =
            u8::try_from(CHARS_INV[usize::from(byte)]).map_err(|_| super::Error::InvalidChar(c))?;
        Ok(Fe(u5))
    }

    /// Converts the field element to a lowercase bech32 character
    pub fn to_char(self) -> char {
        // casting and indexing fine as we have self.0 in [0, 32) as an invariant
        CHARS_LOWER[self.0 as usize]
    }

    /// Converts the field element to a 5-bit u8, with bits representing the coefficients
    /// of the polynomial representation.
    pub fn to_u8(self) -> u8 {
        self.0
    }
}

impl fmt::Display for Fe {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(&self.to_char(), f)
    }
}

impl str::FromStr for Fe {
    type Err = super::Error;
    fn from_str(s: &str) -> Result<Fe, super::Error> {
        let mut chs = s.chars();
        match (chs.next(), chs.next()) {
            (Some(c), None) => Fe::from_char(c),
            (Some(_), Some(c)) => Err(super::Error::Field(Error::ExtraChar(c))),
            (None, _) => Err(super::Error::InvalidLength(0)),
        }
    }
}