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

Add signing methods for pre-determined nonces #24

Merged
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
59 changes: 53 additions & 6 deletions src/wasm/key_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
use blake2::Digest;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use tari_utilities::hex::Hex;
use tari_utilities::hex::{from_hex, Hex};
use wasm_bindgen::prelude::*;

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -87,15 +87,62 @@ pub fn sign(private_key: &str, msg: &str) -> JsValue {
return JsValue::from_serde(&result).unwrap();
},
};
sign_with_key(&k, msg, &mut result);
sign_message_with_key(&k, msg, None, &mut result);
JsValue::from_serde(&result).unwrap()
}

#[allow(non_snake_case)]
pub(crate) fn sign_with_key(k: &RistrettoSecretKey, msg: &str, result: &mut SignResult) {
let (r, R) = RistrettoPublicKey::random_keypair(&mut OsRng);
/// Generate a Schnorr signature of a challenge (that has already been hashed) using the given private
/// key and a specified private nonce. DO NOT reuse nonces. This method is provide for cases where a
/// public nonce has been used
/// in the message.
#[wasm_bindgen]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you meant pre-hashed message, but can you explain what "hashed using the given private key" means? You usually hash with a hashing algorithm.

Copy link
Contributor Author

@stringhandler stringhandler Nov 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah needs a comma or something. PHRASING.

Generate a Schnorr signature of a challenge (that has already been hashed) using the given private

pub fn sign_challenge_with_nonce(private_key: &str, private_nonce: &str, challenge_as_hex: &str) -> JsValue {
let mut result = SignResult::default();
let k = match RistrettoSecretKey::from_hex(private_key) {
Ok(k) => k,
_ => {
result.error = "Invalid private key".to_string();
return JsValue::from_serde(&result).unwrap();
},
};
let r = match RistrettoSecretKey::from_hex(private_nonce) {
Ok(r) => r,
_ => {
result.error = "Invalid private nonce".to_string();
return JsValue::from_serde(&result).unwrap();
},
};

let e = match from_hex(challenge_as_hex) {
Ok(e) => e,
_ => {
result.error = "Challenge was not valid HEX".to_string();
return JsValue::from_serde(&result).unwrap();
},
};
sign_with_key(&k, &e, Some(&r), &mut result);
JsValue::from_serde(&result).unwrap()
}

pub(crate) fn sign_message_with_key(
k: &RistrettoSecretKey,
msg: &str,
r: Option<&RistrettoSecretKey>,
result: &mut SignResult,
)
{
let e = Blake256::digest(msg.as_bytes());
let sig = match RistrettoSchnorr::sign(k.clone(), r, e.as_slice()) {
sign_with_key(k, e.as_slice(), r, result)
}

#[allow(non_snake_case)]
pub(crate) fn sign_with_key(k: &RistrettoSecretKey, e: &[u8], r: Option<&RistrettoSecretKey>, result: &mut SignResult) {
let (r, R) = match r {
Some(r) => (r.clone(), RistrettoPublicKey::from_secret_key(r)),
None => RistrettoPublicKey::random_keypair(&mut OsRng),
};

let sig = match RistrettoSchnorr::sign(k.clone(), r, e) {
Ok(s) => s,
Err(e) => {
result.error = format!("Could not create signature. {}", e.to_string());
Expand Down
32 changes: 30 additions & 2 deletions src/wasm/keyring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use crate::{
},
wasm::{
commitments::CommitmentResult,
key_utils::{sign_with_key, SignResult},
key_utils::{sign_message_with_key, SignResult},
},
};
use rand::rngs::OsRng;
Expand Down Expand Up @@ -101,7 +101,35 @@ impl KeyRing {
return JsValue::from_serde(&result).unwrap();
}
let k = k.unwrap();
sign_with_key(&k.0, msg, &mut result);
sign_message_with_key(&k.0, msg, None, &mut result);
JsValue::from_serde(&result).unwrap()
}

/// Sign a message using a private key and a specific nonce
///
/// Use can use a key in the keyring to generate a digital signature. To create the signature, the caller must
/// provide the `id` associated with the key, the message to sign, and a `nonce_id`. *Do not* reuse nonces.
/// This function is provided because in some signature schemes require the public nonce to be
/// part of the message.
///
/// The return type is pretty unRust-like, but is structured to more closely model a JSON object.
///
/// `keys::check_signature` is used to verify signatures.
pub fn sign_with_nonce(&self, id: &str, nonce_id: &str, msg: &str) -> JsValue {
let mut result = SignResult::default();
let k = self.keys.get(id);
if k.is_none() {
result.error = format!("Private key for '{}' does not exist", id);
return JsValue::from_serde(&result).unwrap();
}
let k = k.unwrap();
let nonce = self.keys.get(nonce_id);
if nonce.is_none() {
result.error = format!("Private nonce for `{}` does not exist", nonce_id);
return JsValue::from_serde(&result).unwrap();
}
let nonce = nonce.unwrap();
sign_message_with_key(&k.0, msg, Some(&nonce.0), &mut result);
JsValue::from_serde(&result).unwrap()
}

Expand Down
21 changes: 20 additions & 1 deletion wasm-demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
//

// If you get a "module not found" error, see README.md for details on how to generate the node package
let tari_crypto = require('./pkg');
let tari_crypto = require('./tari_js');
let assert = require('assert');


Expand Down Expand Up @@ -58,6 +58,25 @@ if (sig.error) {
}
}

// Sign with nonce
console.log("Signing message with predetermined nonce");
let nonce = keys.new_key("Nonce");
sig = keys.sign_with_nonce("Alice", "Nonce","Hello Tari");
if (sig.error) {
console.log(`Error getting signature ${sig.error}`);
} else {
console.log('Signature:', sig);
console.log("Verifying signature..");
let pubkey = keys.public_key("Alice");
console.log(`Pubkey: ${pubkey}`);
let check = tari_crypto.check_signature(sig.public_nonce, sig.signature, pubkey, "Hello Tari");
if (check.result === true) {
console.log("Signature is valid!");
} else {
console.log(`Invalid signature: ${check.error}`);
}
}

// Commitments
const v = BigInt(10200300);
const k = keys.private_key("Bob");
Expand Down