bign256 0.13.1

Pure Rust implementation of the Bign P-256 (a.k.a. bign-curve256v1) elliptic curve as defined in STB 34.101.45-2013, with general purpose curve arithmetic
Documentation
//! Support for signing Bign256 signatures.
//!
//! ## Algorithm
//!
//! ```text
//! 1. Set ๐ป โ† โ„Ž(๐‘‹).
//! 2. Generate ๐‘˜ โ† rand(1,..,๐‘ž-1)
//! 3. Set ๐‘… โ† ๐‘˜๐บ.
//! 4. Set ๐‘†0 โ† โŸจ๏ธ€belt-hash(OID(โ„Ž) โ€– โŸจ๐‘…โŸฉ2๐‘™ โ€– ๐ป)โŸฉ๏ธ€_๐‘™.
//! 5. Set ๐‘†1 โ† โŸจ๏ธ€(๐‘˜ โˆ’ ๐ป โˆ’ (๐‘†0 + 2^๐‘™)๐‘‘) mod ๐‘žโŸฉ๏ธ€_2๐‘™.
//! 6. Set ๐‘† โ† ๐‘†0 โ€– ๐‘†1.
//! 7. Return S.
//! ```

#![allow(non_snake_case)]

use super::{Signature, VerifyingKey, BELT_OID};
use crate::{BignP256, FieldBytes, NonZeroScalar, ProjectivePoint, PublicKey, Scalar, SecretKey};
use core::fmt::{self, Debug};
use elliptic_curve::{
    generic_array::typenum::Unsigned,
    ops::{MulByGenerator, Reduce},
    point::AffineCoordinates,
    subtle::{Choice, ConstantTimeEq},
    Curve, Field, FieldBytesEncoding, PrimeField,
};
use signature::{hazmat::PrehashSigner, Error, KeypairRef, Result, Signer};

use belt_hash::{BeltHash, Digest};
use crypto_bigint::{consts::U32, generic_array::GenericArray};

/// BignP256 secret key used for signing messages and producing signatures.
///
/// ## Usage
///
/// The [`signature`] crate defines the following traits which are the
/// primary API for signing:
///
/// - [`Signer`]: sign a message using this key
/// - [`PrehashSigner`]: sign the low-level raw output bytes of a message digest
#[derive(Clone)]
pub struct SigningKey {
    /// Secret key.
    secret_scalar: NonZeroScalar,

    /// Verifying key for this signing key.
    verifying_key: VerifyingKey,
}

impl SigningKey {
    /// Create signing key from a signer's distinguishing identifier and
    /// secret key.
    pub fn new(secret_key: &SecretKey) -> Result<Self> {
        Self::from_nonzero_scalar(secret_key.to_nonzero_scalar())
    }

    /// Parse signing key from big endian-encoded bytes.
    pub fn from_bytes(bytes: &FieldBytes) -> Result<Self> {
        Self::from_slice(bytes)
    }

    /// Parse signing key from big endian-encoded byte slice containing a secret
    /// scalar value.
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
        let secret_scalar = NonZeroScalar::try_from(slice).map_err(|_| Error::new())?;
        Self::from_nonzero_scalar(secret_scalar)
    }

    /// Create a signing key from a non-zero scalar.
    pub fn from_nonzero_scalar(secret_scalar: NonZeroScalar) -> Result<Self> {
        let public_key = PublicKey::from_secret_scalar(&secret_scalar);
        let verifying_key = VerifyingKey::new(public_key)?;
        Ok(Self {
            secret_scalar,
            verifying_key,
        })
    }

    /// Serialize as bytes.
    pub fn to_bytes(&self) -> FieldBytes {
        self.secret_scalar.to_bytes()
    }

    /// Borrow the secret [`NonZeroScalar`] value for this key.
    ///
    /// # โš ๏ธ Warning
    ///
    /// This value is key material.
    ///
    /// Please treat it with the care it deserves!
    pub fn as_nonzero_scalar(&self) -> &NonZeroScalar {
        &self.secret_scalar
    }

    /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`].
    pub fn verifying_key(&self) -> &VerifyingKey {
        &self.verifying_key
    }
}

//
// `*Signer` trait impls
//

impl PrehashSigner<Signature> for SigningKey {
    fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature> {
        if prehash.len() != <BignP256 as Curve>::FieldBytesSize::USIZE {
            return Err(Error::new());
        }
        let mut h_word: GenericArray<u8, U32> = GenericArray::clone_from_slice(prehash);
        h_word.reverse();

        let h = Scalar::reduce_bytes(&h_word);

        //2. Generate ๐‘˜ โ† rand(1,..,๐‘ž-1)
        let k = Scalar::from_repr(rfc6979::generate_k::<BeltHash, _>(
            &self.secret_scalar.to_repr(),
            &FieldBytesEncoding::<BignP256>::encode_field_bytes(&BignP256::ORDER),
            &h.to_bytes(),
            &[],
        ))
        .unwrap();

        // 3. Set ๐‘… โ† ๐‘˜๐บ.
        let mut R: GenericArray<u8, _> = ProjectivePoint::mul_by_generator(&k).to_affine().x();
        R.reverse();

        // 4. Set ๐‘†0 โ† โŸจ๏ธ€belt-hash(OID(โ„Ž) โ€– โŸจ๐‘…โŸฉ2๐‘™ โ€– ๐ป)โŸฉ๏ธ€_๐‘™.
        let mut hasher = BeltHash::new();
        hasher.update(BELT_OID);
        hasher.update(R);
        hasher.update(prehash);

        let mut s0 = hasher.finalize();
        s0[16..].fill(0x00);
        s0.reverse();

        let s0_scalar = Scalar::from_slice(&s0).map_err(|_| Error::new())?;

        let right = s0_scalar
            .add(&Scalar::from_u64(2).pow([128, 0, 0, 0]))
            .multiply(self.as_nonzero_scalar());

        // 5. Set ๐‘†1 โ† โŸจ๏ธ€(๐‘˜ โˆ’ ๐ป โˆ’ (๐‘†0 + 2^๐‘™)๐‘‘) mod ๐‘žโŸฉ๏ธ€_2๐‘™.
        let s1 = k.sub(&h).sub(&right);

        // 6. Set ๐‘† โ† ๐‘†0 โ€– ๐‘†1.
        // 7. Return S.
        Signature::from_scalars(s0_scalar, s1)
    }
}

impl Signer<Signature> for SigningKey {
    fn try_sign(&self, msg: &[u8]) -> Result<Signature> {
        // 1. Set ๐ป โ† โ„Ž(๐‘‹).
        let hash = self.verifying_key.hash_msg(msg);
        self.sign_prehash(&hash)
    }
}

//
// Other trait impls
//

impl AsRef<VerifyingKey> for SigningKey {
    fn as_ref(&self) -> &VerifyingKey {
        &self.verifying_key
    }
}

impl ConstantTimeEq for SigningKey {
    fn ct_eq(&self, other: &Self) -> Choice {
        self.secret_scalar.ct_eq(&other.secret_scalar)
    }
}

impl Debug for SigningKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SigningKey")
            .field("verifying_key", &self.verifying_key)
            .finish_non_exhaustive()
    }
}

/// Constant-time comparison
impl Eq for SigningKey {}
impl PartialEq for SigningKey {
    fn eq(&self, other: &SigningKey) -> bool {
        self.ct_eq(other).into()
    }
}

impl KeypairRef for SigningKey {
    type VerifyingKey = VerifyingKey;
}