use crate::{
consts::U1,
scalar::NonZeroScalar,
sec1::{
EncodedPoint, FromEncodedPoint, ToEncodedPoint, UncompressedPointSize, UntaggedPointSize,
},
weierstrass::{point, Curve},
AffinePoint, Error, FieldBytes, ProjectiveArithmetic, ProjectivePoint, Scalar,
};
use core::{
convert::{TryFrom, TryInto},
fmt::Debug,
ops::Add,
};
use ff::PrimeField;
use generic_array::ArrayLength;
use group::{Curve as _, Group};
#[cfg(feature = "pkcs8")]
use {
crate::{AlgorithmParameters, ALGORITHM_OID},
pkcs8::FromPublicKey,
};
#[cfg(feature = "pem")]
use {
alloc::{
string::{String, ToString},
vec::Vec,
},
core::str::FromStr,
pkcs8::ToPublicKey,
};
#[derive(Clone, Debug)]
pub struct PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
{
point: AffinePoint<C>,
}
impl<C> PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
ProjectivePoint<C>: From<AffinePoint<C>>,
{
pub fn from_affine(point: AffinePoint<C>) -> Result<Self, Error> {
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: (C::ProjectivePoint::generator() * scalar.as_ref()).to_affine(),
}
}
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Error>
where
Self: TryFrom<EncodedPoint<C>, Error = Error>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
EncodedPoint::from_bytes(bytes)
.map_err(|_| Error)
.and_then(TryInto::try_into)
}
pub fn as_affine(&self) -> &AffinePoint<C> {
&self.point
}
pub fn to_projective(&self) -> ProjectivePoint<C> {
self.point.clone().into()
}
#[cfg(feature = "pem")]
pub(crate) fn to_der_bitstring(&self) -> Vec<u8>
where
AffinePoint<C>: Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
let mut bitstring = Vec::new();
bitstring.push(0);
bitstring.extend_from_slice(self.to_encoded_point(false).as_ref());
bitstring
}
}
impl<C> AsRef<AffinePoint<C>> for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
ProjectivePoint<C>: From<AffinePoint<C>>,
{
fn as_ref(&self) -> &AffinePoint<C> {
self.as_affine()
}
}
impl<C> TryFrom<EncodedPoint<C>> for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(encoded_point: EncodedPoint<C>) -> Result<Self, Error> {
encoded_point.decode()
}
}
impl<C> TryFrom<&EncodedPoint<C>> for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(encoded_point: &EncodedPoint<C>) -> Result<Self, Error> {
encoded_point.decode()
}
}
impl<C> From<PublicKey<C>> for EncodedPoint<C>
where
C: Curve + ProjectiveArithmetic + point::Compression,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn from(public_key: PublicKey<C>) -> EncodedPoint<C> {
EncodedPoint::<C>::from(&public_key)
}
}
impl<C> From<&PublicKey<C>> for EncodedPoint<C>
where
C: Curve + ProjectiveArithmetic + point::Compression,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn from(public_key: &PublicKey<C>) -> EncodedPoint<C> {
public_key.to_encoded_point(C::COMPRESS_POINTS)
}
}
impl<C> FromEncodedPoint<C> for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn from_encoded_point(encoded_point: &EncodedPoint<C>) -> Option<Self> {
AffinePoint::<C>::from_encoded_point(encoded_point)
.and_then(|point| PublicKey::from_affine(point).ok())
}
}
impl<C> ToEncodedPoint<C> for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
self.point.to_encoded_point(compress)
}
}
impl<C> Copy for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
{
}
impl<C> Eq for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
}
impl<C> PartialEq for PublicKey<C>
where
C: Curve + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn eq(&self, other: &Self) -> bool {
self.to_encoded_point(false) == other.to_encoded_point(false)
}
}
#[cfg(feature = "pkcs8")]
#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
impl<C> FromPublicKey for PublicKey<C>
where
Self: TryFrom<EncodedPoint<C>, Error = Error>,
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn from_spki(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::Result<Self> {
if spki.algorithm.oid != ALGORITHM_OID || spki.algorithm.parameters_oid() != Some(C::OID) {
return Err(pkcs8::Error::Decode);
}
if spki.subject_public_key.get(0).cloned() != Some(0x00) {
return Err(pkcs8::Error::Decode);
}
Self::from_sec1_bytes(&spki.subject_public_key[1..]).map_err(|_| pkcs8::Error::Decode)
}
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl<C> ToPublicKey for PublicKey<C>
where
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn to_public_key_der(&self) -> pkcs8::PublicKeyDocument {
let public_key_bytes = self.to_der_bitstring();
pkcs8::SubjectPublicKeyInfo {
algorithm: C::algorithm_identifier(),
subject_public_key: &public_key_bytes,
}
.to_der()
}
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl<C> FromStr for PublicKey<C>
where
Self: TryFrom<EncodedPoint<C>, Error = Error>,
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
type Err = Error;
fn from_str(s: &str) -> Result<Self, Error> {
Self::from_public_key_pem(s).map_err(|_| Error)
}
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl<C> ToString for PublicKey<C>
where
C: Curve + AlgorithmParameters + ProjectiveArithmetic,
FieldBytes<C>: From<Scalar<C>> + for<'r> From<&'r Scalar<C>>,
Scalar<C>: PrimeField<Repr = FieldBytes<C>>,
AffinePoint<C>: Copy + Clone + Debug + Default + FromEncodedPoint<C> + ToEncodedPoint<C>,
ProjectivePoint<C>: From<AffinePoint<C>>,
UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
UncompressedPointSize<C>: ArrayLength<u8>,
{
fn to_string(&self) -> String {
self.to_public_key_pem()
}
}
#[cfg(all(feature = "dev", test))]
mod tests {
use crate::{dev::MockCurve, sec1::FromEncodedPoint};
type EncodedPoint = crate::sec1::EncodedPoint<MockCurve>;
type PublicKey = super::PublicKey<MockCurve>;
#[test]
fn from_encoded_point_rejects_identity() {
let identity = EncodedPoint::identity();
assert_eq!(PublicKey::from_encoded_point(&identity), None);
}
}