From this article: https://tlu.tarilabs.com/cryptography/digital_signatures/introduction_schnorr_signatures.html#why-do-we-need-the-nonce
The article states that the challenge e = H(P || m)
is insecure, and that e = H(R || P || m)
should be used instead. The signature is s = k * e
, where k
is the private key.
My question is: what if we used the insecure challenge e = H(P || m)
and made the nonce part of the signature like so: s = r + (k * e)
? Would that still be secure, or is there a way to extract the private key with this approach?
I've tried to adapt the Rust code example to see if the secret key could still be extracted from the public information with this adapted signature scheme, but it looks like it's still not possible:
extern crate libsecp256k1_rs as secp256k1;
use secp256k1::{SecretKey, PublicKey, thread_rng, Message};
use secp256k1::schnorr::{ Challenge};
// This one tests that adding r/R makes key extraction impossible
#[allow(non_snake_case)]
fn main() {
// Create a random private key
let mut rng = thread_rng();
let r = SecretKey::random(&mut rng);
println!("My private random value is: {}", r);
let R = PublicKey::from_secret_key(&r);
let k = SecretKey::random(&mut rng);
println!("My private key: {}", k);
let P = PublicKey::from_secret_key(&k);
// Challenge, e = H(P || m)
let m = Message::hash(b"Meet me at 12").unwrap();
let e = Challenge::new(&[&P, &m]).as_scalar().unwrap();
// Signature (with nonce)
let s = r + (e * k);
// Verify the signature
assert_eq!(PublicKey::from_secret_key(&s), R + (e*P));
println!("Signature is valid!");
let hacked = s * e.inv();
assert_eq!(k, hacked); // fails
println!("Hacked key: {}", k)
}