Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add signMessageWithoutRand method for kaspa wasm #587

Merged
merged 7 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);