use std::io::Write;
use bitcoin::hashes::{Hash, sha256};
use bitcoin::secp256k1::{schnorr, Keypair, Message, PublicKey};
use crate::SECP;
use crate::address::Address;
const CHALLENGE_MESSAGE_PREFIX: &[u8] = b"bark/message";
fn signable_digest(message: &[u8]) -> Message {
let mut engine = sha256::Hash::engine();
engine.write_all(CHALLENGE_MESSAGE_PREFIX).unwrap();
engine.write_all(message).unwrap();
let hash = sha256::Hash::from_engine(engine).to_byte_array();
Message::from_digest(hash)
}
pub fn sign(keypair: &Keypair, message: &[u8]) -> schnorr::Signature {
SECP.sign_schnorr_with_aux_rand(&signable_digest(message), keypair, &rand::random())
}
pub fn verify(
pubkey: PublicKey,
message: &[u8],
signature: &schnorr::Signature,
) -> bool {
SECP.verify_schnorr(signature, &signable_digest(message), &pubkey.into()).is_ok()
}
pub fn verify_for_address(
address: &Address,
message: &[u8],
signature: &schnorr::Signature,
) -> bool {
verify(address.policy().user_pubkey(), message, signature)
}
#[cfg(test)]
mod test {
use std::str::FromStr;
use crate::address::VtxoDelivery;
use crate::mailbox::BlindedMailboxIdentifier;
use super::*;
#[test]
fn sign_and_verify() {
let keypair = Keypair::from_str(
"6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
).unwrap();
let message = b"hello ark";
let sig = sign(&keypair, message);
assert!(verify(keypair.public_key(), message, &sig));
assert!(!verify(keypair.public_key(), b"hello bark", &sig));
let other = Keypair::from_str(
"0000000000000000000000000000000000000000000000000000000000000001",
).unwrap();
assert!(!verify(other.public_key(), message, &sig));
let plain = Message::from_digest(
bitcoin::hashes::sha256::Hash::hash(message).to_byte_array(),
);
let plain_sig = SECP.sign_schnorr_no_aux_rand(&plain, &keypair);
assert!(!verify(keypair.public_key(), message, &plain_sig));
}
#[test]
fn verify_message_for_address() {
let server = PublicKey::from_str(
"02037188bdd7579a0cd0b22a51110986df1ea08e30192658fe0e219590e4a723d3",
).unwrap();
let keypair = Keypair::from_str(
"6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
).unwrap();
let blinded_id = BlindedMailboxIdentifier::from_pubkey(keypair.public_key());
let addr = Address::builder()
.server_pubkey(server)
.pubkey_policy(keypair.public_key())
.delivery(VtxoDelivery::ServerMailbox { blinded_id })
.into_address().unwrap();
let message = b"hello ark";
let sig = sign(&keypair, message);
assert!(verify_for_address(&addr, message, &sig));
assert!(!verify_for_address(&addr, b"hello bark", &sig));
}
#[test]
fn test_vector() {
let keypair = Keypair::from_str(
"6b0f024af54172a9aed9a0f044689175787676c469ff2aa75024cae5445c7a02",
).unwrap();
let message = b"ark signmessage test vector";
let digest = signable_digest(message);
let sig = SECP.sign_schnorr_no_aux_rand(&digest, &keypair);
assert_eq!(sig.to_string(),
"84e04017d4afeb4e945a2f69d2d969edfc5c15a0ced4397ea9dcc07a34bffb15\
f5ae22424126adc2bc23183b6949f0ec5113e5c99081ffde241c653772e9c64f",
);
assert!(verify(keypair.public_key(), message, &sig));
}
}