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 and using the Ed25519-Dalek library. 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 with the Dalek library |
Theory
In this case, Alice will sign a message (\(m\)). Initially she creates a random secret key (\(k\)) and then creates a public key of:
\(A=sG\)
and G is the base point on the curve, and:
\(s=H_{512}(k)\)
and where we create a SHA 512-bit hash and use the lower 256 bits. For each signature, we generate a random value (\(r\)) and then generate a signature of (\(R,S\)) with:
\(R=rB\)
\( S \equiv r+H(R\parallel A\parallel M)s{\pmod {\ell}} \)
and where \(\ell\) is the order of the curve. For Curve 25519, this is:
\(\ell =2^{252}+27742317777372353535851937790883648493\)
The public key and secret key both have 256 bits, and the signature \((R,S)\) has 512 bits.
Code
For Rust, we initially create the project with::
cargo new dalek01
We then go into the dalek01 folder, and add the following to the cargo.toml file:
[package] name = "dalek01" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies.ed25519-dalek] version = "1.0.1" [dependencies] rand_core="0.5.0" hex="0.4.3"
We can then add our source code with the integration of the Ed25519-Dalek library:
extern crate rand_core; extern crate ed25519_dalek; extern crate hex; use ed25519_dalek::*; use rand_core::OsRng; use std::env; fn main() { let mut str1 ="This is a test of the Ed25519 signature in Rust."; let args: Vec= env::args().collect(); if args.len() >1 { str1 = args[1].as_str();} let keypair: Keypair = Keypair::generate(&mut OsRng); println!("Secret key: {}",hex::encode(keypair.public.as_bytes())); println!("Public key: {}",hex::encode(keypair.secret.as_bytes())); let message: &[u8] = str1.as_bytes(); let signature: Signature = keypair.sign(message); println!("Message: {}",str1); println!("Signature: {}",signature.to_string()); let rtn=keypair.verify(message, &signature).is_ok(); if rtn==true { println!("Signature has been proven!"); } else { println!("Signature has NOT been proven!"); } }
Finally we simply build with:
cargo build
Finally we can run with:
cargo run "Hello World!
A sample run is:
Secret key: 2c4669bae043e5797f138578743d551bcf6e5a067d69f6d406907f00458e1cf9 Public key: e8954715e3a3d23aa525764ea43fd2890e5be0cb75b515bcb54b26533a15cce1 Message: Hello World! Signature: 8B86266548131912A47ED25F25B22C1D5C7D47ADBE3966F7C7A959F42BAA8D0707C70955ADC279ED768B9D979753EC33357ED40943276EA5E0747E63BDC0C00F Signature has been proven!