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
//! Public key types and traits
// TODO(tarcieri): replace with `elliptic_curve::PublicKey`

#[cfg(feature = "pkcs8")]
use crate::ALGORITHM_OID;
use crate::{AffinePoint, BignP256, NonZeroScalar, ProjectivePoint, Sec1Point};
use core::{fmt::Display, str::FromStr};
use elliptic_curve::{
    CurveArithmetic, Error, Group,
    array::Array,
    point::NonIdentity,
    sec1::{FromSec1Point, ToSec1Point},
};
#[cfg(feature = "pkcs8")]
use pkcs8::{
    AssociatedOid, DecodePublicKey, EncodePublicKey, ObjectIdentifier,
    spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier},
};

#[cfg(feature = "alloc")]
use alloc::{boxed::Box, fmt};

/// Elliptic curve BignP256 public key.
#[cfg(feature = "arithmetic")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey {
    point: AffinePoint,
}

impl PublicKey {
    /// Convert an [`AffinePoint`] into a [`PublicKey`]
    pub fn from_affine(point: AffinePoint) -> Result<Self, Error> {
        if ProjectivePoint::from(point).is_identity().into() {
            Err(Error)
        } else {
            Ok(Self { point })
        }
    }

    /// Compute a [`PublicKey`] from a secret [`NonZeroScalar`] value
    /// (i.e. a secret key represented as a raw scalar value)
    pub fn from_secret_scalar(scalar: &NonZeroScalar) -> Self {
        // `NonZeroScalar` ensures the resulting point is not the identity
        #[allow(clippy::arithmetic_side_effects)]
        Self {
            point: (<BignP256 as CurveArithmetic>::ProjectivePoint::generator() * scalar.as_ref())
                .to_affine(),
        }
    }

    /// Borrow the inner [`AffinePoint`] from this [`PublicKey`].
    ///
    /// In ECC, public keys are elliptic curve points.
    pub fn as_affine(&self) -> &AffinePoint {
        &self.point
    }

    /// Convert this [`PublicKey`] to a [`ProjectivePoint`] for the given curve
    pub fn to_projective(&self) -> ProjectivePoint {
        self.point.into()
    }

    /// Convert this [`PublicKey`] to a [`NonIdentity`] of the inner [`AffinePoint`]
    pub fn to_nonidentity(&self) -> NonIdentity<AffinePoint> {
        NonIdentity::new(self.point).unwrap()
    }

    /// Get [`PublicKey`] from bytes
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
        let bytes = Array::try_from(bytes).map_err(|_| Error)?;

        let point = Sec1Point::from_untagged_bytes(&bytes);
        let affine = AffinePoint::from_sec1_point(&point);
        if affine.is_none().into() {
            Err(Error)
        } else {
            Ok(Self {
                point: affine.unwrap(),
            })
        }
    }

    /// Get [`PublicKey`] from encoded point
    pub fn from_sec1_point(point: Sec1Point) -> Result<Self, Error> {
        let affine = AffinePoint::from_sec1_point(&point);
        if affine.is_none().into() {
            Err(Error)
        } else {
            Ok(Self {
                point: affine.unwrap(),
            })
        }
    }

    #[cfg(feature = "alloc")]
    /// Get bytes from [`PublicKey`]
    pub fn to_bytes(&self) -> Box<[u8]> {
        let bytes = self.point.to_sec1_point(false).to_bytes();
        bytes[1..].to_vec().into_boxed_slice()
    }

    #[cfg(feature = "alloc")]
    /// Get encoded point from [`PublicKey`]
    pub fn to_sec1_point(&self) -> Sec1Point {
        self.point.to_sec1_point(false)
    }
}

impl AsRef<AffinePoint> for PublicKey {
    fn as_ref(&self) -> &AffinePoint {
        self.as_affine()
    }
}
impl Copy for PublicKey {}
impl From<NonIdentity<AffinePoint>> for PublicKey {
    fn from(value: NonIdentity<AffinePoint>) -> Self {
        Self::from(&value)
    }
}

impl From<&NonIdentity<AffinePoint>> for PublicKey {
    fn from(value: &NonIdentity<AffinePoint>) -> Self {
        Self {
            point: value.to_point(),
        }
    }
}

impl From<PublicKey> for NonIdentity<AffinePoint> {
    fn from(value: PublicKey) -> Self {
        Self::from(&value)
    }
}

impl From<&PublicKey> for NonIdentity<AffinePoint> {
    fn from(value: &PublicKey) -> Self {
        PublicKey::to_nonidentity(value)
    }
}

impl From<PublicKey> for elliptic_curve::PublicKey<BignP256> {
    fn from(value: PublicKey) -> Self {
        elliptic_curve::PublicKey::<BignP256>::from_affine(value.point)
            .expect("should be non-identity")
    }
}

#[cfg(feature = "pkcs8")]
impl AssociatedAlgorithmIdentifier for PublicKey {
    type Params = ObjectIdentifier;

    const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> = AlgorithmIdentifier {
        oid: ALGORITHM_OID,
        parameters: Some(BignP256::OID),
    };
}

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

    fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
        Self::try_from(&spki)
    }
}

#[cfg(feature = "pkcs8")]
impl TryFrom<&pkcs8::SubjectPublicKeyInfoRef<'_>> for PublicKey {
    type Error = pkcs8::spki::Error;

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

        let public_key_bytes = spki
            .subject_public_key
            .as_bytes()
            .ok_or_else(|| der::Tag::BitString.value_error().to_error())?;

        Self::from_bytes(public_key_bytes).map_err(|_| pkcs8::spki::Error::KeyMalformed)
    }
}

#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl EncodePublicKey for PublicKey {
    fn to_public_key_der(&self) -> pkcs8::spki::Result<der::Document> {
        let pk_bytes = self.to_bytes();
        let subject_public_key = der::asn1::BitStringRef::new(0, &pk_bytes)?;

        pkcs8::SubjectPublicKeyInfo {
            algorithm: Self::ALGORITHM_IDENTIFIER,
            subject_public_key,
        }
        .try_into()
    }
}

impl From<PublicKey> for Sec1Point {
    fn from(value: PublicKey) -> Self {
        value.point.to_sec1_point(false)
    }
}

#[cfg(feature = "pem")]
impl FromStr for PublicKey {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Error> {
        Self::from_public_key_pem(s).map_err(|_| Error)
    }
}

#[cfg(feature = "pem")]
impl Display for PublicKey {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            self.to_public_key_pem(Default::default())
                .expect("PEM encoding error")
        )
    }
}