schnorr-rs 0.1.0

Schnorr Protocols
Documentation
//! Implementation of Schnorr Signature Scheme (a varient scheme by using elliptic curve cryptography)

use crate::Hash;
use p256::elliptic_curve::point::AffineCoordinates;
use p256::elliptic_curve::Group;
use serde::{Deserialize, Serialize};
use std::ops::{Mul, Neg};

/// Schnorr Signature Scheme based on elliptic curve cryptography.
/// The scheme consists of the following steps:
/// 1. Generate a key pair (d, p) where p = -dG.
/// 2. Sign a message m with the key pair (d, p) by generating a random number k and calculate r = kG and e = H(r_x || p_x || m).
/// 3. Calculate s = k + e*d.
/// 4. The signature is (e, s).
/// 5. Verify the signature by calculating r_v = sG + eP and e_v = H(r_x || p_x || m). If e_v == e, then the signature is valid.
///
/// The scheme is based on the elliptic curve cryptography with the curve P-256.
/// The hash function H is used to hash a byte array into a 32-byte array.
#[derive(Default, Clone, Serialize, Deserialize)]
pub struct SignatureScheme<H: Hash> {
    /// generator point
    g: p256::AffinePoint,
    _phantom: std::marker::PhantomData<H>,
}

impl<H: Hash> SignatureScheme<H> {
    pub fn new() -> Self {
        let g = p256::ProjectivePoint::generator().to_affine();

        Self {
            g,
            _phantom: std::marker::PhantomData,
        }
    }

    /// Generate a key pair (d, p) where p = -dG.
    /// Return the signing key and public key.
    /// The signing key is used to sign a message (by calling [SignatureScheme::sign]),
    /// while the public key is used to verify the signature (by calling [SignatureScheme::verify]).
    /// The key pair is generated by using the random number generator rng.
    pub fn generate_key<R: rand::CryptoRng + rand::RngCore>(
        &self,
        rng: &mut R,
    ) -> (SigningKey, PublicKey) {
        // p = -dG
        let d = p256::NonZeroScalar::random(rng);
        let p = self.g.mul(d.neg().as_ref()).to_affine();
        (SigningKey { d }, PublicKey { p })
    }

    /// Sign a message m with the key pair (d, p) by generating a random number k and
    /// - calculate r = kG and e = H(r_x || p_x || m).
    /// - calculate s = k + e*d.
    /// Return the signature (e, s).
    /// The signature is used to verify the message (by calling [SignatureScheme::verify]).
    /// The signature is generated by using the random number generator rng.
    pub fn sign<R: rand::CryptoRng + rand::RngCore, M: AsRef<[u8]>>(
        &self,
        rng: &mut R,
        key: &SigningKey,
        pub_key: &PublicKey,
        message: M,
    ) -> Signature {
        // r = kG
        let k = p256::NonZeroScalar::random(rng);
        let r = self.g.mul(k.as_ref());

        // e = H(r_x || p_x || m)
        let r_x = r.to_affine().x().to_vec();
        let p_x = pub_key.p.x().to_vec();
        let e = p256::elliptic_curve::ScalarPrimitive::<p256::NistP256>::from_slice(&H::hash(
            [r_x, p_x, message.as_ref().to_vec()].concat(),
        ))
        .unwrap();
        let e = p256::Scalar::from(e);
        // s = k + e*d
        let s = k.add(&e.multiply(&key.d));
        Signature { e, s }
    }

    /// Verify the signature by calculating r_v = sG + eP and e_v = H(r_x || p_x || m).
    /// If e_v == e, then the signature is valid.
    /// Return true if the signature is valid, otherwise false.
    pub fn verify(&self, key: &PublicKey, message: &[u8], signature: &Signature) -> bool {
        // r_v = sG + eP
        let r_v = self
            .g
            .mul(signature.s.as_ref())
            .add(&key.p.mul(signature.e.as_ref()));
        // ev = H(r_x || p_x || m)
        let r_x = r_v.to_affine().x().to_vec();
        let p_x = key.p.x().to_vec();
        let e_v = p256::elliptic_curve::ScalarPrimitive::<p256::NistP256>::from_slice(&H::hash(
            [r_x, p_x, message.to_vec()].concat(),
        ));
        // if e_v == e, then the signature is valid\
        e_v.map(p256::Scalar::from)
            .map(|e_v| e_v == signature.e)
            .unwrap_or(false)
    }
}

/// Public key used to verify a signature.
#[derive(Clone, Serialize, Deserialize)]
pub struct PublicKey {
    p: p256::AffinePoint,
}

/// Signing key used to sign a message.
#[derive(Clone, Serialize, Deserialize)]
pub struct SigningKey {
    d: p256::NonZeroScalar,
}

/// Signature used to verify a message.
#[derive(Clone, Serialize, Deserialize)]
pub struct Signature {
    e: p256::Scalar,
    s: p256::Scalar,
}