diff options
Diffstat (limited to 'btc-wire/src/segwit.rs')
-rw-r--r-- | btc-wire/src/segwit.rs | 343 |
1 files changed, 172 insertions, 171 deletions
diff --git a/btc-wire/src/segwit.rs b/btc-wire/src/segwit.rs index 5b7414d..e754d71 100644 --- a/btc-wire/src/segwit.rs +++ b/btc-wire/src/segwit.rs @@ -1,171 +1,172 @@ -/*
- This file is part of TALER
- Copyright (C) 2022 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-use bech32::{u5, FromBase32, ToBase32, Variant};
-use common::{rand::rngs::OsRng, rand_slice};
-
-/// Encode metadata into a segwit address
-pub fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String {
- // We use the version 0 with bech32 encoding
- let mut buf = vec![u5::try_from_u8(0).unwrap()];
- buf.extend_from_slice(&metada.to_base32());
- bech32::encode(hrp, buf, Variant::Bech32).unwrap()
-}
-
-/// Encode half of a 32B key into a segwit address
-fn encode_segwit_key_half(
- hrp: &str,
- is_first: bool,
- prefix: &[u8; 4],
- key_half: &[u8; 16],
-) -> String {
- // Combine prefix and the key half
- let mut buf = [0u8; 20];
- buf[..4].copy_from_slice(prefix);
- buf[4..].copy_from_slice(key_half);
- // Toggle first bit for ordering
- if is_first {
- buf[0] &= 0b0111_1111 // Unset first bit
- } else {
- buf[0] |= 0b1000_0000 // Set first bit
- }
- // Encode into an fake segwit address
- encode_segwit_addr(hrp, &buf)
-}
-
-/// Encode a 32B key into two segwit adresses
-pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] {
- // Generate a random prefix
- let prefix = rand_slice();
- // Split key in half;
- let split: (&[u8; 16], &[u8; 16]) =
- (msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap());
- [
- encode_segwit_key_half(hrp, true, &prefix, split.0),
- encode_segwit_key_half(hrp, false, &prefix, split.1),
- ]
-}
-
-#[derive(Debug, Clone, thiserror::Error)]
-pub enum DecodeSegWitErr {
- #[error("There is less than 2 segwit addresses")]
- MissingSegWitAddress,
- #[error("No adresses are sharing a common prefix")]
- NoPrefixMatch,
- #[error("More than two addresses are sharing a common prefix")]
- PrefixCollision,
-}
-
-/// Decode a 32B key into from adresses
-pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], DecodeSegWitErr> {
- // Extract parts from every addresses
- let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs
- .iter()
- .filter_map(|addr| {
- bech32::decode(addr.as_ref()).ok().and_then(|(_, wp, _)| {
- // Skip version
- let pg: Vec<u8> = Vec::from_base32(&wp[1..]).unwrap();
- if pg.len() == 20 {
- let mut prefix: [u8; 4] = pg[..4].try_into().unwrap();
- let key_half: [u8; 16] = pg[4..].try_into().unwrap();
- let is_first = !pg[0] & 0b1000_0000 == 0;
- // Clear first bit
- prefix[0] &= 0b0111_1111;
- Some((is_first, prefix, key_half))
- } else {
- None
- }
- })
- })
- .collect();
-
- if decoded.len() < 2 {
- return Err(DecodeSegWitErr::MissingSegWitAddress);
- }
- // Keep only the addresses with duplicated prefix
- // TODO use sort_unstable_by and partition_dedup_by_key when stable
- let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded
- .iter()
- .filter(|(_, prefix, _)| {
- decoded
- .iter()
- .filter(|(_, other, _)| other == prefix)
- .count()
- > 1
- })
- .collect();
-
- if matches.len() > 2 {
- return Err(DecodeSegWitErr::PrefixCollision);
- } else if matches.len() < 2 {
- return Err(DecodeSegWitErr::MissingSegWitAddress);
- }
-
- let mut key = [0; 32];
- for (is_first, _, half) in matches {
- key[*is_first as usize * 16..][..16].copy_from_slice(half);
- }
- Ok(key)
-}
-
-// TODO find a way to hide that function while using it in test and benchmark
-pub fn rand_addresses(hrp: &str, key: &[u8; 32]) -> Vec<String> {
- use common::rand::prelude::SliceRandom;
-
- let mut rng_address: Vec<String> =
- std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_slice()))
- .take(2)
- .collect();
-
- let mut addresses = encode_segwit_key(hrp, key).to_vec();
- addresses.append(&mut rng_address);
- addresses.shuffle(&mut OsRng);
- addresses
-}
-
-#[cfg(test)]
-mod test {
- use common::{
- rand::{prelude::SliceRandom, rngs::OsRng},
- rand_slice,
- };
-
- use crate::segwit::{decode_segwit_msg, encode_segwit_key, rand_addresses};
-
- #[test]
- fn test_shuffle() {
- for _ in 0..1000 {
- let key = rand_slice();
- let mut addresses = encode_segwit_key("test", &key);
- addresses.shuffle(&mut OsRng);
- let decoded =
- decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
- .unwrap();
- assert_eq!(key, decoded);
- }
- }
-
- #[test]
- fn test_shuffle_many() {
- for _ in 0..1000 {
- let key = rand_slice();
- let addresses = rand_addresses("test", &key);
- let decoded =
- decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
- .unwrap();
- assert_eq!(key, decoded);
- }
- }
-}
+/* + This file is part of TALER + Copyright (C) 2022 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU Affero General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +use bech32::{u5, FromBase32, ToBase32, Variant}; +use common::{rand::rngs::OsRng, rand_slice}; +use std::cmp::Ordering; + +/// Encode metadata into a segwit address +pub fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String { + // We use the version 0 with bech32 encoding + let mut buf = vec![u5::try_from_u8(0).unwrap()]; + buf.extend_from_slice(&metada.to_base32()); + bech32::encode(hrp, buf, Variant::Bech32).unwrap() +} + +/// Encode half of a 32B key into a segwit address +fn encode_segwit_key_half( + hrp: &str, + is_first: bool, + prefix: &[u8; 4], + key_half: &[u8; 16], +) -> String { + // Combine prefix and the key half + let mut buf = [0u8; 20]; + buf[..4].copy_from_slice(prefix); + buf[4..].copy_from_slice(key_half); + // Toggle first bit for ordering + if is_first { + buf[0] &= 0b0111_1111 // Unset first bit + } else { + buf[0] |= 0b1000_0000 // Set first bit + } + // Encode into an fake segwit address + encode_segwit_addr(hrp, &buf) +} + +/// Encode a 32B key into two segwit adresses +pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] { + // Generate a random prefix + let prefix = rand_slice(); + // Split key in half; + let split: (&[u8; 16], &[u8; 16]) = + (msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap()); + [ + encode_segwit_key_half(hrp, true, &prefix, split.0), + encode_segwit_key_half(hrp, false, &prefix, split.1), + ] +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum DecodeSegWitErr { + #[error("There is less than 2 segwit addresses")] + MissingSegWitAddress, + #[error("No adresses are sharing a common prefix")] + NoPrefixMatch, + #[error("More than two addresses are sharing a common prefix")] + PrefixCollision, +} + +/// Decode a 32B key into from adresses +pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], DecodeSegWitErr> { + // Extract parts from every addresses + let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs + .iter() + .filter_map(|addr| { + bech32::decode(addr.as_ref()).ok().and_then(|(_, wp, _)| { + // Skip version + let pg: Vec<u8> = Vec::from_base32(&wp[1..]).unwrap(); + if pg.len() == 20 { + let mut prefix: [u8; 4] = pg[..4].try_into().unwrap(); + let key_half: [u8; 16] = pg[4..].try_into().unwrap(); + let is_first = !pg[0] & 0b1000_0000 == 0; + // Clear first bit + prefix[0] &= 0b0111_1111; + Some((is_first, prefix, key_half)) + } else { + None + } + }) + }) + .collect(); + + if decoded.len() < 2 { + return Err(DecodeSegWitErr::MissingSegWitAddress); + } + // Keep only the addresses with duplicated prefix + // TODO use sort_unstable_by and partition_dedup_by_key when stable + let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded + .iter() + .filter(|(_, prefix, _)| { + decoded + .iter() + .filter(|(_, other, _)| other == prefix) + .count() + > 1 + }) + .collect(); + + match matches.len().cmp(&2) { + Ordering::Equal => { + let mut key = [0; 32]; + for (is_first, _, half) in matches { + key[*is_first as usize * 16..][..16].copy_from_slice(half); + } + Ok(key) + } + Ordering::Greater => Err(DecodeSegWitErr::PrefixCollision), + Ordering::Less => Err(DecodeSegWitErr::MissingSegWitAddress), + } +} + +// TODO find a way to hide that function while using it in test and benchmark +pub fn rand_addresses(hrp: &str, key: &[u8; 32]) -> Vec<String> { + use common::rand::prelude::SliceRandom; + + let mut rng_address: Vec<String> = + std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_slice())) + .take(2) + .collect(); + + let mut addresses = encode_segwit_key(hrp, key).to_vec(); + addresses.append(&mut rng_address); + addresses.shuffle(&mut OsRng); + addresses +} + +#[cfg(test)] +mod test { + use common::{ + rand::{prelude::SliceRandom, rngs::OsRng}, + rand_slice, + }; + + use crate::segwit::{decode_segwit_msg, encode_segwit_key, rand_addresses}; + + #[test] + fn test_shuffle() { + for _ in 0..1000 { + let key = rand_slice(); + let mut addresses = encode_segwit_key("test", &key); + addresses.shuffle(&mut OsRng); + let decoded = + decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) + .unwrap(); + assert_eq!(key, decoded); + } + } + + #[test] + fn test_shuffle_many() { + for _ in 0..1000 { + let key = rand_slice(); + let addresses = rand_addresses("test", &key); + let decoded = + decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) + .unwrap(); + assert_eq!(key, decoded); + } + } +} |