Skip to content

Commit

Permalink
Add signing methods for pre-determined nonces
Browse files Browse the repository at this point in the history
In some cases, the public nonce used in the signature is committed and
used in the challenge. These extra methods allow signing with a private
nonce to accommodate that. There is also support for signing a pre-hashed
byte array instead of a string message
  • Loading branch information
stringhandler committed Nov 2, 2020
1 parent e6085be commit fe097d5
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 12 deletions.
59 changes: 50 additions & 9 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::{Hex, from_hex};
use wasm_bindgen::prelude::*;

#[derive(Serialize, Deserialize)]
Expand Down Expand Up @@ -85,22 +85,63 @@ pub fn sign(private_key: &str, msg: &str) -> JsValue {
_ => {
result.error = "Invalid private key".to_string();
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]
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());
return;
},
}
};
result.public_nonce = Some(R.to_hex());
result.signature = Some(sig.get_signature().to_hex());
Expand All @@ -120,7 +161,7 @@ pub fn check_signature(pub_nonce: &str, signature: &str, pub_key: &str, msg: &st
Err(_) => {
result.error = format!("{} is not a valid public nonce", pub_nonce);
return JsValue::from_serde(&result).unwrap();
},
}
};

let P = RistrettoPublicKey::from_hex(pub_key);
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

0 comments on commit fe097d5

Please sign in to comment.