I'm trying to implement ECDSA for learning purposes and have generated public and private keys, which seem to be correct as I have compared them with those generated by an online tool called noble. However, my signing or verification algorithm (or both) is not working correctly and I am having difficulty debugging the code due to my lack of cryptographic or mathematical background. I have been trying to fix the issue for the past two days with no luck any second eye would really be appreciated.
If there are any important details that I have missed in this description, please let me know and I will modify the post.
Btw I'm implementing The algorithms based on cryptobook nakov
this is my signing algorithm:
// FieldElement struct is just num % prime
pub fn sign(&self, z: FieldElement) -> Signature {
// this struct contains all secp256k1 parameters
let params = Secp256k1Param::new();
// get a random 1 < k < n - 1
let k = BigInt::from(get_rndm(¶ms.n()));
// wrape it in a FieldElement to be able to multiply it with the generator
// k % prime
let k = FieldElement {
num: k,
prime: params.prime(),
};
// r = k * G
let r = params.generator().multiply(&k);
// k_inv = k^-1 = k^(n-2) mod n
let k_inv = k.num().modpow(&(params.n().sub(2)), ¶ms.n());
// s = k^-1 * (z + r * d) mod n
let s = (k_inv * (&z.num + (&r.x.num() * &self.prk))) % ¶ms.n();
// wrap s in a FieldElement
let s = FieldElement {
num: s,
prime: params.prime(),
};
// signature is struct {r, s} where r is an EllipticPoint and s is a FieldElement
// EllipticPoint {x, y, a, b}
// FieldElement {num, prime}
let sign = Signature { r, s };
sign
and this is my verification algorithm:
pub fn verify(&self, z: FieldElement, signature: Signature) -> bool {
let params = Secp256k1Param::new();
// calculating the hash of the message h = hash(z)
let h = hash(&z.num().to_string(), None).num();
// r' = (h * s^-1) * G + (r * s^-1 * pk)
// (h * s^-1) * G (I'm sure that multiplication method in EllipticPoint is correct since i used it in Public/private key generation and they're correct)
let left_side = params.generator().multiply(&FieldElement::new(h * &signature.s.num().modpow(&(params.n().sub(2)), ¶ms.n()), params.prime()));
// (r * s^-1 * pk)
let right_side = signature.r.multiply(&FieldElement::new(signature.s.num().modpow(&(params.n().sub(2)), ¶ms.n()) * &self.pk(), params.prime()));
// r' = (h * s^-1) * G + (r * s^-1 * pk)
let r2 = left_side.add(&right_side);
// equality of two elliptic points is the equality of their x,y,a,b coordinates
r2.eq(&signature.r)
}
my tests are simply:
let params = Secp256k1Param::new();
let keys = ECDSA::new("my secret key".to_string());
println!("private key: {}", keys.prk());
println!("public key: {}", keys.pk());
//
let msg = "hello world";
let msg_hash = hash(msg, None);
println!("message hash: {}", msg_hash.num().to_str_radix(16));
let signature = keys.sign(msg_hash.clone());
if keys.verify(msg_hash, signature) == true {
println!("signature is valid");
} else {
// println!("s: {}", signature.s.num().to_str_radix(16));
println!("signature is invalid");
}