use crate::{
AffinePoint, CurveArithmetic, CurveGroup, Error, NonZeroScalar, ProjectivePoint, Result,
SecretKey, point::NonIdentity,
};
use core::fmt::Debug;
use group::Group;
#[cfg(feature = "pkcs8")]
use pkcs8::spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, ObjectIdentifier};
#[cfg(feature = "pem")]
use {
alloc::string::{String, ToString},
core::str::FromStr,
};
#[cfg(feature = "sec1")]
use {
crate::{
FieldBytesSize,
ctutils::{Choice, CtOption},
point::PointCompression,
sec1::{CompressedPoint, FromSec1Point, ModulusSize, Sec1Point, ToSec1Point},
},
core::cmp::Ordering,
};
#[cfg(feature = "serde")]
use serdect::serde::{Deserialize, Serialize, de, ser};
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use pkcs8::EncodePublicKey;
#[cfg(all(feature = "alloc", feature = "sec1"))]
use alloc::boxed::Box;
#[cfg(any(feature = "pem", feature = "serde"))]
use pkcs8::DecodePublicKey;
#[cfg(all(feature = "sec1", feature = "pkcs8"))]
use {
crate::{ALGORITHM_OID, pkcs8::AssociatedOid},
pkcs8::der,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PublicKey<C>
where
C: CurveArithmetic,
{
point: AffinePoint<C>,
}
impl<C> PublicKey<C>
where
C: CurveArithmetic,
{
pub fn from_affine(point: AffinePoint<C>) -> Result<Self> {
if ProjectivePoint::<C>::from(point).is_identity().into() {
Err(Error)
} else {
Ok(Self { point })
}
}
pub fn from_secret_scalar(scalar: &NonZeroScalar<C>) -> Self {
Self {
point: ProjectivePoint::<C>::mul_by_generator(scalar).to_affine(),
}
}
#[cfg(feature = "sec1")]
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self>
where
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
{
let point = Sec1Point::<C>::from_bytes(bytes).map_err(|_| Error)?;
Self::from_sec1_point(&point).into_option().ok_or(Error)
}
#[cfg(all(feature = "alloc", feature = "sec1"))]
pub fn to_sec1_bytes(&self) -> Box<[u8]>
where
C: PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
Sec1Point::<C>::from(self).to_bytes()
}
pub fn as_affine(&self) -> &AffinePoint<C> {
&self.point
}
pub fn to_projective(&self) -> ProjectivePoint<C> {
self.point.into()
}
pub fn to_nonidentity(&self) -> NonIdentity<AffinePoint<C>> {
NonIdentity::new_unchecked(self.point)
}
}
impl<C> AsRef<AffinePoint<C>> for PublicKey<C>
where
C: CurveArithmetic,
{
fn as_ref(&self) -> &AffinePoint<C> {
self.as_affine()
}
}
impl<C> Copy for PublicKey<C> where C: CurveArithmetic {}
#[cfg(feature = "sec1")]
impl<C> FromSec1Point<C> for PublicKey<C>
where
C: CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn from_sec1_point(encoded_point: &Sec1Point<C>) -> CtOption<Self> {
AffinePoint::<C>::from_sec1_point(encoded_point).and_then(|point| {
let is_identity = Choice::from_u8_lsb(u8::from(encoded_point.is_identity()));
CtOption::new(PublicKey { point }, !is_identity)
})
}
}
#[cfg(feature = "sec1")]
impl<C> ToSec1Point<C> for PublicKey<C>
where
C: CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn to_sec1_point(&self, compress: bool) -> Sec1Point<C> {
self.point.to_sec1_point(compress)
}
}
#[cfg(feature = "sec1")]
impl<C> From<PublicKey<C>> for CompressedPoint<C>
where
C: CurveArithmetic + PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn from(public_key: PublicKey<C>) -> CompressedPoint<C> {
CompressedPoint::<C>::from(&public_key)
}
}
#[cfg(feature = "sec1")]
impl<C> From<&PublicKey<C>> for CompressedPoint<C>
where
C: CurveArithmetic + PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn from(public_key: &PublicKey<C>) -> CompressedPoint<C> {
public_key
.to_sec1_point(true)
.as_bytes()
.try_into()
.expect("wrong compressed point size")
}
}
#[cfg(feature = "sec1")]
impl<C> From<PublicKey<C>> for Sec1Point<C>
where
C: CurveArithmetic + PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn from(public_key: PublicKey<C>) -> Sec1Point<C> {
Sec1Point::<C>::from(&public_key)
}
}
#[cfg(feature = "sec1")]
impl<C> From<&PublicKey<C>> for Sec1Point<C>
where
C: CurveArithmetic + PointCompression,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn from(public_key: &PublicKey<C>) -> Sec1Point<C> {
public_key.to_sec1_point(C::COMPRESS_POINTS)
}
}
impl<C, P> From<NonIdentity<P>> for PublicKey<C>
where
C: CurveArithmetic,
P: Copy + Into<AffinePoint<C>>,
{
fn from(value: NonIdentity<P>) -> Self {
Self::from(&value)
}
}
impl<C, P> From<&NonIdentity<P>> for PublicKey<C>
where
C: CurveArithmetic,
P: Copy + Into<AffinePoint<C>>,
{
fn from(value: &NonIdentity<P>) -> Self {
Self {
point: value.to_point().into(),
}
}
}
impl<C> From<PublicKey<C>> for NonIdentity<AffinePoint<C>>
where
C: CurveArithmetic,
{
fn from(value: PublicKey<C>) -> Self {
Self::from(&value)
}
}
impl<C> From<&PublicKey<C>> for NonIdentity<AffinePoint<C>>
where
C: CurveArithmetic,
{
fn from(value: &PublicKey<C>) -> Self {
PublicKey::to_nonidentity(value)
}
}
impl<C> From<SecretKey<C>> for PublicKey<C>
where
C: CurveArithmetic,
{
fn from(secret_key: SecretKey<C>) -> Self {
secret_key.public_key()
}
}
impl<C> From<&SecretKey<C>> for PublicKey<C>
where
C: CurveArithmetic,
{
fn from(secret_key: &SecretKey<C>) -> PublicKey<C> {
secret_key.public_key()
}
}
#[cfg(feature = "sec1")]
impl<C> PartialOrd for PublicKey<C>
where
C: CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(feature = "sec1")]
impl<C> Ord for PublicKey<C>
where
C: CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn cmp(&self, other: &Self) -> Ordering {
self.to_sec1_point(false).cmp(&other.to_sec1_point(false))
}
}
#[cfg(feature = "sec1")]
impl<C> TryFrom<CompressedPoint<C>> for PublicKey<C>
where
C: CurveArithmetic,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
{
type Error = Error;
fn try_from(point: CompressedPoint<C>) -> Result<Self> {
Self::from_sec1_bytes(&point)
}
}
#[cfg(feature = "sec1")]
impl<C> TryFrom<&CompressedPoint<C>> for PublicKey<C>
where
C: CurveArithmetic,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
{
type Error = Error;
fn try_from(point: &CompressedPoint<C>) -> Result<Self> {
Self::from_sec1_bytes(point)
}
}
#[cfg(feature = "sec1")]
impl<C> TryFrom<Sec1Point<C>> for PublicKey<C>
where
C: CurveArithmetic,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
{
type Error = Error;
fn try_from(point: Sec1Point<C>) -> Result<Self> {
Self::from_sec1_bytes(point.as_bytes())
}
}
#[cfg(feature = "sec1")]
impl<C> TryFrom<&Sec1Point<C>> for PublicKey<C>
where
C: CurveArithmetic,
FieldBytesSize<C>: ModulusSize,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
{
type Error = Error;
fn try_from(point: &Sec1Point<C>) -> Result<Self> {
Self::from_sec1_bytes(point.as_bytes())
}
}
#[cfg(feature = "pkcs8")]
impl<C> AssociatedAlgorithmIdentifier for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
{
type Params = ObjectIdentifier;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> = AlgorithmIdentifier {
oid: ALGORITHM_OID,
parameters: Some(C::OID),
};
}
#[cfg(feature = "pkcs8")]
impl<C> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'_>> for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
type Error = pkcs8::spki::Error;
fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
Self::try_from(&spki)
}
}
#[cfg(feature = "pkcs8")]
impl<C> TryFrom<&pkcs8::SubjectPublicKeyInfoRef<'_>> for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
type Error = pkcs8::spki::Error;
fn try_from(spki: &pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
spki.algorithm.assert_oids(ALGORITHM_OID, C::OID)?;
let public_key_bytes = spki
.subject_public_key
.as_bytes()
.ok_or_else(|| der::Tag::BitString.value_error().to_error())?;
Self::from_sec1_bytes(public_key_bytes)
.map_err(|_| der::Tag::BitString.value_error().to_error().into())
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl<C> EncodePublicKey for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<der::Document> {
let public_key_bytes = self.to_sec1_point(false);
let subject_public_key = der::asn1::BitStringRef::new(0, public_key_bytes.as_bytes())?;
pkcs8::SubjectPublicKeyInfo {
algorithm: Self::ALGORITHM_IDENTIFIER,
subject_public_key,
}
.try_into()
}
}
#[cfg(feature = "pem")]
impl<C> FromStr for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_public_key_pem(s).map_err(|_| Error)
}
}
#[cfg(feature = "pem")]
#[allow(clippy::to_string_trait_impl)]
impl<C> ToString for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn to_string(&self) -> String {
self.to_public_key_pem(Default::default())
.expect("PEM encoding error")
}
}
#[cfg(feature = "serde")]
impl<C> Serialize for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let der = self.to_public_key_der().map_err(ser::Error::custom)?;
serdect::slice::serialize_hex_upper_or_bin(&der, serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C> Deserialize<'de> for PublicKey<C>
where
C: AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromSec1Point<C> + ToSec1Point<C>,
FieldBytesSize<C>: ModulusSize,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let der_bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
Self::from_public_key_der(&der_bytes).map_err(de::Error::custom)
}
}
#[cfg(all(feature = "dev", test))]
mod tests {
use crate::{dev::MockCurve, sec1::FromSec1Point};
type Sec1Point = crate::sec1::Sec1Point<MockCurve>;
type PublicKey = super::PublicKey<MockCurve>;
#[test]
fn from_sec1_point_rejects_identity() {
let identity = Sec1Point::identity();
assert!(bool::from(PublicKey::from_sec1_point(&identity).is_none()));
}
}