#[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};
#[cfg(feature = "arithmetic")]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey {
point: AffinePoint,
}
impl PublicKey {
pub fn from_affine(point: AffinePoint) -> Result<Self, Error> {
if ProjectivePoint::from(point).is_identity().into() {
Err(Error)
} else {
Ok(Self { point })
}
}
pub fn from_secret_scalar(scalar: &NonZeroScalar) -> Self {
#[allow(clippy::arithmetic_side_effects)]
Self {
point: (<BignP256 as CurveArithmetic>::ProjectivePoint::generator() * scalar.as_ref())
.to_affine(),
}
}
pub fn as_affine(&self) -> &AffinePoint {
&self.point
}
pub fn to_projective(&self) -> ProjectivePoint {
self.point.into()
}
pub fn to_nonidentity(&self) -> NonIdentity<AffinePoint> {
NonIdentity::new(self.point).unwrap()
}
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(),
})
}
}
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")]
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")]
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")
)
}
}