Skip to content

Commit

Permalink
feat: add signMessage noAuxRand option for kaspa wasm (#587)
Browse files Browse the repository at this point in the history
* feat: add signMessageWithoutRand method for kaspa wasm

* enhance: sign message api

* fix: unit test fail

* chore: update noAuxRand of ISignMessage

* chore: add sign message demo for noAuxRand
  • Loading branch information
witter-deland authored Nov 14, 2024
1 parent 116dfb0 commit 1d3b9a9
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 9 deletions.
4 changes: 3 additions & 1 deletion cli/src/modules/message.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use kaspa_addresses::Version;
use kaspa_bip32::secp256k1::XOnlyPublicKey;
use kaspa_wallet_core::message::SignMessageOptions;
use kaspa_wallet_core::{
account::{BIP32_ACCOUNT_KIND, KEYPAIR_ACCOUNT_KIND},
message::{sign_message, verify_message, PersonalMessage},
Expand Down Expand Up @@ -87,8 +88,9 @@ impl Message {

let pm = PersonalMessage(message);
let privkey = self.get_address_private_key(&ctx, kaspa_address).await?;
let sign_options = SignMessageOptions { no_aux_rand: false };

let sig_result = sign_message(&pm, &privkey);
let sig_result = sign_message(&pm, &privkey, &sign_options);

match sig_result {
Ok(signature) => {
Expand Down
54 changes: 49 additions & 5 deletions wallet/core/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,28 @@ impl AsRef<[u8]> for PersonalMessage<'_> {
}
}

#[derive(Clone)]
pub struct SignMessageOptions {
/// The auxiliary randomness exists only to mitigate specific kinds of power analysis
/// side-channel attacks. Providing it definitely improves security, but omitting it
/// should not be considered dangerous, as most legacy signature schemes don't provide
/// mitigations against such attacks. To read more about the relevant discussions that
/// arose in adding this randomness please see: https://github.com/sipa/bips/issues/195
pub no_aux_rand: bool,
}

/// Sign a message with the given private key
pub fn sign_message(msg: &PersonalMessage, privkey: &[u8; 32]) -> Result<Vec<u8>, Error> {
pub fn sign_message(msg: &PersonalMessage, privkey: &[u8; 32], options: &SignMessageOptions) -> Result<Vec<u8>, Error> {
let hash = calc_personal_message_hash(msg);

let msg = secp256k1::Message::from_digest_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();

let sig: [u8; 64] = if options.no_aux_rand {
*secp256k1::SECP256K1.sign_schnorr_no_aux_rand(&msg, &schnorr_key).as_ref()
} else {
*schnorr_key.sign_schnorr(msg).as_ref()
};

Ok(sig.to_vec())
}
Expand Down Expand Up @@ -74,7 +89,26 @@ mod tests {
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
let sign_with_aux_rand = SignMessageOptions { no_aux_rand: false };
let sign_with_no_aux_rand = SignMessageOptions { no_aux_rand: true };
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_no_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
}

#[test]
fn test_basic_sign_without_rand_twice_should_get_same_signature() {
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 sign_with_no_aux_rand = SignMessageOptions { no_aux_rand: true };
let signature = sign_message(&pm, &privkey, &sign_with_no_aux_rand).expect("sign_message failed");
let signature_twice = sign_message(&pm, &privkey, &sign_with_no_aux_rand).expect("sign_message failed");
assert_eq!(signature, signature_twice);
}

#[test]
Expand All @@ -90,7 +124,12 @@ mod tests {
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
let sign_with_aux_rand = SignMessageOptions { no_aux_rand: false };
let sign_with_no_aux_rand = SignMessageOptions { no_aux_rand: true };
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_no_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
}

#[test]
Expand All @@ -110,7 +149,12 @@ Ut omnis magnam et accusamus earum rem impedit provident eum commodi repellat qu
])
.unwrap();

verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed");
let sign_with_aux_rand = SignMessageOptions { no_aux_rand: false };
let sign_with_no_aux_rand = SignMessageOptions { no_aux_rand: true };
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
verify_message(&pm, &sign_message(&pm, &privkey, &sign_with_no_aux_rand).expect("sign_message failed"), &pubkey)
.expect("verify_message failed");
}

#[test]
Expand Down
5 changes: 4 additions & 1 deletion wallet/core/src/wasm/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const TS_MESSAGE_TYPES: &'static str = r#"
export interface ISignMessage {
message: string;
privateKey: PrivateKey | string;
noAuxRand?: boolean;
}
"#;

Expand All @@ -30,10 +31,12 @@ pub fn js_sign_message(value: ISignMessage) -> Result<HexString, Error> {
if let Some(object) = Object::try_from(&value) {
let private_key = object.cast_into::<PrivateKey>("privateKey")?;
let raw_msg = object.get_string("message")?;
let no_aux_rand = object.get_bool("noAuxRand").unwrap_or(false);
let mut privkey_bytes = [0u8; 32];
privkey_bytes.copy_from_slice(&private_key.secret_bytes());
let pm = PersonalMessage(&raw_msg);
let sig_vec = sign_message(&pm, &privkey_bytes)?;
let sign_options = SignMessageOptions { no_aux_rand };
let sig_vec = sign_message(&pm, &privkey_bytes, &sign_options)?;
privkey_bytes.zeroize();
Ok(faster_hex::hex_string(sig_vec.as_slice()).into())
} else {
Expand Down
6 changes: 4 additions & 2 deletions wasm/examples/nodejs/javascript/general/message-signing.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ let message = 'Hello Kaspa!';
let privkey = 'b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190cfef';
let pubkey = 'dff1d77f2a671c5f36183726db2341be58feae1da2deced843240f7b502ba659';

function runDemo(message, privateKey, publicKey) {
let signature = signMessage({message, privateKey});
function runDemo(message, privateKey, publicKey, noAuxRand) {
let signature = signMessage({message, privateKey, noAuxRand});

console.info(`Message: ${message} => Signature: ${signature}`);

Expand All @@ -26,5 +26,7 @@ function runDemo(message, privateKey, publicKey) {

// Using strings:
runDemo(message, privkey, pubkey);
runDemo(message, privkey, pubkey, true);
// Using Objects:
runDemo(message, new PrivateKey(privkey), new PublicKey(pubkey));
runDemo(message, new PrivateKey(privkey), new PublicKey(pubkey), true);

0 comments on commit 1d3b9a9

Please sign in to comment.