diff --git a/cli/src/modules/message.rs b/cli/src/modules/message.rs index dce7f3679..a641aa8c6 100644 --- a/cli/src/modules/message.rs +++ b/cli/src/modules/message.rs @@ -1,12 +1,12 @@ +use crate::imports::*; 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}, }; -use crate::imports::*; - #[derive(Default)] pub struct Message; @@ -87,8 +87,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) => { diff --git a/wallet/core/src/message.rs b/wallet/core/src/message.rs index 0fff9cc5a..8d4d4e491 100644 --- a/wallet/core/src/message.rs +++ b/wallet/core/src/message.rs @@ -15,24 +15,28 @@ impl AsRef<[u8]> for PersonalMessage<'_> { } } -/// Sign a message with the given private key -pub fn sign_message(msg: &PersonalMessage, privkey: &[u8; 32]) -> Result, 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(); - - Ok(sig.to_vec()) +#[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 without random -pub fn sign_message_without_rand(msg: &PersonalMessage, privkey: &[u8; 32]) -> Result, Error> { +/// Sign a message with the given private key +pub fn sign_message(msg: &PersonalMessage, privkey: &[u8; 32], options: &SignMessageOptions) -> Result, 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] = *secp256k1::SECP256K1.sign_schnorr_no_aux_rand(&msg, &schnorr_key).as_ref(); + + let sig: [u8; 64] = if options.no_aux_rand { + *schnorr_key.sign_schnorr(msg).as_ref() + } else { + *secp256k1::Secp256k1::new().sign_schnorr_no_aux_rand(&msg, &schnorr_key).as_ref() + }; Ok(sig.to_vec()) } @@ -85,8 +89,11 @@ mod tests { ]) .unwrap(); - verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed"); - verify_message(&pm, &sign_message_without_rand(&pm, &privkey).expect("sign_message failed"), &pubkey) + 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"); } @@ -98,8 +105,9 @@ mod tests { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, ]; - let signature = sign_message_without_rand(&pm, &privkey).expect("sign_message failed"); - let signature_twice = sign_message_without_rand(&pm, &privkey).expect("sign_message failed"); + 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); } @@ -116,8 +124,11 @@ mod tests { ]) .unwrap(); - verify_message(&pm, &sign_message(&pm, &privkey).expect("sign_message failed"), &pubkey).expect("verify_message failed"); - verify_message(&pm, &sign_message_without_rand(&pm, &privkey).expect("sign_message failed"), &pubkey) + 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"); } @@ -138,8 +149,11 @@ 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"); - verify_message(&pm, &sign_message_without_rand(&pm, &privkey).expect("sign_message failed"), &pubkey) + 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"); } diff --git a/wallet/core/src/wasm/message.rs b/wallet/core/src/wasm/message.rs index 41ce83cc5..6bc0e2eb7 100644 --- a/wallet/core/src/wasm/message.rs +++ b/wallet/core/src/wasm/message.rs @@ -14,6 +14,7 @@ const TS_MESSAGE_TYPES: &'static str = r#" export interface ISignMessage { message: string; privateKey: PrivateKey | string; + noAuxRand: boolean; } "#; @@ -30,28 +31,12 @@ pub fn js_sign_message(value: ISignMessage) -> Result { if let Some(object) = Object::try_from(&value) { let private_key = object.cast_into::("privateKey")?; let raw_msg = object.get_string("message")?; + let no_aux_rand = object.get_bool("noAuxRand")?; 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)?; - privkey_bytes.zeroize(); - Ok(faster_hex::hex_string(sig_vec.as_slice()).into()) - } else { - Err(Error::custom("Failed to parse input")) - } -} - -/// Signs a message with the given private key without rand -/// @category Message Signing -#[wasm_bindgen(js_name = signMessageWithoutRand)] -pub fn js_sign_message_without_rand(value: ISignMessage) -> Result { - if let Some(object) = Object::try_from(&value) { - let private_key = object.cast_into::("privateKey")?; - let raw_msg = object.get_string("message")?; - 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_without_rand(&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 {