143 lines
4.7 KiB
Rust
143 lines
4.7 KiB
Rust
//! An implementation of digitial signatures using secp256k1 ECDSA.
|
|
|
|
use k256::ecdsa::{
|
|
Signature, SigningKey, VerifyingKey,
|
|
signature::{Signer, Verifier},
|
|
};
|
|
|
|
use crate::error::CryptoError;
|
|
|
|
pub struct SigningKeypair {
|
|
sk: SigningKey,
|
|
vk: VerifyingKey,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct PublicKey(VerifyingKey);
|
|
|
|
impl SigningKeypair {
|
|
/// Generates a new random keypair
|
|
pub fn new() -> Result<Self, CryptoError> {
|
|
let mut raw_private = [0u8; 32];
|
|
rand::fill(&mut raw_private);
|
|
let sk = SigningKey::from_slice(&raw_private)
|
|
.expect("Key is provided generated with fixed valid size");
|
|
let vk = sk.verifying_key().to_owned();
|
|
|
|
Ok(Self { sk, vk })
|
|
}
|
|
|
|
/// Create a new key from existing bytes.
|
|
pub(crate) fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
|
|
if bytes.len() == 32 {
|
|
let sk = SigningKey::from_slice(&bytes).expect("Key was checked to be a valid size");
|
|
let vk = sk.verifying_key().to_owned();
|
|
Ok(Self { sk, vk })
|
|
} else {
|
|
Err(CryptoError::InvalidKeySize)
|
|
}
|
|
}
|
|
|
|
/// View the raw bytes of the private key of this keypair.
|
|
pub(crate) fn as_raw_private_key(&self) -> Vec<u8> {
|
|
self.sk.as_nonzero_scalar().to_bytes().to_vec()
|
|
}
|
|
|
|
/// Get the public part of this keypair.
|
|
pub fn public_key(&self) -> PublicKey {
|
|
PublicKey(self.vk)
|
|
}
|
|
|
|
/// Sign data with the private key of this `SigningKeypair`. Other parties can use the public
|
|
/// key to verify the signature. The generated signature is a detached signature.
|
|
pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, CryptoError> {
|
|
let sig: Signature = self.sk.sign(message);
|
|
Ok(sig.to_vec())
|
|
}
|
|
}
|
|
|
|
impl PublicKey {
|
|
/// Import a public key from raw bytes
|
|
pub fn from_bytes(bytes: &[u8]) -> Result<Self, CryptoError> {
|
|
Ok(Self(VerifyingKey::from_sec1_bytes(bytes)?))
|
|
}
|
|
|
|
/// Get the raw bytes of this `PublicKey`, which can be transferred to another party.
|
|
///
|
|
/// The public key is SEC-1 encoded and compressed.
|
|
pub fn as_bytes(&self) -> Box<[u8]> {
|
|
self.0.to_encoded_point(true).to_bytes()
|
|
}
|
|
|
|
pub fn verify_signature(&self, message: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
|
|
let sig = Signature::from_slice(sig).map_err(|_| CryptoError::InvalidKeySize)?;
|
|
self.0
|
|
.verify(message, &sig)
|
|
.map_err(|_| CryptoError::SignatureFailed)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
|
|
/// Generate a key, get the public key, export the bytes of said public key, import them again
|
|
/// as a public key, and verify the keys match. This make sure public keys can be exchanged.
|
|
#[test]
|
|
fn recover_public_key() {
|
|
let sk = super::SigningKeypair::new().expect("Can generate new key");
|
|
let pk = sk.public_key();
|
|
let pk_bytes = pk.as_bytes();
|
|
|
|
let pk2 = super::PublicKey::from_bytes(&pk_bytes).expect("Can import public key");
|
|
|
|
assert_eq!(pk, pk2);
|
|
}
|
|
|
|
/// Sign a message and validate the signature with the public key. Together with the above test
|
|
/// this makes sure a remote system can receive our public key and validate messages we sign.
|
|
#[test]
|
|
fn validate_signature() {
|
|
let sk = super::SigningKeypair::new().expect("Can generate new key");
|
|
let pk = sk.public_key();
|
|
|
|
let message = b"this is an arbitrary message we want to sign";
|
|
|
|
let sig = sk.sign(message).expect("Message can be signed");
|
|
|
|
assert!(pk.verify_signature(message, &sig).is_ok());
|
|
}
|
|
|
|
/// Make sure a signature which is tampered with does not pass signature validation
|
|
#[test]
|
|
fn corrupt_signature_does_not_validate() {
|
|
let sk = super::SigningKeypair::new().expect("Can generate new key");
|
|
let pk = sk.public_key();
|
|
|
|
let message = b"this is an arbitrary message we want to sign";
|
|
|
|
let mut sig = sk.sign(message).expect("Message can be signed");
|
|
|
|
// Tamper with the sig
|
|
sig[0] = sig[0].wrapping_add(1);
|
|
|
|
assert!(pk.verify_signature(message, &sig).is_err());
|
|
}
|
|
|
|
/// Make sure a valid signature does not work for a message which has been modified
|
|
#[test]
|
|
fn tampered_message_does_not_validate() {
|
|
let sk = super::SigningKeypair::new().expect("Can generate new key");
|
|
let pk = sk.public_key();
|
|
|
|
let message = b"this is an arbitrary message we want to sign";
|
|
let mut message_clone = message.to_vec();
|
|
|
|
let sig = sk.sign(message).expect("Message can be signed");
|
|
|
|
// Modify the message
|
|
message_clone[0] = message[0].wrapping_add(1);
|
|
|
|
assert!(pk.verify_signature(&message_clone, &sig).is_err());
|
|
}
|
|
}
|