From 32ceb24ce542b2724afedb2fe9f23614e49a80ca Mon Sep 17 00:00:00 2001 From: Abiodun Awoyemi Date: Sat, 18 Jul 2026 01:04:00 +0100 Subject: [PATCH 1/2] feat: add BIP-322 signature variant prefixes --- src/sign.rs | 15 ++++++++++++--- src/util.rs | 5 +++++ src/verify.rs | 12 ++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/sign.rs b/src/sign.rs index e41a107..56c22b5 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -78,7 +78,10 @@ pub fn sign_simple_encoded( .consensus_encode(&mut buffer) .context(error::WitnessEncoding)?; - Ok(general_purpose::STANDARD.encode(buffer)) + Ok(format!( + "{SIMPLE_SIGNATURE_PREFIX}{}", + general_purpose::STANDARD.encode(buffer) + )) } /// Signs the BIP-322 full from spec-compliant string encodings. @@ -109,7 +112,10 @@ pub fn sign_full_encoded( tx.consensus_encode(&mut buffer) .context(error::TransactionEncode)?; - Ok(general_purpose::STANDARD.encode(buffer)) + Ok(format!( + "{FULL_SIGNATURE_PREFIX}{}", + general_purpose::STANDARD.encode(buffer) + )) } /// Signs in the BIP-322 simple format and returns the witness. @@ -194,7 +200,10 @@ pub fn sign_pof_encoded( .serialize_to_writer(&mut buffer) .context(error::TransactionEncode)?; - Ok(general_purpose::STANDARD.encode(buffer)) + Ok(format!( + "{POF_SIGNATURE_PREFIX}{}", + general_purpose::STANDARD.encode(buffer) + )) } /// Signs a BIP-322 full proof diff --git a/src/util.rs b/src/util.rs index a9026d3..dd04cf2 100644 --- a/src/util.rs +++ b/src/util.rs @@ -6,6 +6,11 @@ pub const BIP322_TAG: &str = "BIP0322-signed-message"; /// (PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE). pub const PSBT_GLOBAL_GENERIC_SIGNED_MESSAGE: u8 = 0x09; +/// Signature variant prefixes. +pub const SIMPLE_SIGNATURE_PREFIX: &str = "smp"; +pub const FULL_SIGNATURE_PREFIX: &str = "ful"; +pub const POF_SIGNATURE_PREFIX: &str = "pof"; + /// Create the tagged message hash. pub fn tagged_hash(tag: &str, message: impl AsRef<[u8]>) -> [u8; 32] { let tag_hash = Sha256::new().chain_update(tag).finalize(); diff --git a/src/verify.rs b/src/verify.rs index e8f3f49..1f04ef4 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -55,6 +55,10 @@ pub fn verify_simple_encoded(address: &str, message: &str, signature: &str) -> R .context(error::AddressParse { address })? .assume_checked(); + let signature = signature + .strip_prefix(SIMPLE_SIGNATURE_PREFIX) + .unwrap_or(signature); + let mut cursor = bitcoin::io::Cursor::new( general_purpose::STANDARD .decode(signature) @@ -74,6 +78,10 @@ pub fn verify_full_encoded(address: &str, message: &str, to_sign: &str) -> Resul .context(error::AddressParse { address })? .assume_checked(); + let to_sign = to_sign + .strip_prefix(FULL_SIGNATURE_PREFIX) + .unwrap_or(to_sign); + let mut cursor = bitcoin::io::Cursor::new(general_purpose::STANDARD.decode(to_sign).context( error::TransactionBase64Decode { transaction: to_sign, @@ -103,6 +111,10 @@ pub fn verify_pof_encoded( .context(error::AddressParse { address })? .assume_checked(); + let to_sign = to_sign + .strip_prefix(POF_SIGNATURE_PREFIX) + .unwrap_or(to_sign); + let bytes = general_purpose::STANDARD .decode(to_sign) From 61c35652e4a0a6adcfbb7b4c21cce4c54990d5e5 Mon Sep 17 00:00:00 2001 From: Abiodun Awoyemi Date: Sat, 18 Jul 2026 01:06:54 +0100 Subject: [PATCH 2/2] test: add test for signature variant prefixes --- src/lib.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b304652..8fb9cea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,8 +157,8 @@ mod tests { assert!( verify::verify_simple_encoded( TAPROOT_ADDRESS, - "Hello World", - "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" + "Hello World", + "smpAUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" ).is_ok() ); @@ -166,7 +166,7 @@ mod tests { verify::verify_simple_encoded( TAPROOT_ADDRESS, "Hello World -- This should fail", - "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" + "smpAUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" ).unwrap_err().to_string(), "Invalid signature" ); @@ -176,7 +176,7 @@ mod tests { fn simple_sign_taproot() { assert_eq!( sign::sign_simple_encoded(TAPROOT_ADDRESS, "Hello World", &[WIF_PRIVATE_KEY], None).unwrap(), - "AUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" + "smpAUHd69PrJQEv+oKTfZ8l+WROBHuy9HKrbFCJu7U1iK2iiEy1vMU5EfMtjc+VSHM7aU0SDbak5IUZRVno2P5mjSafAQ==" ); } @@ -216,8 +216,8 @@ mod tests { assert_eq!( verify::verify_simple_encoded( TAPROOT_ADDRESS, - "Hello World", - "AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViH" + "Hello World", + "smpAkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViH" ).unwrap_err().to_string(), "Decode error for signature `AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViH`" ) @@ -229,7 +229,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "Hello World", - "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" + "smpAkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" ).is_ok() ); @@ -237,7 +237,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "Hello World - this should fail", - "AkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" + "smpAkcwRAIgZRfIY3p7/DoVTty6YZbWS71bc5Vct9p9Fia83eRmw2QCICK/ENGfwLtptFluMGs2KsqoNSk89pO7F29zJLUx9a/sASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" ).is_err() ); @@ -245,7 +245,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "Hello World", - "AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" + "smpAkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" ).is_ok() ); @@ -253,7 +253,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "", - "AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" + "smpAkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" ).is_ok() ); @@ -261,7 +261,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "fail", - "AkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" + "smpAkcwRAIgM2gBAQqvZX15ZiysmKmQpDrG83avLIT492QBzLnQIxYCIBaTpOaD20qRlEylyxFSeEA2ba9YOixpX8z46TSDtS40ASECx/EgAxlkQpQ9hYjgGu6EBCPMVPwVIVJqO4XCsMvViHI=" ).is_err() ); @@ -269,7 +269,7 @@ mod tests { verify::verify_simple_encoded( SEGWIT_ADDRESS, "", - "AkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" + "smpAkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" ).is_ok() ); } @@ -294,12 +294,12 @@ mod tests { fn simple_sign_p2wpkh() { assert_eq!( sign::sign_simple_encoded(SEGWIT_ADDRESS, "Hello World", &[WIF_PRIVATE_KEY], None).unwrap(), - "AkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" + "smpAkgwRQIhAOzyynlqt93lOKJr+wmmxIens//zPzl9tqIOua93wO6MAiBi5n5EyAcPScOjf1lAqIUIQtr3zKNeavYabHyR8eGhowEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" ); assert_eq!( sign::sign_simple_encoded(SEGWIT_ADDRESS, "", &[WIF_PRIVATE_KEY], None).unwrap(), - "AkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" + "smpAkgwRQIhAPkJ1Q4oYS0htvyuSFHLxRQpFAY56b70UvE7Dxazen0ZAiAtZfFz1S6T6I23MWI2lK/pcNTWncuyL8UL+oMdydVgzAEhAsfxIAMZZEKUPYWI4BruhAQjzFT8FSFSajuFwrDL1Yhy" ); } @@ -328,14 +328,14 @@ mod tests { assert!(verify::verify_simple_encoded( NESTED_SEGWIT_ADDRESS, "Hello World", - "AkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" + "smpAkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" ).is_ok() ); assert!(verify::verify_simple_encoded( NESTED_SEGWIT_ADDRESS, "Hello World - this should fail", - "AkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" + "smpAkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" ).is_err() ); } @@ -344,7 +344,7 @@ mod tests { fn simple_sign_p2sh_p2wpkh() { assert_eq!( sign::sign_simple_encoded(NESTED_SEGWIT_ADDRESS, "Hello World", &[NESTED_SEGWIT_WIF_PRIVATE_KEY], None).unwrap(), - "AkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" + "smpAkgwRQIhAMd2wZSY3x0V9Kr/NClochoTXcgDaGl3OObOR17yx3QQAiBVWxqNSS+CKen7bmJTG6YfJjsggQ4Fa2RHKgBKrdQQ+gEhAxa5UDdQCHSQHfKQv14ybcYm1C9y6b12xAuukWzSnS+w" ); }