use crate::primitives::private_key::PrivateKey;
use crate::primitives::public_key::PublicKey;
use crate::primitives::random::random_bytes;
use crate::primitives::signature::Signature;
use crate::services::ServicesError;
const SIGNED_MESSAGE_VERSION: [u8; 4] = [0x42, 0x42, 0x33, 0x01];
pub fn sign(
message: &[u8],
signer: &PrivateKey,
verifier: Option<&PublicKey>,
) -> Result<Vec<u8>, ServicesError> {
let recipient_anyone = verifier.is_none();
let verifier_key = match verifier {
Some(v) => v.clone(),
None => {
let anyone = PrivateKey::from_hex("1")
.map_err(|e| ServicesError::Messages(format!("anyone key: {}", e)))?;
anyone.to_public_key()
}
};
let key_id = random_bytes(32);
let key_id_base64 = to_base64(&key_id);
let invoice_number = format!("2-message signing-{}", key_id_base64);
let signing_key = signer
.derive_child(&verifier_key, &invoice_number)
.map_err(|e| ServicesError::Messages(format!("derive signing key: {}", e)))?;
let signature = signing_key
.sign(message, true)
.map_err(|e| ServicesError::Messages(format!("sign: {}", e)))?;
let sig_der = signature.to_der();
let sender_pubkey = signer.to_public_key().to_der();
let mut result =
Vec::with_capacity(4 + 33 + if recipient_anyone { 1 } else { 33 } + 32 + sig_der.len());
result.extend_from_slice(&SIGNED_MESSAGE_VERSION);
result.extend_from_slice(&sender_pubkey);
if recipient_anyone {
result.push(0x00);
} else {
result.extend_from_slice(&verifier_key.to_der());
}
result.extend_from_slice(&key_id);
result.extend_from_slice(&sig_der);
Ok(result)
}
pub fn verify(
signed_message: &[u8],
message: &[u8],
recipient: Option<&PrivateKey>,
) -> Result<PublicKey, ServicesError> {
if signed_message.len() < 4 {
return Err(ServicesError::Messages(
"signed message too short".to_string(),
));
}
let version = &signed_message[0..4];
if version != SIGNED_MESSAGE_VERSION {
return Err(ServicesError::Messages(format!(
"Message version mismatch: expected {:02x}{:02x}{:02x}{:02x}, received {:02x}{:02x}{:02x}{:02x}",
SIGNED_MESSAGE_VERSION[0], SIGNED_MESSAGE_VERSION[1],
SIGNED_MESSAGE_VERSION[2], SIGNED_MESSAGE_VERSION[3],
version[0], version[1], version[2], version[3]
)));
}
let mut pos = 4;
if pos + 33 > signed_message.len() {
return Err(ServicesError::Messages(
"truncated sender pubkey".to_string(),
));
}
let sender_bytes = &signed_message[pos..pos + 33];
let sender = PublicKey::from_der_bytes(sender_bytes)
.map_err(|e| ServicesError::Messages(format!("invalid sender pubkey: {}", e)))?;
pos += 33;
if pos >= signed_message.len() {
return Err(ServicesError::Messages(
"truncated verifier field".to_string(),
));
}
let verifier_first = signed_message[pos];
pos += 1;
let actual_recipient: PrivateKey;
if verifier_first == 0x00 {
actual_recipient = PrivateKey::from_hex("1")
.map_err(|e| ServicesError::Messages(format!("anyone key: {}", e)))?;
} else {
if pos + 32 > signed_message.len() {
return Err(ServicesError::Messages(
"truncated verifier pubkey".to_string(),
));
}
let mut verifier_bytes = vec![verifier_first];
verifier_bytes.extend_from_slice(&signed_message[pos..pos + 32]);
pos += 32;
let verifier_pubkey = PublicKey::from_der_bytes(&verifier_bytes)
.map_err(|e| ServicesError::Messages(format!("invalid verifier pubkey: {}", e)))?;
match recipient {
Some(r) => {
let recipient_pubkey = r.to_public_key();
if recipient_pubkey != verifier_pubkey {
return Err(ServicesError::Messages(format!(
"Recipient public key mismatch: expected {}, got {}",
hex_encode(&verifier_pubkey.to_der()),
hex_encode(&recipient_pubkey.to_der()),
)));
}
actual_recipient = r.clone();
}
None => {
return Err(ServicesError::Messages(format!(
"This signature requires a specific private key to verify. \
Associated public key: {}",
hex_encode(&verifier_pubkey.to_der()),
)));
}
}
}
if pos + 32 > signed_message.len() {
return Err(ServicesError::Messages("truncated keyID".to_string()));
}
let key_id = &signed_message[pos..pos + 32];
pos += 32;
if pos >= signed_message.len() {
return Err(ServicesError::Messages("truncated signature".to_string()));
}
let sig_der = &signed_message[pos..];
let signature = Signature::from_der(sig_der)
.map_err(|e| ServicesError::Messages(format!("invalid DER signature: {}", e)))?;
let key_id_base64 = to_base64(key_id);
let invoice_number = format!("2-message signing-{}", key_id_base64);
let verification_key = sender
.derive_child(&actual_recipient, &invoice_number)
.map_err(|e| ServicesError::Messages(format!("derive verification key: {}", e)))?;
if verification_key.verify(message, &signature) {
Ok(sender)
} else {
Err(ServicesError::Messages(
"signature verification failed".to_string(),
))
}
}
fn to_base64(data: &[u8]) -> String {
const ALPHABET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut result = String::new();
let chunks = data.chunks(3);
for chunk in chunks {
let b0 = chunk[0] as u32;
let b1 = if chunk.len() > 1 { chunk[1] as u32 } else { 0 };
let b2 = if chunk.len() > 2 { chunk[2] as u32 } else { 0 };
let triple = (b0 << 16) | (b1 << 8) | b2;
result.push(ALPHABET[((triple >> 18) & 0x3F) as usize] as char);
result.push(ALPHABET[((triple >> 12) & 0x3F) as usize] as char);
if chunk.len() > 1 {
result.push(ALPHABET[((triple >> 6) & 0x3F) as usize] as char);
} else {
result.push('=');
}
if chunk.len() > 2 {
result.push(ALPHABET[(triple & 0x3F) as usize] as char);
} else {
result.push('=');
}
}
result
}
fn hex_encode(bytes: &[u8]) -> String {
bytes.iter().map(|b| format!("{:02x}", b)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sign_verify_round_trip_specific_verifier() {
let signer = PrivateKey::from_hex(
"e6baf19a8b0f6d2ab882bc22e53ef18587e1b2a35ce998d4dbeb3e5f97647e0e",
)
.unwrap();
let verifier_priv = PrivateKey::from_hex(
"c8bc2c2575f2cf350b5aa0f3497b4e38f70270cd53d23b29da54ea3a685bbf39",
)
.unwrap();
let verifier_pub = verifier_priv.to_public_key();
let message = b"Hello BRC-77!";
let signed = sign(message, &signer, Some(&verifier_pub)).unwrap();
let sender_back = verify(&signed, message, Some(&verifier_priv)).unwrap();
let expected_sender = signer.to_public_key();
assert_eq!(sender_back.to_der(), expected_sender.to_der());
}
#[test]
fn test_sign_verify_round_trip_anyone() {
let signer = PrivateKey::from_hex(
"e6baf19a8b0f6d2ab882bc22e53ef18587e1b2a35ce998d4dbeb3e5f97647e0e",
)
.unwrap();
let message = b"Public announcement";
let signed = sign(message, &signer, None).unwrap();
let sender_back = verify(&signed, message, None).unwrap();
let expected_sender = signer.to_public_key();
assert_eq!(sender_back.to_der(), expected_sender.to_der());
}
#[test]
fn test_verify_rejects_tampered_message() {
let signer = PrivateKey::from_hex(
"e6baf19a8b0f6d2ab882bc22e53ef18587e1b2a35ce998d4dbeb3e5f97647e0e",
)
.unwrap();
let message = b"Original message";
let signed = sign(message, &signer, None).unwrap();
let tampered = b"Tampered message";
let result = verify(&signed, tampered, None);
assert!(result.is_err());
}
#[test]
fn test_verify_rejects_wrong_version() {
let mut bad_msg = vec![0xFF, 0xFF, 0xFF, 0xFF];
bad_msg.extend_from_slice(&[0u8; 100]);
let result = verify(&bad_msg, b"message", None);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("version mismatch"));
}
#[test]
fn test_signed_message_format_anyone() {
let signer = PrivateKey::from_hex("1").unwrap();
let message = b"test";
let signed = sign(message, &signer, None).unwrap();
assert!(signed.len() >= 4 + 33 + 1 + 32);
assert_eq!(&signed[0..4], &SIGNED_MESSAGE_VERSION);
assert_eq!(signed[37], 0x00);
}
#[test]
fn test_signed_message_format_specific() {
let signer = PrivateKey::from_hex("1").unwrap();
let verifier_priv = PrivateKey::from_hex("2").unwrap();
let verifier_pub = verifier_priv.to_public_key();
let message = b"test";
let signed = sign(message, &signer, Some(&verifier_pub)).unwrap();
assert!(signed.len() >= 4 + 33 + 33 + 32);
assert_eq!(&signed[0..4], &SIGNED_MESSAGE_VERSION);
assert!(signed[37] == 0x02 || signed[37] == 0x03);
}
}