We can use Curve 25519 to sign messages with a private key, and then use the associated public key to verify the signature. In this case we will implement Ed25519 with Rust. With Ed25519 we use a private key to sign data, and then the public key can prove it. We use Curve 25519 for the generation of the public key and for the signing process. Curve 25519 uses a Montgomery curve of the form of \(y^2 = x^3+a x^2+x \pmod p\). If we have a point \(P\), we then find a point \(nP\) - where \(n\) is the number of times we add \(P\)). Curve 25519 takes the form of \(y^2 = x^3+486662 x^2+x \pmod p\) and a base point at \(x=9\). This gives a base point (\(G\)), and where we find the point \(nG\) for a given \(n\) value, and where \(n\) is the private key value, and \(nG\) is the public key point. Normally in Curve 25519 we only focus on the x-point, and do not need the \((x,y)\) value.
Ed25519 Signatures in Rust |
Code
First we create the with:
cargo new ed
We then go into the ed folder, and add the following to the cargo.toml file:
[package] name = "ed" version = "1.0.0" authors = ["Bill"] [dependencies] ring="0.16.20" hex = "0.4"
In this case we will use the Ring crate, and which supports a range of cryptographic methods. For this we will use Ed25519 for the signature:
use ring::{ rand, signature::{self, KeyPair}, }; use std::env; use hex::ToHex; 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 msg=message.as_bytes(); let rng = rand::SystemRandom::new(); let pkcs8_bytes = signature::Ed25519KeyPair::generate_pkcs8(&rng).unwrap(); let key_pair = signature::Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).unwrap(); let peer_public_key_bytes = key_pair.public_key().as_ref(); println! ("\nPkcs8: {:?}",pkcs8_bytes.as_ref().encode_hex:: ()); println! ("\nPublic key: {:?}",peer_public_key_bytes.encode_hex:: ()); let sig = key_pair.sign(msg); let sig_bytes = sig.as_ref(); println!("\nSignature: {:?}", sig_bytes.encode_hex:: ()); let peer_public_key = signature::UnparsedPublicKey::new(&signature::ED25519, peer_public_key_bytes); let rtn=peer_public_key.verify(msg, sig.as_ref()).is_ok(); if rtn==true { println!("\nMessage signature correct"); } else { println!("\nMessage signature incorrect");} }
Finally we simply build with:
cargo build
A sample run is:
Message: Hello world! Pkcs8: "3053020101300506032b65700422042082cade15c2cfb1d3900fe3f8ea47ff8a4bad0983836267b5272aee0b1337207da123032100e10d14faf1171513638cd2d1f997d2d60b3b187fd268784b83c2986a90c4e57f" Public key: "e10d14faf1171513638cd2d1f997d2d60b3b187fd268784b83c2986a90c4e57f" Signature: "3e435baa94dff5e7f385e6c17320b6db2c0a68c5d753ca310df0f9d36c8440fcdd468bb3d3ac2fd1b6eee40d830e5d6b06f06fe1105f1234b1e765c9989c2507" Message signature correct