depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

segwit.rs (6009B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 use std::cmp::Ordering;
     17 
     18 use bech32::Hrp;
     19 use depolymerizer_common::rand_slice;
     20 use rand::rngs::ThreadRng;
     21 use taler_common::api::{EddsaPublicKey, EddsaPublicKeyError};
     22 
     23 // TODO use segwit v1 to only use a single address
     24 
     25 /// Encode metadata into a segwit address
     26 pub fn encode_segwit_addr(hrp: Hrp, metada: &[u8; 20]) -> String {
     27     bech32::segwit::encode_v0(hrp, metada).unwrap()
     28 }
     29 
     30 /// Encode half of a 32B key into a segwit address
     31 fn encode_segwit_key_half(
     32     hrp: Hrp,
     33     is_first: bool,
     34     prefix: &[u8; 4],
     35     key_half: &[u8; 16],
     36 ) -> String {
     37     // Combine prefix and the key half
     38     let mut buf = [0u8; 20];
     39     buf[..4].copy_from_slice(prefix);
     40     buf[4..].copy_from_slice(key_half);
     41     // Toggle first bit for ordering
     42     if is_first {
     43         buf[0] &= 0b0111_1111 // Unset first bit
     44     } else {
     45         buf[0] |= 0b1000_0000 // Set first bit
     46     }
     47     // Encode into an fake segwit address
     48     encode_segwit_addr(hrp, &buf)
     49 }
     50 
     51 /// Encode a 32B key into two segwit addresses
     52 pub fn encode_segwit_key(hrp: Hrp, msg: &[u8; 32]) -> [String; 2] {
     53     // Generate a random prefix
     54     let prefix = rand_slice();
     55     // Split key in half;
     56     let split: (&[u8; 16], &[u8; 16]) =
     57         (msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap());
     58     [
     59         encode_segwit_key_half(hrp, true, &prefix, split.0),
     60         encode_segwit_key_half(hrp, false, &prefix, split.1),
     61     ]
     62 }
     63 
     64 #[derive(Debug, thiserror::Error)]
     65 pub enum DecodeSegWitErr {
     66     #[error("There is less than 2 segwit addresses")]
     67     MissingSegWitAddress,
     68     #[error("No addresses are sharing a common prefix")]
     69     NoPrefixMatch,
     70     #[error("More than two addresses are sharing a common prefix")]
     71     PrefixCollision,
     72     #[error(transparent)]
     73     Malformed(EddsaPublicKeyError),
     74 }
     75 
     76 /// Decode a 32B key into from addresses
     77 pub fn decode_segwit_msg(
     78     segwit_addrs: &[impl AsRef<str>],
     79 ) -> Result<EddsaPublicKey, DecodeSegWitErr> {
     80     // Extract parts from every addresses
     81     let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs
     82         .iter()
     83         .filter_map(|addr| {
     84             bech32::segwit::decode(addr.as_ref())
     85                 .ok()
     86                 .and_then(|(_, _, pg)| {
     87                     if pg.len() == 20 {
     88                         let mut prefix: [u8; 4] = pg[..4].try_into().unwrap();
     89                         let key_half: [u8; 16] = pg[4..].try_into().unwrap();
     90                         let is_first = !pg[0] & 0b1000_0000 == 0;
     91                         // Clear first bit
     92                         prefix[0] &= 0b0111_1111;
     93                         Some((is_first, prefix, key_half))
     94                     } else {
     95                         None
     96                     }
     97                 })
     98         })
     99         .collect();
    100 
    101     if decoded.len() < 2 {
    102         return Err(DecodeSegWitErr::MissingSegWitAddress);
    103     }
    104     // Keep only the addresses with duplicated prefix
    105     // TODO use sort_unstable_by and partition_dedup_by_key when stable
    106     let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded
    107         .iter()
    108         .filter(|(_, prefix, _)| {
    109             decoded
    110                 .iter()
    111                 .filter(|(_, other, _)| other == prefix)
    112                 .count()
    113                 > 1
    114         })
    115         .collect();
    116 
    117     match matches.len().cmp(&2) {
    118         Ordering::Equal => {
    119             let mut key = [0; 32];
    120             for (is_first, _, half) in matches {
    121                 key[*is_first as usize * 16..][..16].copy_from_slice(half);
    122             }
    123             EddsaPublicKey::try_from(key).map_err(DecodeSegWitErr::Malformed)
    124         }
    125         Ordering::Greater => Err(DecodeSegWitErr::PrefixCollision),
    126         Ordering::Less => Err(DecodeSegWitErr::MissingSegWitAddress),
    127     }
    128 }
    129 
    130 // TODO find a way to hide that function while using it in test and benchmark
    131 pub fn rand_addresses(hrp: Hrp, key: &[u8; 32]) -> Vec<String> {
    132     use rand::prelude::SliceRandom;
    133 
    134     let mut rng_address: Vec<String> =
    135         std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_slice()))
    136             .take(2)
    137             .collect();
    138 
    139     let mut addresses = encode_segwit_key(hrp, key).to_vec();
    140     addresses.append(&mut rng_address);
    141     addresses.shuffle(&mut ThreadRng::default());
    142     addresses
    143 }
    144 
    145 #[cfg(test)]
    146 mod test {
    147     use rand::{prelude::SliceRandom, rngs::ThreadRng};
    148     use taler_common::api::EddsaPublicKey;
    149 
    150     use crate::segwit::{decode_segwit_msg, encode_segwit_key, rand_addresses};
    151 
    152     #[test]
    153     fn test_shuffle() {
    154         let mut rng = ThreadRng::default();
    155         for _ in 0..1000 {
    156             let key = EddsaPublicKey::rand();
    157             let mut addresses = encode_segwit_key(bech32::hrp::TB, &key);
    158             addresses.shuffle(&mut rng);
    159             let decoded =
    160                 decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
    161                     .unwrap();
    162             assert_eq!(key, decoded);
    163         }
    164     }
    165 
    166     #[test]
    167     fn test_shuffle_many() {
    168         for _ in 0..1000 {
    169             let key = EddsaPublicKey::rand();
    170             let addresses = rand_addresses(bech32::hrp::TB, &key);
    171             let decoded =
    172                 decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
    173                     .unwrap();
    174             assert_eq!(key, decoded);
    175         }
    176     }
    177 }