commit 94b9f89b75ff1925948a896476c91be2dbee310a
parent 4e1f47bcc56639427b99de6bff49830ee586b63d
Author: Antoine A <>
Date: Fri, 12 Nov 2021 11:54:29 +0100
Improve encode_segwit_msg security and perf
Diffstat:
5 files changed, 105 insertions(+), 41 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -303,12 +303,14 @@ name = "depolymerization"
version = "0.1.0"
dependencies = [
"argh",
+ "bech32",
"bitcoin-bech32",
"bitcoincore-rpc",
"bs58",
"criterion",
"digest",
"fastrand",
+ "rand",
"rustyline",
]
@@ -586,6 +588,12 @@ dependencies = [
]
[[package]]
+name = "ppv-lite86"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
+
+[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -614,6 +622,46 @@ dependencies = [
]
[[package]]
+name = "rand"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
@@ -13,6 +13,8 @@ digest = "0.9.0"
argh = "0.1.6"
rustyline = "9.0.0"
bitcoin-bech32 = "0.12.1"
+bech32 = "0.8.1"
+rand = { version = "0.8.4", features = ["getrandom"] }
[dev-dependencies]
# statistics-driven micro-benchmarks
@@ -21,4 +23,3 @@ criterion = "0.3.5"
[[bench]]
name = "metadata"
harness = false
-
diff --git a/benches/metadata.rs b/benches/metadata.rs
@@ -1,8 +1,8 @@
-use bitcoin_bech32::constants::Network;
use criterion::{criterion_group, criterion_main, Criterion};
use depolymerization::{
decode_segwit_msg, encode_segwit_msg,
utils::{rand_addresses, rand_key},
+ Network,
};
fn criterion_benchmark(c: &mut Criterion) {
@@ -10,13 +10,13 @@ fn criterion_benchmark(c: &mut Criterion) {
group.bench_function("encode", |b| {
b.iter_batched(
rand_key,
- |key| encode_segwit_msg(Network::Bitcoin, &key),
+ |key| encode_segwit_msg(Network::MainNet, &key),
criterion::BatchSize::SmallInput,
);
});
group.bench_function("decode", |b| {
b.iter_batched(
- || rand_addresses(&rand_key()),
+ || rand_addresses(Network::MainNet, &rand_key()),
|addrs| decode_segwit_msg(&addrs),
criterion::BatchSize::SmallInput,
);
diff --git a/src/lib.rs b/src/lib.rs
@@ -1,21 +1,47 @@
-use std::{collections::BTreeMap, iter::repeat_with};
+use std::collections::BTreeMap;
-use bitcoin_bech32::{constants::Network, u5, WitnessProgram};
+use bech32::{ToBase32, Variant};
+use bitcoin_bech32::{u5, WitnessProgram};
use bitcoincore_rpc::bitcoin::Amount;
+use rand::{rngs::OsRng, RngCore};
+
+#[derive(Debug, Clone, Copy)]
+pub enum Network {
+ MainNet,
+ TestNet,
+ RegTest,
+}
+
+impl Network {
+ pub fn segwit_hrp(&self) -> &'static str {
+ match self {
+ Network::MainNet => "bc",
+ Network::TestNet => "tb",
+ Network::RegTest => "bcrt",
+ }
+ }
+}
pub fn segwit_min_amount() -> Amount {
// https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp
return Amount::from_sat(294);
}
-fn encode_segwit_address(
+pub fn encode_segwit_addr(network: Network, data: &[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(&data.to_base32());
+ bech32::encode(network.segwit_hrp(), buf, Variant::Bech32).unwrap()
+}
+
+fn encode_segwit_key_half(
network: Network,
is_first: bool,
magic_id: &[u8; 4],
key_half: &[u8; 16],
) -> String {
// Combine magic_it and the key half
- let mut buf = vec![0u8; 20];
+ let mut buf = [0u8; 20];
buf[..4].copy_from_slice(magic_id);
buf[4..].copy_from_slice(key_half);
// Toggle first bit for ordering
@@ -25,23 +51,20 @@ fn encode_segwit_address(
buf[0] |= 0b1000_0000 // Set first bit
}
// Encode into an fake segwit address
- WitnessProgram::new(u5::try_from_u8(0).unwrap(), buf, network)
- .unwrap()
- .to_address()
+ encode_segwit_addr(network, &buf)
}
pub fn encode_segwit_msg(network: Network, msg: &[u8; 32]) -> [String; 2] {
// Generate a random magic identifier
let mut magic_id = [0; 4];
- // TODO use secure os based random
- magic_id.fill_with(|| fastrand::u8(..));
+ OsRng.fill_bytes(&mut magic_id);
// Split key in half;
let mut split = ([0; 16], [0; 16]);
split.0.copy_from_slice(&msg[..16]);
split.1.copy_from_slice(&msg[16..]);
[
- encode_segwit_address(network, true, &magic_id, &split.0),
- encode_segwit_address(network, false, &magic_id, &split.1),
+ encode_segwit_key_half(network, true, &magic_id, &split.0),
+ encode_segwit_key_half(network, false, &magic_id, &split.1),
]
}
@@ -113,11 +136,7 @@ pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], D
}
pub mod utils {
- use std::iter::repeat_with;
-
- use bitcoin_bech32::{constants::Network, u5, WitnessProgram};
-
- use crate::encode_segwit_msg;
+ use crate::{encode_segwit_addr, encode_segwit_msg, Network};
pub fn rand_key() -> [u8; 32] {
let mut key = [0; 32];
@@ -125,17 +144,19 @@ pub mod utils {
key
}
- pub fn rand_addresses(key: &[u8; 32]) -> Vec<String> {
- let mut rng_address: Vec<String> = std::iter::repeat_with(|| {
- let key: Vec<u8> = repeat_with(|| fastrand::u8(..)).take(20).collect();
- WitnessProgram::new(u5::try_from_u8(0).unwrap(), key, Network::Bitcoin)
- .unwrap()
- .to_address()
- })
- .take(2)
- .collect();
+ pub fn rand_data() -> [u8; 20] {
+ let mut key = [0; 20];
+ key.fill_with(|| fastrand::u8(..));
+ key
+ }
- let mut addresses = encode_segwit_msg(Network::Bitcoin, &key).to_vec();
+ pub fn rand_addresses(network: Network, key: &[u8; 32]) -> Vec<String> {
+ let mut rng_address: Vec<String> =
+ std::iter::repeat_with(|| encode_segwit_addr(network, &rand_data()))
+ .take(2)
+ .collect();
+
+ let mut addresses = encode_segwit_msg(network, &key).to_vec();
addresses.append(&mut rng_address);
fastrand::shuffle(&mut addresses);
addresses
@@ -144,18 +165,17 @@ pub mod utils {
#[cfg(test)]
mod test {
- use bitcoin_bech32::constants::Network;
-
use crate::{
decode_segwit_msg, encode_segwit_msg,
utils::{rand_addresses, rand_key},
+ Network,
};
#[test]
fn test_shuffle() {
for _ in 0..1000 {
let key = rand_key();
- let mut addresses = encode_segwit_msg(Network::Bitcoin, &key);
+ let mut addresses = encode_segwit_msg(Network::RegTest, &key);
fastrand::shuffle(&mut addresses);
let decoded =
decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
@@ -168,7 +188,7 @@ mod test {
fn test_shuffle_many() {
for _ in 0..1000 {
let key = rand_key();
- let addresses = rand_addresses(&key);
+ let addresses = rand_addresses(Network::TestNet, &key);
let decoded =
decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
.unwrap();
diff --git a/src/main.rs b/src/main.rs
@@ -1,12 +1,11 @@
use std::{collections::HashSet, iter::repeat_with, path::PathBuf, str::FromStr};
-use bitcoin_bech32::{constants::Network, u5, WitnessProgram};
use bitcoincore_rpc::{
bitcoin::{Address, Amount, Txid},
jsonrpc::serde_json::Value,
Auth, Client, RpcApi,
};
-use depolymerization::{decode_segwit_msg, encode_segwit_msg, segwit_min_amount};
+use depolymerization::{decode_segwit_msg, encode_segwit_msg, segwit_min_amount, Network};
const CLIENT: &str = "client";
const WIRE: &str = "wire";
@@ -41,10 +40,6 @@ struct Args {
/// start as a receiver
#[argh(switch, short = 'w')]
wire: bool,
-
- /// a message to send
- #[argh(option)]
- msg: Option<String>,
}
fn common_rpc() -> Client {
Client::new(
@@ -91,7 +86,7 @@ fn send_with_metadata(
amount: Amount,
metadata: &[u8],
) -> bitcoincore_rpc::Result<Txid> {
- let addresses = encode_segwit_msg(Network::Regtest, &metadata.try_into().unwrap());
+ let addresses = encode_segwit_msg(Network::RegTest, &metadata.try_into().unwrap());
let mut recipients = vec![(to.to_string(), amount)];
recipients.extend(
addresses