Skip to content

Commit

Permalink
Add signing
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph committed Aug 20, 2024
1 parent 2ccdd27 commit 8103fee
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 30 deletions.
69 changes: 66 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ mod tests {
const WIF_PRIVATE_KEY: &str = "L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k";
const SEGWIT_ADDRESS: &str = "bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l";
const TAPROOT_ADDRESS: &str = "bc1ppv609nr0vr25u07u95waq5lucwfm6tde4nydujnu8npg4q75mr5sxq8lt3";
const SEGWIT_ADDRESS_LOCAL: &str = "bc1qvf8tafcx6yncee9f7jvza5nhnyalp32jjuhw6w";

#[test]
fn message_hashes_are_correct() {
Expand Down Expand Up @@ -266,7 +265,71 @@ mod tests {
}

#[test]
fn verify_valid_p2wpkh_signature() {
assert!(verify_simple_encoded(SEGWIT_ADDRESS_LOCAL, "Hello World gotcha", "AkcwRAIgJcWpci9gC4x9YMSGReCXx6IrylfHz8LzHrid8QXU4DECICMh74J1qn/XT8BGNCtxsFxa2rX7r02BYsLALYMNnDF0ASECcB+M459ms4JoioDlHLut7acdmMxFdMwsNCTlpoZWS2s=").is_ok())
fn simple_verify_and_falsify_p2wpkh() {
assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"Hello World",
"AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="
).is_ok()
);

assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"Hello World - this should fail",
"AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="
).is_err()
);

assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"Hello World",
"AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy"
).is_ok()
);

assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"",
"AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="
).is_ok()
);

assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"fail",
"AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI="
).is_err()
);

assert!(
verify_simple_encoded(
SEGWIT_ADDRESS,
"",
"AkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy"
).is_ok()
);
}

#[test]
fn simple_sign_p2wpkh() {
assert_eq!(
sign_simple_encoded(SEGWIT_ADDRESS, "Hello World", WIF_PRIVATE_KEY).unwrap(),
"AkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy"
);
}

#[test]
fn roundtrip_p2wpkh_simple() {
assert!(verify_simple_encoded(
SEGWIT_ADDRESS,
"Hello World",
&sign_simple_encoded(SEGWIT_ADDRESS, "Hello World", WIF_PRIVATE_KEY).unwrap()
)
.is_ok());
}
}
93 changes: 77 additions & 16 deletions src/sign.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use bitcoin::EcdsaSighashType;

use super::*;

/// Signs the BIP-322 simple from encoded values, i.e. address encoding, message string and
Expand Down Expand Up @@ -54,40 +56,99 @@ pub fn sign_full(
message: &[u8],
private_key: PrivateKey,
) -> Result<Transaction> {
if let bitcoin::address::Payload::WitnessProgram(witness_program) = address.payload() {
if witness_program.version().to_num() != 1 {
let to_spend = create_to_spend(address, message)?;
let mut to_sign = create_to_sign(&to_spend, None)?;

let witness =
if let bitcoin::address::Payload::WitnessProgram(witness_program) = address.payload() {
let version = witness_program.version().to_num();
let program_len = witness_program.program().len();

match version {
0 => {
if program_len != 20 {
return Err(Error::NotKeyPathSpend); // TODO
}
create_message_signature_p2wpkh(&to_spend, &to_sign, private_key)
}
1 => {
if program_len != 32 {
return Err(Error::NotKeyPathSpend); // TODO
}
create_message_signature_taproot(&to_spend, &to_sign, private_key)
}
_ => {
return Err(Error::UnsupportedAddress {
address: address.to_string(),
})
}
}
} else {
return Err(Error::UnsupportedAddress {
// TODO maybe rename to addres type?
address: address.to_string(),
});
}
};

if witness_program.program().len() != 32 {
return Err(Error::NotKeyPathSpend);
}
} else {
return Err(Error::UnsupportedAddress {
address: address.to_string(),
});
};
dbg!(&witness);

let to_spend = create_to_spend(address, message)?;
let mut to_sign = create_to_sign(&to_spend, None)?;

let witness = create_message_signature(&to_spend, &to_sign, private_key);
to_sign.inputs[0].final_script_witness = Some(witness);

to_sign.extract_tx().context(error::TransactionExtract)
}

fn create_message_signature(
fn create_message_signature_p2wpkh(
to_spend_tx: &Transaction,
to_sign: &Psbt,
private_key: PrivateKey,
) -> Witness {
let secp = Secp256k1::new();
let sighash_type = EcdsaSighashType::All;
let mut sighash_cache = SighashCache::new(to_sign.unsigned_tx.clone());

let sighash = sighash_cache
.p2wpkh_signature_hash(
0,
&to_spend_tx.output[0].script_pubkey,
to_spend_tx.output[0].value,
sighash_type,
)
.expect("signature hash should compute");

let sig = secp.sign_ecdsa(
&secp256k1::Message::from_digest_slice(sighash.as_ref())
.expect("should be cryptographically secure hash"),
&private_key.inner,
);

let witness = sighash_cache
.witness_mut(0)
.expect("getting mutable witness reference should work");

witness.push(
bitcoin::ecdsa::Signature {
sig,
hash_ty: sighash_type,
}
.to_vec(),
);

witness.push(private_key.public_key(&secp).to_bytes());

witness.to_owned()
}

fn create_message_signature_taproot(
to_spend_tx: &Transaction,
to_sign: &Psbt,
private_key: PrivateKey,
) -> Witness {
println!("here");
let mut to_sign = to_sign.clone();

let secp = Secp256k1::new();
let key_pair = Keypair::from_secret_key(&secp, &private_key.inner);

let (x_only_public_key, _parity) = XOnlyPublicKey::from_keypair(&key_pair);
to_sign.inputs[0].tap_internal_key = Some(x_only_public_key);

Expand Down
21 changes: 10 additions & 11 deletions src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,16 @@ fn verify_full_p2wpkh(
return Err(Error::PublicKeyMismatch);
}

let (signature, sighash_type) = match encoded_signature.len() {
71 | 72 => {
let len = encoded_signature.len();
(
bitcoin::secp256k1::ecdsa::Signature::from_der_lax(
&encoded_signature.as_slice()[..len - 1],
)
.context(error::SignatureInvalid)?,
EcdsaSighashType::from_consensus(encoded_signature[len - 1] as u32),
let signature_length = encoded_signature.len();

let (signature, sighash_type) = match signature_length {
71 | 72 => (
bitcoin::secp256k1::ecdsa::Signature::from_der(
&encoded_signature.as_slice()[..signature_length - 1],
)
}
.context(error::SignatureInvalid)?,
EcdsaSighashType::from_consensus(encoded_signature[signature_length - 1] as u32),
),
_ => {
return Err(Error::SignatureLength {
length: encoded_signature.len(),
Expand Down Expand Up @@ -161,7 +160,7 @@ fn verify_full_p2wpkh(
Message::from_digest_slice(sighash.as_ref()).expect("should be cryptographically secure hash");

Secp256k1::verification_only()
.verify_ecdsa(&message, &(signature), &pub_key.inner)
.verify_ecdsa(&message, &signature, &pub_key.inner)
.context(error::SignatureInvalid)?;

Ok(())
Expand Down

0 comments on commit 8103fee

Please sign in to comment.