use super::Signature;
use crate::{
AffinePoint, DistId, FieldBytes, Hash, ProjectivePoint, PublicKey, Scalar, Sec1Point, Sm2,
distid::hash_z,
};
use elliptic_curve::{
Curve, Group,
array::typenum::Unsigned,
ops::{LinearCombination, Reduce},
point::AffineCoordinates,
sec1::ToSec1Point,
};
use signature::{Error, MultipartVerifier, Result, Verifier, hazmat::PrehashVerifier};
use sm3::{Sm3, digest::Digest};
#[cfg(feature = "alloc")]
use alloc::{boxed::Box, string::String};
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use crate::pkcs8::{self, EncodePublicKey, spki};
#[cfg(feature = "pkcs8")]
use crate::pkcs8::{
der::AnyRef,
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
};
#[derive(Clone, Debug)]
pub struct VerifyingKey {
public_key: PublicKey,
identity_hash: Hash,
#[cfg(feature = "alloc")]
distid: String,
}
impl VerifyingKey {
pub fn new(distid: &DistId, public_key: PublicKey) -> Result<Self> {
let identity_hash = hash_z(distid, &public_key).map_err(|_| Error::new())?;
Ok(Self {
identity_hash,
public_key,
#[cfg(feature = "alloc")]
distid: distid.into(),
})
}
pub fn from_sec1_bytes(distid: &DistId, bytes: &[u8]) -> Result<Self> {
let public_key = PublicKey::from_sec1_bytes(bytes).map_err(|_| Error::new())?;
Self::new(distid, public_key)
}
pub fn from_affine(distid: &DistId, affine: AffinePoint) -> Result<Self> {
let public_key = PublicKey::from_affine(affine).map_err(|_| Error::new())?;
Self::new(distid, public_key)
}
pub fn as_affine(&self) -> &AffinePoint {
self.public_key.as_affine()
}
#[cfg(feature = "alloc")]
pub fn distid(&self) -> &DistId {
self.distid.as_str()
}
#[cfg(feature = "alloc")]
pub fn to_sec1_bytes(&self) -> Box<[u8]> {
self.public_key.to_sec1_bytes()
}
pub(crate) fn hash_msg(&self, msg: &[&[u8]]) -> Hash {
let mut hasher = Sm3::new_with_prefix(self.identity_hash);
msg.iter().for_each(|slice| hasher.update(slice));
hasher.finalize()
}
}
impl PrehashVerifier<Signature> for VerifyingKey {
fn verify_prehash(&self, prehash: &[u8], signature: &Signature) -> Result<()> {
if prehash.len() != <Sm2 as Curve>::FieldBytesSize::USIZE {
return Err(Error::new());
}
let r = signature.r();
let s = signature.s();
#[allow(deprecated)] let e = Scalar::reduce(FieldBytes::from_slice(prehash));
let t = *r + *s;
if t.is_zero().into() {
return Err(Error::new());
}
let x = ProjectivePoint::lincomb(&[
(ProjectivePoint::generator(), *s),
(ProjectivePoint::from(&self.public_key), t),
])
.to_affine()
.x();
if *r == e + Scalar::reduce(&x) {
Ok(())
} else {
Err(Error::new())
}
}
}
impl Verifier<Signature> for VerifyingKey {
fn verify(&self, msg: &[u8], signature: &Signature) -> Result<()> {
self.multipart_verify(&[msg], signature)
}
}
impl MultipartVerifier<Signature> for VerifyingKey {
fn multipart_verify(&self, msg: &[&[u8]], signature: &Signature) -> Result<()> {
let hash = self.hash_msg(msg);
self.verify_prehash(&hash, signature)
}
}
impl AsRef<AffinePoint> for VerifyingKey {
fn as_ref(&self) -> &AffinePoint {
self.as_affine()
}
}
impl From<VerifyingKey> for PublicKey {
fn from(verifying_key: VerifyingKey) -> PublicKey {
verifying_key.public_key
}
}
impl From<&VerifyingKey> for PublicKey {
fn from(verifying_key: &VerifyingKey) -> PublicKey {
verifying_key.public_key
}
}
impl ToSec1Point<Sm2> for VerifyingKey {
fn to_sec1_point(&self, compress: bool) -> Sec1Point {
self.as_affine().to_sec1_point(compress)
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl EncodePublicKey for VerifyingKey {
fn to_public_key_der(&self) -> spki::Result<pkcs8::Document> {
self.public_key.to_public_key_der()
}
}
#[cfg(feature = "pkcs8")]
impl SignatureAlgorithmIdentifier for VerifyingKey {
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::ALGORITHM_IDENTIFIER;
}