bignp256 0.14.0-rc.10

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
//! Bign256 secret key.
// TODO(tarcieri): replace with `elliptic_curve::SecretKey`

use core::str::FromStr;
use der::{SecretDocument, asn1::OctetStringRef};

#[cfg(feature = "pkcs8")]
use crate::ALGORITHM_OID;
use crate::{PublicKey, ScalarValue};
use elliptic_curve::{Error, Generate, array::typenum::Unsigned, zeroize::Zeroizing};
#[cfg(feature = "pkcs8")]
use pkcs8::{
    AssociatedOid, DecodePrivateKey, EncodePrivateKey, ObjectIdentifier,
    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier},
};

#[cfg(feature = "arithmetic")]
use crate::{BignP256, FieldBytes, NonZeroScalar, Result, elliptic_curve::rand_core::TryCryptoRng};

/// Elliptic curve BignP256 Secret Key
#[cfg(feature = "arithmetic")]
#[derive(Copy, Clone, Debug)]
pub struct SecretKey {
    inner: ScalarValue,
}

impl SecretKey {
    const MIN_SIZE: usize = 24;

    /// Borrow the inner secret [`elliptic_curve::ScalarValue`] value.
    ///
    /// # ⚠️ Warning
    ///
    /// This value is key material.
    ///
    /// Please treat it with the care it deserves!
    pub fn as_scalar_primitive(&self) -> &ScalarValue {
        &self.inner
    }

    /// Get the secret [`elliptic_curve::NonZeroScalar`] value for this key.
    ///
    /// # ⚠️ Warning
    ///
    /// This value is key material.
    ///
    /// Please treat it with the care it deserves!
    #[cfg(feature = "arithmetic")]
    pub fn to_nonzero_scalar(&self) -> NonZeroScalar {
        (*self).into()
    }

    /// Get the [`PublicKey`] which corresponds to this secret key
    #[cfg(feature = "arithmetic")]
    pub fn public_key(&self) -> PublicKey {
        PublicKey::from_secret_scalar(&self.to_nonzero_scalar())
    }

    /// Deserialize secret key from an encoded secret scalar.
    pub fn from_bytes(bytes: &FieldBytes) -> Result<Self> {
        let inner = ScalarValue::from_bytes(bytes).into_option().ok_or(Error)?;

        if inner.is_zero().into() {
            return Err(Error);
        }

        Ok(Self { inner })
    }

    /// Deserialize secret key from an encoded secret scalar passed as a byte slice.
    ///
    /// The slice is expected to be a minimum of 24-bytes (192-bytes) and at most
    /// `C::FieldBytesSize` bytes in length.
    ///
    /// Byte slices shorter than the field size are handled by zero padding the input.
    ///
    /// NOTE: this function is variable-time with respect to the input length. To avoid a timing
    /// sidechannel, always ensure that the input has been pre-padded to `C::FieldBytesSize`.
    pub fn from_slice(slice: &[u8]) -> Result<Self> {
        if slice.len() == <BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE {
            Self::from_bytes(&FieldBytes::try_from(slice).map_err(|_| Error)?)
        } else if (Self::MIN_SIZE..<BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE)
            .contains(&slice.len())
        {
            let mut bytes = Zeroizing::new(FieldBytes::default());
            let offset = <BignP256 as elliptic_curve::Curve>::FieldBytesSize::USIZE
                .saturating_sub(slice.len());
            bytes[offset..].copy_from_slice(slice);
            Self::from_bytes(&bytes)
        } else {
            Err(Error)
        }
    }

    /// Serialize raw secret scalar as a big endian integer.
    pub fn to_bytes(&self) -> FieldBytes {
        self.inner.to_bytes()
    }
}

#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for SecretKey {
    type Params = ObjectIdentifier;
    const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> = AlgorithmIdentifier {
        oid: ALGORITHM_OID,
        parameters: Some(BignP256::OID),
    };
}

impl From<SecretKey> for NonZeroScalar {
    fn from(secret_key: SecretKey) -> NonZeroScalar {
        secret_key.to_nonzero_scalar()
    }
}

#[cfg(feature = "arithmetic")]
impl From<NonZeroScalar> for SecretKey {
    fn from(scalar: NonZeroScalar) -> SecretKey {
        SecretKey::from(&scalar)
    }
}

#[cfg(feature = "arithmetic")]
impl From<&NonZeroScalar> for SecretKey {
    fn from(scalar: &NonZeroScalar) -> SecretKey {
        SecretKey {
            inner: scalar.into(),
        }
    }
}

impl Generate for SecretKey {
    fn try_generate_from_rng<R: TryCryptoRng + ?Sized>(
        rng: &mut R,
    ) -> core::result::Result<Self, R::Error> {
        Ok(Self {
            inner: ScalarValue::try_generate_from_rng(rng)?,
        })
    }
}

#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PrivateKeyInfoRef<'_>> for SecretKey {
    type Error = pkcs8::Error;

    fn try_from(private_key_info: pkcs8::PrivateKeyInfoRef<'_>) -> pkcs8::Result<Self> {
        private_key_info
            .algorithm
            .assert_oids(ALGORITHM_OID, BignP256::OID)?;

        Self::from_slice(private_key_info.private_key.as_bytes())
            .map_err(|_| pkcs8::KeyError::Invalid.into())
    }
}

#[cfg(feature = "pem")]
impl FromStr for SecretKey {
    type Err = Error;
    fn from_str(s: &str) -> core::result::Result<Self, Error> {
        Self::from_pkcs8_pem(s).map_err(|_| Error)
    }
}

#[cfg(feature = "pkcs8")]
impl EncodePrivateKey for SecretKey {
    fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
        let algorithm_identifier = pkcs8::AlgorithmIdentifierRef {
            oid: ALGORITHM_OID,
            parameters: Some((&BignP256::OID).into()),
        };

        let ec_private_key = self.to_bytes();
        let pkcs8_key = pkcs8::PrivateKeyInfoRef::new(
            algorithm_identifier,
            OctetStringRef::new(&ec_private_key)?,
        );
        Ok(SecretDocument::encode_msg(&pkcs8_key)?)
    }
}