Skip to content

Commit 6061131

Browse files
committed
bech32m variant
1 parent 534eb24 commit 6061131

File tree

2 files changed

+208
-7
lines changed

2 files changed

+208
-7
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ This repository contains crates to be used with a ledger device via the use of t
55
Currently everything is a work in progress, subject to change at any time without notice.
66

77
> # DO NOT USE
8-
> # IF YOU USE, USE AR YOUR OWN RISK!!!
8+
> # IF YOU USE, USE AT YOUR OWN RISK!!!
99
1010
We do not provide any warranty whatsoever on the use of these crates, so if you decide to use it
1111
any and all problems arising are purely your responsibility.

bolos/src/bech32.rs

+207-6
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub struct Bech32Writer<'b> {
146146
out: &'b mut [u8],
147147
bytes_written: usize,
148148
chk: u32,
149+
variant: Variant,
149150
}
150151

151152
#[derive(Debug)]
@@ -164,11 +165,12 @@ impl<'b> Bech32Writer<'b> {
164165
];
165166

166167
/// Creatw a new bech32 writer
167-
pub fn new(hrp: &str, out: &'b mut [u8]) -> Result<Self, Bech32WriterError> {
168+
pub fn new(hrp: &str, out: &'b mut [u8], variant: Variant) -> Result<Self, Bech32WriterError> {
168169
let mut this = Self {
169170
chk: 1,
170171
out,
171172
bytes_written: 0,
173+
variant,
172174
};
173175

174176
let hrp = hrp.as_bytes();
@@ -274,7 +276,7 @@ impl<'b> Bech32Writer<'b> {
274276
self.polymod_step(u5(0))
275277
}
276278

277-
let plm: u32 = self.chk ^ 1;
279+
let plm: u32 = self.chk ^ self.variant.constant();
278280

279281
self.check_rem_out(6)?;
280282
for p in 0..6 {
@@ -308,8 +310,9 @@ pub fn encode(
308310
hrp: &str,
309311
data: impl AsRef<[u8]>,
310312
out: &mut [u8],
313+
variant: Variant,
311314
) -> Result<usize, Bech32WriterError> {
312-
let mut encoder = Bech32Writer::new(hrp, out)?;
315+
let mut encoder = Bech32Writer::new(hrp, out, variant)?;
313316
encoder.write(data)?;
314317
encoder.finalize()
315318
}
@@ -318,6 +321,28 @@ pub const fn estimate_size(hrp_len: usize, data_len: usize) -> usize {
318321
Bech32Writer::estimate_size(hrp_len, data_len)
319322
}
320323

324+
325+
/// Used for encoding operations for the two variants of Bech32
326+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
327+
pub enum Variant {
328+
/// The original Bech32 described in [BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki)
329+
Bech32,
330+
/// The improved Bech32m variant described in [BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
331+
Bech32m,
332+
}
333+
334+
const BECH32_CONST: u32 = 1;
335+
const BECH32M_CONST: u32 = 0x2bc8_30a3;
336+
337+
impl Variant {
338+
fn constant(self) -> u32 {
339+
match self {
340+
Variant::Bech32 => BECH32_CONST,
341+
Variant::Bech32m => BECH32M_CONST,
342+
}
343+
}
344+
}
345+
321346
#[cfg(test)]
322347
mod tests {
323348
use super::*;
@@ -343,7 +368,7 @@ mod tests {
343368
fn encode_with_writer() {
344369
let mut out = [0; Bech32Writer::estimate_size(HRP.len(), INPUT.len())];
345370

346-
let mut encoder = Bech32Writer::new(HRP, &mut out).expect("unable to write HRP");
371+
let mut encoder = Bech32Writer::new(HRP, &mut out, Variant::Bech32).expect("unable to write HRP");
347372
encoder.write(&INPUT).expect("unable to write data");
348373
let written = encoder.finalize().expect("unable to finalize");
349374

@@ -355,8 +380,184 @@ mod tests {
355380
#[test]
356381
fn encode_with_fn() {
357382
let mut out = [0; estimate_size(HRP.len(), INPUT.len())];
358-
let written = encode(HRP, &INPUT, &mut out).expect("unable to encode bech32");
383+
let written = encode(HRP, &INPUT, &mut out, Variant::Bech32).expect("unable to encode bech32");
359384

360385
assert_eq!(&out[..written], EXPECTED.as_bytes());
361386
}
362-
}
387+
388+
const HRPS: [&str;5] =["a",
389+
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio",
390+
"abcdef",
391+
"1",
392+
"split"
393+
];
394+
395+
const BECH32_ENCODINGS: [&str;5] = ["A12UEL5L",
396+
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
397+
"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw",
398+
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j",
399+
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w"];
400+
401+
/*
402+
const BECH32_INPUTS: [&[u8];5] = [& *b"",
403+
& *b"",
404+
& [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
405+
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31],
406+
& [0;82],
407+
& *b""];
408+
*/
409+
410+
#[test]
411+
fn valid_checksum_bech32_0() {
412+
const INPUT: [u8; 0] = *b"";
413+
let mut out = [0; estimate_size(HRPS[0].len(),
414+
INPUT.len())];
415+
encode(HRPS[0], &INPUT, &mut out, Variant::Bech32)
416+
.expect("unable to encode bech32");
417+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
418+
419+
assert_eq!(BECH32_ENCODINGS[0].to_lowercase(), encoded.to_lowercase());
420+
}
421+
422+
#[test]
423+
fn valid_checksum_bech32_1() {
424+
const INPUT: [u8; 0] = *b"";
425+
let mut out = [0; estimate_size(HRPS[1].len(), INPUT.len())];
426+
encode(HRPS[1], &INPUT, &mut out, Variant::Bech32)
427+
.expect("unable to encode bech32");
428+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
429+
430+
assert_eq!(BECH32_ENCODINGS[1].to_lowercase(), encoded.to_lowercase());
431+
}
432+
433+
434+
#[test]#[ignore]
435+
fn valid_checksum_bech32_2() {
436+
const INPUT: [u8; 32] = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
437+
16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31];
438+
439+
let mut out = [0; estimate_size(HRPS[2].len(), INPUT.len())];
440+
let written = encode(HRPS[2], &INPUT, &mut out, Variant::Bech32)
441+
.expect("unable to encode bech32");
442+
let encoded = std::str::from_utf8(&out[..written]).expect("invalid utf8 bytes");
443+
444+
assert_eq!(BECH32_ENCODINGS[2].to_lowercase(), encoded.to_lowercase());
445+
}
446+
447+
#[test]#[ignore]
448+
fn valid_checksum_bech32_3() {
449+
const INPUT: [u8; 82] = [0;82];
450+
let mut out = [0; estimate_size(HRPS[3].len(), INPUT.len())];
451+
let written = encode(HRPS[3], &INPUT, &mut out, Variant::Bech32)
452+
.expect("unable to encode bech32");
453+
let encoded = std::str::from_utf8(&out[..written]).expect("invalid utf8 bytes");
454+
455+
assert_eq!(BECH32_ENCODINGS[3].to_lowercase(), encoded.to_lowercase());
456+
}
457+
458+
#[test]#[ignore]
459+
fn valid_checksum_bech32_4() {
460+
const INPUT: [u8; 0] = *b"";
461+
let mut out = [0; estimate_size(HRPS[4].len(), INPUT.len())];
462+
encode(HRPS[4], &INPUT, &mut out, Variant::Bech32)
463+
.expect("unable to encode bech32");
464+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
465+
466+
assert_eq!(BECH32_ENCODINGS[4].to_lowercase(),
467+
encoded.to_lowercase());
468+
}
469+
470+
471+
472+
const BECH32M_HRPS: [&str;7] =["a", "a",
473+
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber1",
474+
"abcdef",
475+
"1",
476+
"split",
477+
"?"
478+
];
479+
const BECH32M_ENCODINGS: [&str;7] =["A1LQFN3A", "a1lqfn3a",
480+
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
481+
"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
482+
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
483+
"split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
484+
"?1v759aa"];
485+
486+
#[test]
487+
fn valid_checksum_bech32m_0() {
488+
const INPUT: [u8; 0] = *b"";
489+
let mut out = [0; estimate_size(BECH32M_HRPS[0].len(), INPUT.len())];
490+
encode(BECH32M_HRPS[0], &INPUT, &mut out, Variant::Bech32m)
491+
.expect("unable to encode bech32m");
492+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
493+
494+
assert_eq!(BECH32M_ENCODINGS[0].to_lowercase(), encoded.to_lowercase());
495+
}
496+
497+
#[test]
498+
fn valid_checksum_bech32m_1() {
499+
const INPUT: [u8; 0] = *b"";
500+
let mut out = [0; estimate_size(BECH32M_HRPS[1].len(), INPUT.len())];
501+
encode(BECH32M_HRPS[1], &INPUT, &mut out, Variant::Bech32m)
502+
.expect("unable to encode bech32m");
503+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
504+
505+
assert_eq!(BECH32M_ENCODINGS[1].to_lowercase(), encoded.to_lowercase());
506+
}
507+
508+
#[test]
509+
fn valid_checksum_bech32m_2() {
510+
const INPUT: [u8; 0] = *b"";
511+
let mut out = [0; estimate_size(BECH32M_HRPS[2].len(), INPUT.len())];
512+
encode(BECH32M_HRPS[2], &INPUT, &mut out, Variant::Bech32m)
513+
.expect("unable to encode bech32m");
514+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
515+
516+
assert_eq!(BECH32M_ENCODINGS[2].to_lowercase(), encoded.to_lowercase());
517+
}
518+
519+
#[test]#[ignore]
520+
fn valid_checksum_bech32m_3() {
521+
const INPUT: [u8; 32] = [31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0];
522+
let mut out = [0; estimate_size(BECH32M_HRPS[3].len(), INPUT.len())];
523+
encode(BECH32M_HRPS[3], &INPUT, &mut out, Variant::Bech32m)
524+
.expect("unable to encode bech32m");
525+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
526+
527+
assert_eq!(BECH32M_ENCODINGS[3].to_lowercase(), encoded.to_lowercase());
528+
}
529+
530+
#[test]#[ignore]
531+
fn valid_checksum_bech32m_4() {
532+
const INPUT: [u8; 82] = [31;82];
533+
let mut out = [0; estimate_size(BECH32M_HRPS[4].len(), INPUT.len())];
534+
encode(BECH32M_HRPS[4], &INPUT, &mut out, Variant::Bech32m)
535+
.expect("unable to encode bech32m");
536+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
537+
538+
assert_eq!(BECH32M_ENCODINGS[4].to_lowercase(), encoded.to_lowercase());
539+
}
540+
541+
#[test]#[ignore]
542+
fn valid_checksum_bech32m_5() {
543+
const INPUT: [u8; 48] = [24, 23, 25, 24, 22, 28, 1, 16, 11, 29, 8, 25, 23, 29, 19, 13, 16, 23, 29, 22, 25, 28, 1, 16, 11, 3, 25, 29, 27, 25, 3, 3, 29, 19, 11, 25, 3, 3, 25, 13, 24, 29, 1, 25, 3, 3, 25, 13];
544+
let mut out = [0; estimate_size(BECH32M_HRPS[5].len(), INPUT.len())];
545+
let written = encode(BECH32M_HRPS[5], &INPUT, &mut out, Variant::Bech32m)
546+
.expect("unable to encode bech32m");
547+
let encoded = std::str::from_utf8(&out[..written]).expect("invalid utf8 bytes");
548+
549+
assert_eq!(BECH32M_ENCODINGS[5].to_lowercase(), encoded.to_lowercase());
550+
}
551+
552+
#[test]
553+
fn valid_checksum_bech32m_6() {
554+
const INPUT: [u8; 0] = *b"";
555+
let mut out = [0; estimate_size(BECH32M_HRPS[6].len(), INPUT.len())];
556+
encode(BECH32M_HRPS[6], &INPUT, &mut out, Variant::Bech32m)
557+
.expect("unable to encode bech32m");
558+
let encoded = std::str::from_utf8(&out).expect("invalid utf8 bytes");
559+
560+
assert_eq!(BECH32M_ENCODINGS[6].to_lowercase(), encoded.to_lowercase());
561+
}
562+
563+
}

0 commit comments

Comments
 (0)