With ECDSA (Elliptic Curve Digital Signature Algorithm) we use a private key to sign data, and then the public key can prove it. In this case we will use the P256 curve (and which is used in Bitcoin) and implement in Rust. For this we will sign a message with a private key, and the produce a verifying key to prove the signature. We can see that that the public key (the verifying key) is double the size of the private key (the signing key). This is because the public key is an \((x,y)\) point on the curve. For P256 we use \(y^2 = x^3 - 3x + b \) over a 256-bit prime number field, and where \(b\)=41058363725152142 ... 256314039467401291. The prime number used is \(p = 2^{256} - 2^{224} + 2^{192} + 2^{96} - 1\).
ECDSA Signatures in Rust using P256 |
Code
First we create the project with:
cargo new ecdsa2
We then go into the ecdsa2 folder, and add the following to the cargo.toml file:
[package] name = "ecdh" version = "1.0.0" authors = ["Bill"] [dependencies] p256="0.9.0" hex="0.4.3" rand_core="0.6.3"
For this we will use P256 for an ECDSA signature:
use p256::{ecdsa::{SigningKey, Signature, signature::Signer, VerifyingKey, signature::Verifier}}; use rand_core::OsRng; use std::env; fn main() { let mut message = String::from("Hello world!"); let args: Vec= env::args().collect(); if args.len() >1 { message = args[1].clone(); } println!("Message: {}",message); let mut msg=message.as_bytes(); let signing_key = SigningKey::random(&mut OsRng); let sk=signing_key.to_bytes(); println!("\nSigning key: {:x?}",hex::encode(sk)); let verify_key = VerifyingKey::from(&signing_key); let vk=verify_key.to_encoded_point(false); println!("\nVerifying key: {:x?}",hex::encode(vk)); let signature: Signature = signing_key.sign(msg); println!("\nSignature: {:x?}",hex::encode(signature)); let rtn=verify_key.verify(msg, &signature).is_ok(); if rtn==true { println!("\nMessage '{0}' signature correct", message); } else { println!("\nMessage '{0}' signature incorrect",message);} msg="hello".as_bytes(); let rtn=verify_key.verify(msg, &signature).is_ok(); if rtn==true { println!("\nWith 'hello', message signature correct"); } else { println!("\nWith 'hello', message signature incorrect");} }
Finally we simply build with:
cargo build
A sample run is:
Message: Hello World! Signing key: "287030bf3a53309fe83f55f2834f047321bf48aa55aec67c2e8c117452f4c30b" Verifying key: "04dbb1c32cb15867b7d3d78d7b6c9d5334781e42fa943a4eafee75ecf5a2f219bb306aa6d2f25a538d28202b491e41e27604032d216e5af9e240d003243cd97aa5" Signature: "31f2f151c6cc0745647cc473e7ba7127b236cbd79e85497ea5ca02983c3e4815dd88c5d555cbaabf7a07761572a494ab327272cdd9fb4107e73adf918e5f49bf" Message 'Hello World!' signature correct With 'hello', message signature incorrect
We can see that that the public key (the verifying key) is double the size of the private key (the signing key). This is because he public key is an \((x,y)\) point on the curve.