1use std::io::Write;
16
17use bitcoin::hashes::{Hash, sha256};
18use bitcoin::secp256k1::{schnorr, Keypair, Message, PublicKey};
19
20use crate::SECP;
21use crate::address::Address;
22
23const CHALLENGE_MESSAGE_PREFIX: &[u8] = b"bark/message";
24
25fn signable_digest(message: &[u8]) -> Message {
27 let mut engine = sha256::Hash::engine();
28 engine.write_all(CHALLENGE_MESSAGE_PREFIX).unwrap();
29 engine.write_all(message).unwrap();
30
31 let hash = sha256::Hash::from_engine(engine).to_byte_array();
32 Message::from_digest(hash)
33}
34
35pub fn sign(keypair: &Keypair, message: &[u8]) -> schnorr::Signature {
40 SECP.sign_schnorr_with_aux_rand(&signable_digest(message), keypair, &rand::random())
41}
42
43pub fn verify(
46 pubkey: PublicKey,
47 message: &[u8],
48 signature: &schnorr::Signature,
49) -> bool {
50 SECP.verify_schnorr(signature, &signable_digest(message), &pubkey.into()).is_ok()
51}
52
53pub fn verify_for_address(
56 address: &Address,
57 message: &[u8],
58 signature: &schnorr::Signature,
59) -> bool {
60 verify(address.policy().user_pubkey(), message, signature)
61}
62
63#[cfg(test)]
64mod test {
65 use std::str::FromStr;
66
67 use crate::address::VtxoDelivery;
68 use crate::mailbox::BlindedMailboxIdentifier;
69
70 use super::*;
71
72 #[test]
73 fn sign_and_verify() {
74 let keypair = Keypair::from_str(
75 "6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
76 ).unwrap();
77 let message = b"hello ark";
78
79 let sig = sign(&keypair, message);
80 assert!(verify(keypair.public_key(), message, &sig));
81
82 assert!(!verify(keypair.public_key(), b"hello bark", &sig));
84
85 let other = Keypair::from_str(
87 "0000000000000000000000000000000000000000000000000000000000000001",
88 ).unwrap();
89 assert!(!verify(other.public_key(), message, &sig));
90
91 let plain = Message::from_digest(
93 bitcoin::hashes::sha256::Hash::hash(message).to_byte_array(),
94 );
95 let plain_sig = SECP.sign_schnorr_no_aux_rand(&plain, &keypair);
96 assert!(!verify(keypair.public_key(), message, &plain_sig));
97 }
98
99 #[test]
100 fn verify_message_for_address() {
101 let server = PublicKey::from_str(
102 "02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3",
103 ).unwrap();
104 let keypair = Keypair::from_str(
105 "6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
106 ).unwrap();
107 let blinded_id = BlindedMailboxIdentifier::from_pubkey(keypair.public_key());
108 let addr = Address::builder()
109 .server_pubkey(server)
110 .pubkey_policy(keypair.public_key())
111 .delivery(VtxoDelivery::ServerMailbox { blinded_id })
112 .into_address().unwrap();
113
114 let message = b"hello ark";
115 let sig = sign(&keypair, message);
116 assert!(verify_for_address(&addr, message, &sig));
117 assert!(!verify_for_address(&addr, b"hello bark", &sig));
118 }
119
120 #[test]
125 fn test_vector() {
126 let keypair = Keypair::from_str(
127 "6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
128 ).unwrap();
129 let message = b"ark signmessage test vector";
130
131 let digest = signable_digest(message);
132 let sig = SECP.sign_schnorr_no_aux_rand(&digest, &keypair);
133 assert_eq!(sig.to_string(),
134 "84e04017d4afeb4e945a2f69d2d969edfc5c15a0ced4397ea9dcc07a34bffb15\
135 f5ae22424126adc2bc23183b6949f0ec5113e5c99081ffde241c653772e9c64f",
136 );
137 assert!(verify(keypair.public_key(), message, &sig));
138 }
139}