Skip to content

Commit

Permalink
Implement Message Signing (kaspanet#278)
Browse files Browse the repository at this point in the history
* Implement message signing

* Add unit tests with aux_rand following kip

* Move message signing mod

* Pass pubkey as ref for verify_message

* Change message inputs to refs; update verify api

Added unit tests for invalid signature

* Fix test redundant structure

* Make attribute of PersonalMessage public
  • Loading branch information
coderofstuff authored Sep 12, 2023
1 parent e1bb62b commit 405cc14
Show file tree
Hide file tree
Showing 3 changed files with 250 additions and 0 deletions.
1 change: 1 addition & 0 deletions crypto/hashes/src/hashers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ blake2b_hasher! {
struct MerkleBranchHash => b"MerkleBranchHash",
struct MuHashElementHash => b"MuHashElement",
struct MuHashFinalizeHash => b"MuHashFinalize",
struct PersonalMessageSigningHash => b"PersonalMessageSigningHash",
}

sha256_hasher! {
Expand Down
1 change: 1 addition & 0 deletions wallet/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod encryption;
pub mod error;
pub mod events;
mod imports;
pub mod message;
pub mod result;
pub mod runtime;
pub mod secret;
Expand Down
248 changes: 248 additions & 0 deletions wallet/core/src/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
use kaspa_hashes::{Hash, PersonalMessageSigningHash};
use secp256k1::{Error, XOnlyPublicKey};

#[derive(Clone)]
pub struct PersonalMessage<'a>(pub &'a str);

impl AsRef<[u8]> for PersonalMessage<'_> {
fn as_ref(&self) -> &[u8] {
self.0.as_bytes()
}
}

pub fn sign_message(msg: &PersonalMessage, privkey: &[u8; 32]) -> Result<Vec<u8>, Error> {
let hash = calc_personal_message_hash(msg);

let msg = secp256k1::Message::from_slice(hash.as_bytes().as_slice())?;
let schnorr_key = secp256k1::KeyPair::from_seckey_slice(secp256k1::SECP256K1, privkey)?;
let sig: [u8; 64] = *schnorr_key.sign_schnorr(msg).as_ref();

Ok(sig.to_vec())
}

pub fn sign_message_with_aux_rand(msg: &PersonalMessage, privkey: &[u8; 32], aux_rand: &[u8; 32]) -> Result<Vec<u8>, Error> {
let hash = calc_personal_message_hash(msg);

let msg = secp256k1::Message::from_slice(hash.as_bytes().as_slice())?;
let schnorr_key = secp256k1::KeyPair::from_seckey_slice(secp256k1::SECP256K1, privkey)?;
let curve = secp256k1::Secp256k1::new();
let sig: [u8; 64] = *curve.sign_schnorr_with_aux_rand(&msg, &schnorr_key, aux_rand).as_ref();

Ok(sig.to_vec())
}

/// Ok(()) if the signature matches the given message and pubkey
/// Error if any of the inputs are incorrect, or the signature is invalid
pub fn verify_message(msg: &PersonalMessage, signature: &Vec<u8>, pubkey: &XOnlyPublicKey) -> Result<(), Error> {
let hash = calc_personal_message_hash(msg);
let msg = secp256k1::Message::from_slice(hash.as_bytes().as_slice())?;
let sig = secp256k1::schnorr::Signature::from_slice(signature.as_slice())?;
sig.verify(&msg, pubkey)
}

fn calc_personal_message_hash(msg: &PersonalMessage) -> Hash {
let mut hasher = PersonalMessageSigningHash::new();
hasher.write(msg);
hasher.finalize()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_basic_sign_and_verify_sign() {
let pm = PersonalMessage("Hello Kaspa!");
let privkey: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45,
0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9,
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
}

#[test]
fn test_kanji_sign_and_verify_sign() {
let pm = PersonalMessage("こんにちは世界");
let privkey: [u8; 32] = [
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F,
0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D,
0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59,
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
}

#[test]
fn test_long_text_sign_and_verify_sign() {
let pm = PersonalMessage("Lorem ipsum dolor sit amet. Aut omnis amet id voluptatem eligendi sit accusantium dolorem 33 corrupti necessitatibus hic consequatur quod et maiores alias non molestias suscipit? Est voluptatem magni qui odit eius est eveniet cupiditate id eius quae aut molestiae nihil eum excepturi voluptatem qui nisi architecto?
Et aliquid ipsa ut quas enim et dolorem deleniti ut eius dicta non praesentium neque est velit numquam. Ut consectetur amet ut error veniam et officia laudantium ea velit nesciunt est explicabo laudantium sit totam aperiam.
Ut omnis magnam et accusamus earum rem impedit provident eum commodi repellat qui dolores quis et voluptate labore et adipisci deleniti. Est nostrum explicabo aut quibusdam labore et molestiae voluptate. Qui omnis nostrum At libero deleniti et quod quia.");
let privkey: [u8; 32] = [
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F,
0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D,
0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59,
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
}

#[test]
fn test_fail_verify() {
let pm = PersonalMessage("Not Hello Kaspa!");
let pubkey = XOnlyPublicKey::from_slice(&[
0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45,
0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9,
])
.unwrap();
let fake_sig: Vec<u8> = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
]
.to_vec();

let verify_result = verify_message(&pm, &fake_sig, &pubkey);
assert!(verify_result.is_err());
}

#[test]
fn test_sign_and_verify_test_case_0() {
let pm = PersonalMessage("Hello Kaspa!");
let privkey: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
];
let aux_rand: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10, 0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29, 0xB5, 0x31, 0xC8, 0x45,
0x83, 0x6F, 0x99, 0xB0, 0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9,
])
.unwrap();
let expected_sig: Vec<u8> = [
0x40, 0xB9, 0xBB, 0x2B, 0xE0, 0xAE, 0x02, 0x60, 0x72, 0x79, 0xED, 0xA6, 0x40, 0x15, 0xA8, 0xD8, 0x6E, 0x37, 0x63, 0x27,
0x91, 0x70, 0x34, 0x0B, 0x82, 0x43, 0xF7, 0xCE, 0x53, 0x44, 0xD7, 0x7A, 0xFF, 0x11, 0x91, 0x59, 0x8B, 0xAF, 0x2F, 0xD2,
0x61, 0x49, 0xCA, 0xC3, 0xB4, 0xB1, 0x2C, 0x2C, 0x43, 0x32, 0x61, 0xC0, 0x08, 0x34, 0xDB, 0x60, 0x98, 0xCB, 0x17, 0x2A,
0xA4, 0x8E, 0xF5, 0x22,
]
.to_vec();

let sig_result = sign_message_with_aux_rand(&pm, &privkey, &aux_rand).expect("sign_message failed");
assert_eq!(expected_sig, sig_result);

verify_message(&pm, &sig_result, &pubkey).expect("verify_message failed");
}

#[test]
fn test_sign_and_verify_test_case_1() {
let pm = PersonalMessage("Hello Kaspa!");
let privkey: [u8; 32] = [
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F,
0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF,
];
let aux_rand: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D,
0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59,
])
.unwrap();
let expected_sig: Vec<u8> = [
0xEB, 0x9E, 0x8A, 0x3C, 0x54, 0x7E, 0xB9, 0x1B, 0x6A, 0x75, 0x92, 0x64, 0x4F, 0x32, 0x8F, 0x06, 0x48, 0xBD, 0xD2, 0x1A,
0xBA, 0x3C, 0xD4, 0x47, 0x87, 0xD4, 0x29, 0xD4, 0xD7, 0x90, 0xAA, 0x8B, 0x96, 0x27, 0x45, 0x69, 0x1F, 0x3B, 0x47, 0x2E,
0xD8, 0xD6, 0x5F, 0x3B, 0x77, 0x0E, 0xCB, 0x4F, 0x77, 0x7B, 0xD1, 0x7B, 0x1D, 0x30, 0x91, 0x00, 0x91, 0x9B, 0x53, 0xE0,
0xE2, 0x06, 0xB4, 0xC6,
]
.to_vec();

let sig_result = sign_message_with_aux_rand(&pm, &privkey, &aux_rand).expect("sign_message failed");
assert_eq!(expected_sig, sig_result);

verify_message(&pm, &sig_result, &pubkey).expect("verify_message failed");
}

#[test]
fn test_sign_and_verify_test_case_2() {
let pm = PersonalMessage("こんにちは世界");
let privkey: [u8; 32] = [
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F,
0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF,
];
let aux_rand: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D,
0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59,
])
.unwrap();
let expected_sig: Vec<u8> = [
0x81, 0x06, 0x53, 0xD5, 0xF8, 0x02, 0x06, 0xDB, 0x51, 0x96, 0x72, 0x36, 0x2A, 0xDD, 0x6C, 0x98, 0xDA, 0xD3, 0x78, 0x84,
0x4E, 0x5B, 0xA4, 0xD8, 0x9A, 0x22, 0xC9, 0xF0, 0xC7, 0x09, 0x2E, 0x8C, 0xEC, 0xBA, 0x73, 0x4F, 0xFF, 0x79, 0x22, 0xB6,
0x56, 0xB4, 0xBE, 0x3F, 0x4B, 0x1F, 0x09, 0x88, 0x99, 0xC9, 0x5C, 0xB5, 0xC1, 0x02, 0x3D, 0xCE, 0x35, 0x19, 0x20, 0x8A,
0xFA, 0xFB, 0x59, 0xBC,
]
.to_vec();

let sig_result = sign_message_with_aux_rand(&pm, &privkey, &aux_rand).expect("sign_message failed");
assert_eq!(expected_sig, sig_result);

verify_message(&pm, &sig_result, &pubkey).expect("verify_message failed");
}

#[test]
fn test_sign_and_verify_test_case_3() {
let pm = PersonalMessage("Lorem ipsum dolor sit amet. Aut omnis amet id voluptatem eligendi sit accusantium dolorem 33 corrupti necessitatibus hic consequatur quod et maiores alias non molestias suscipit? Est voluptatem magni qui odit eius est eveniet cupiditate id eius quae aut molestiae nihil eum excepturi voluptatem qui nisi architecto?
Et aliquid ipsa ut quas enim et dolorem deleniti ut eius dicta non praesentium neque est velit numquam. Ut consectetur amet ut error veniam et officia laudantium ea velit nesciunt est explicabo laudantium sit totam aperiam.
Ut omnis magnam et accusamus earum rem impedit provident eum commodi repellat qui dolores quis et voluptate labore et adipisci deleniti. Est nostrum explicabo aut quibusdam labore et molestiae voluptate. Qui omnis nostrum At libero deleniti et quod quia.");
let privkey: [u8; 32] = [
0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F,
0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF,
];
let aux_rand: [u8; 32] = [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
let pubkey = XOnlyPublicKey::from_slice(&[
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE, 0x58, 0xFE, 0xAE, 0x1D,
0xA2, 0xDE, 0xCE, 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59,
])
.unwrap();
let expected_sig: Vec<u8> = [
0x40, 0xCB, 0xBD, 0x39, 0x38, 0x86, 0x7B, 0x10, 0x07, 0x6B, 0xB1, 0x48, 0x35, 0x55, 0x7C, 0x06, 0x2F, 0x5B, 0xF6, 0xA4,
0x68, 0x29, 0x95, 0xFC, 0x8B, 0x0A, 0x1C, 0xD2, 0xED, 0x98, 0x6E, 0xED, 0xAA, 0xA0, 0x0C, 0xFE, 0x04, 0xF6, 0xC9, 0xE5,
0xA9, 0x54, 0x6B, 0x86, 0x07, 0x32, 0xE5, 0xB9, 0x03, 0xCC, 0x82, 0x78, 0x02, 0x28, 0x64, 0x7D, 0x53, 0x75, 0xBE, 0xC3,
0xD2, 0xA4, 0x98, 0x3A,
]
.to_vec();

let sig_result = sign_message_with_aux_rand(&pm, &privkey, &aux_rand).expect("sign_message failed");
assert_eq!(expected_sig, sig_result);

verify_message(&pm, &sig_result, &pubkey).expect("verify_message failed");
}
}

0 comments on commit 405cc14

Please sign in to comment.