Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Bitcoin P2WPKH-P2SH #35

Merged
merged 2 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions c/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,16 @@ static int _recover_secp256k1_pubkey(const uint8_t *sig, size_t sig_len,
}

// Refer to: https://en.bitcoin.it/wiki/BIP_0137
int get_btc_recid(uint8_t d, bool *compressed) {
int get_btc_recid(uint8_t d, bool *compressed, bool *p2sh_hash) {
*compressed = true;
*p2sh_hash = false;
if (d >= 27 && d <= 30) { // P2PKH uncompressed
*compressed = false;
return d - 27;
} else if (d >= 31 && d <= 34) { // P2PKH compressed
return d - 31;
} else if (d >= 35 && d <= 38) { // Segwit P2SH
*p2sh_hash = true;
return d - 35;
} else if (d >= 39 && d <= 42) { // Segwit Bech32
return d - 39;
Expand All @@ -156,6 +158,20 @@ int get_btc_recid(uint8_t d, bool *compressed) {
}
}

int bitcoin_hash160(const uint8_t *data, size_t size, uint8_t *output) {
int err = 0;
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
unsigned char temp[SHA256_SIZE];
err = md_string(md_info, data, size, temp);
if (err) return err;

md_info = mbedtls_md_info_from_type(MBEDTLS_MD_RIPEMD160);
err = md_string(md_info, temp, SHA256_SIZE, output);
if (err) return err;
return 0;
}

static int _recover_secp256k1_pubkey_btc(const uint8_t *sig, size_t sig_len,
const uint8_t *msg, size_t msg_len,
uint8_t *out_pubkey,
Expand All @@ -169,7 +185,8 @@ static int _recover_secp256k1_pubkey_btc(const uint8_t *sig, size_t sig_len,
return ERROR_INVALID_ARG;
}
bool compressed = true;
int recid = get_btc_recid(sig[0], &compressed);
bool p2sh_hash = false;
int recid = get_btc_recid(sig[0], &compressed, &p2sh_hash);
if (recid == -1) {
return ERROR_INVALID_ARG;
}
Expand Down Expand Up @@ -203,6 +220,15 @@ static int _recover_secp256k1_pubkey_btc(const uint8_t *sig, size_t sig_len,
return ERROR_WRONG_STATE;
}

if (p2sh_hash) {
int err =
bitcoin_hash160(out_pubkey, *out_pubkey_size, out_pubkey + 2);
if (err) return err;

out_pubkey[0] = 0;
out_pubkey[1] = 20; // RIPEMD160 size
*out_pubkey_size = 22;
}
} else {
*out_pubkey_size = UNCOMPRESSED_SECP256K1_PUBKEY_SIZE;
flag = SECP256K1_EC_UNCOMPRESSED;
Expand Down Expand Up @@ -320,15 +346,8 @@ int validate_signature_btc(void *prefilled_data, const uint8_t *sig,
&out_pubkey_size);
CHECK(err);

const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
unsigned char temp[SHA256_SIZE];
err = md_string(md_info, out_pubkey, out_pubkey_size, temp);
CHECK(err);

md_info = mbedtls_md_info_from_type(MBEDTLS_MD_RIPEMD160);
err = md_string(md_info, temp, SHA256_SIZE, temp);
CHECK(err);
unsigned char temp[AUTH160_SIZE];
err = bitcoin_hash160(out_pubkey, out_pubkey_size, temp);

memcpy(output, temp, AUTH160_SIZE);
*output_len = AUTH160_SIZE;
Expand Down Expand Up @@ -884,10 +903,7 @@ int convert_litecoin_message(const uint8_t *msg, size_t msg_len,
int convert_ripple_message(const uint8_t *msg, size_t msg_len, uint8_t *new_msg,
size_t new_msg_len) {
int err = 0;
CHECK(mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), msg, msg_len,
new_msg));
CHECK(mbedtls_md(mbedtls_md_info_from_type(MBEDTLS_MD_RIPEMD160), new_msg,
SHA256_SIZE, new_msg));
CHECK(bitcoin_hash160(msg, msg_len, new_msg));
memset(new_msg + 20, 0, 12);
exit:
return err;
Expand Down
118 changes: 107 additions & 11 deletions tools/ckb-auth-cli/src/chain_command/unisat.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{auth_script::run_auth_exec, utils::decode_string, BlockChain, BlockChainArgs};
use crate::{
auth_script::run_auth_exec,
utils::{calculate_sha256, decode_string},
BlockChain, BlockChainArgs,
};
use anyhow::{anyhow, Error};
use bitcoin::bech32::{self, FromBase32};
use ckb_auth_rs::AuthAlgorithmIdType;
Expand Down Expand Up @@ -30,6 +34,14 @@ impl BlockChainArgs for UnisatLockArgs {

pub struct UnisatLock {}

#[derive(PartialEq, Eq)]
enum UnisatLockAddressType {
NativeSegwit,
NestedSegwit,
Taproot,
Legacy,
}

impl BlockChain for UnisatLock {
fn parse(&self, _operate_mathches: &ArgMatches) -> Result<(), Error> {
Err(anyhow!("unisat does not parse"))
Expand All @@ -40,11 +52,11 @@ impl BlockChain for UnisatLock {
}

fn verify(&self, operate_mathches: &ArgMatches) -> Result<(), Error> {
let address = operate_mathches
.get_one::<String>("address")
.expect("Get address from args");
let (_hrp, address, _v) = bech32::decode(&address).expect("decode bech32");
let address = Vec::<u8>::from_base32(&address[1..33]).unwrap();
let (address, addr_type) = Self::get_publickey_hash(
operate_mathches
.get_one::<String>("address")
.expect("Get address from args"),
);

let mut signature = decode_string(
operate_mathches
Expand All @@ -61,20 +73,104 @@ impl BlockChain for UnisatLock {
)
.expect("decode message");

if address.len() < 20 {
return Err(anyhow!("unisat address invalidate"));
}
if signature.len() != 65 {
return Err(anyhow!("unisat signature size is not 65"));
}
signature[0] += 4;
if addr_type == UnisatLockAddressType::NestedSegwit {
let recid = (signature[0] - 27) % 4;
signature[0] = recid + 35;
} else {
let recid = (signature[0] - 27) % 4;
signature[0] = recid + 31;
}

if message.len() != 32 {
return Err(anyhow!("unisat message size is not 32"));
}

run_auth_exec(AuthAlgorithmIdType::Bitcoin, &address, &message, &signature)?;

println!("Signature verification succeeded!");
Ok(())
}
}

impl UnisatLock {
fn get_publickey_hash(address: &str) -> ([u8; 20], UnisatLockAddressType) {
let r_address = Self::parse_address_with_native_segwit(address);
if r_address.is_some() {
return (r_address.unwrap(), UnisatLockAddressType::NativeSegwit);
}

let r_address = Self::parse_address_with_nested_segwit(address);
if r_address.is_some() {
return (r_address.unwrap(), UnisatLockAddressType::NestedSegwit);
}

let r_address = Self::_parse_address_with_taproot(address);
if r_address.is_some() {
return (r_address.unwrap(), UnisatLockAddressType::Taproot);
}

let r_address = Self::parse_address_with_legacy(address);
if r_address.is_some() {
return (r_address.unwrap(), UnisatLockAddressType::Legacy);
}
panic!("unknow parse address");
}

fn parse_address_with_native_segwit(address: &str) -> Option<[u8; 20]> {
let r = bech32::decode(address);
if r.is_err() {
return None;
}
let (_hrp, address, _v) = r.unwrap();

let address = Vec::<u8>::from_base32(&address[1..33]);
if address.is_err() {
return None;
}
let address = address.unwrap();
if address.len() != 20 {
return None;
}

Some(address.try_into().unwrap())
}

fn parse_address_with_nested_segwit(address: &str) -> Option<[u8; 20]> {
let address = bs58::decode(address).into_vec();
if address.is_err() {
return None;
}
let address = address.unwrap();
Self::check_sum(&address);

Some(address[1..21].try_into().unwrap())
}

fn _parse_address_with_taproot(_address: &str) -> Option<[u8; 20]> {
// Unsupport
None
}

fn parse_address_with_legacy(address: &str) -> Option<[u8; 20]> {
let address = bs58::decode(address).into_vec();
if address.is_err() {
return None;
}
let address = address.unwrap();

if address.len() < 21 {
return None;
}

Self::check_sum(&address);

Some(address[1..21].try_into().unwrap())
}

fn check_sum(data: &[u8]) {
let checksum = calculate_sha256(&calculate_sha256(&data[0..21]));
assert_eq!(checksum[..4], data[21..]);
}
}