use core::fmt::Debug;
use core::hash::{Hash, Hasher};
use curve25519_dalek::{
digest::{generic_array::typenum::U64, Digest},
edwards::{CompressedEdwardsY, EdwardsPoint},
montgomery::MontgomeryPoint,
scalar::Scalar,
};
use ed25519::signature::Verifier;
use sha2::Sha512;
#[cfg(feature = "pkcs8")]
use ed25519::pkcs8;
#[cfg(feature = "serde")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "digest")]
use crate::context::Context;
#[cfg(feature = "digest")]
use signature::DigestVerifier;
use crate::{
constants::PUBLIC_KEY_LENGTH,
errors::{InternalError, SignatureError},
hazmat::ExpandedSecretKey,
signature::InternalSignature,
signing::SigningKey,
};
#[cfg(feature = "hazmat")]
mod stream;
#[cfg(feature = "hazmat")]
pub use self::stream::StreamVerifier;
#[derive(Copy, Clone, Default, Eq)]
pub struct VerifyingKey {
pub(crate) compressed: CompressedEdwardsY,
pub(crate) point: EdwardsPoint,
}
impl Debug for VerifyingKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point)
}
}
impl AsRef<[u8]> for VerifyingKey {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Hash for VerifyingKey {
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state);
}
}
impl PartialEq<VerifyingKey> for VerifyingKey {
fn eq(&self, other: &VerifyingKey) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl From<&ExpandedSecretKey> for VerifyingKey {
fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
VerifyingKey::from(EdwardsPoint::mul_base(&expanded_secret_key.scalar))
}
}
impl From<&SigningKey> for VerifyingKey {
fn from(signing_key: &SigningKey) -> VerifyingKey {
signing_key.verifying_key()
}
}
impl From<EdwardsPoint> for VerifyingKey {
fn from(point: EdwardsPoint) -> VerifyingKey {
VerifyingKey {
point,
compressed: point.compress(),
}
}
}
impl VerifyingKey {
#[inline]
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.compressed.to_bytes()
}
#[inline]
pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
&(self.compressed).0
}
#[inline]
pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result<VerifyingKey, SignatureError> {
let compressed = CompressedEdwardsY(*bytes);
let point = compressed
.decompress()
.ok_or(InternalError::PointDecompression)?;
Ok(VerifyingKey { compressed, point })
}
#[cfg(feature = "digest")]
pub fn with_context<'k, 'v>(
&'k self,
context_value: &'v [u8],
) -> Result<Context<'k, 'v, Self>, SignatureError> {
Context::new(self, context_value)
}
pub fn is_weak(&self) -> bool {
self.point.is_small_order()
}
#[allow(non_snake_case)]
pub(crate) fn raw_verify<CtxDigest>(
&self,
message: &[u8],
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let expected_R = RCompute::<CtxDigest>::compute(self, signature, None, message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub(crate) fn raw_verify_prehashed<CtxDigest, MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
CtxDigest: Digest<OutputSize = U64>,
MsgDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let ctx: &[u8] = context.unwrap_or(b"");
debug_assert!(
ctx.len() <= 255,
"The context must not be longer than 255 octets."
);
let message = prehashed_message.finalize();
let expected_R = RCompute::<CtxDigest>::compute(self, signature, Some(ctx), &message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn verify_prehashed<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
self.raw_verify_prehashed::<Sha512, MsgDigest>(prehashed_message, context, signature)
}
#[allow(non_snake_case)]
pub fn verify_strict(
&self,
message: &[u8],
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
let signature = InternalSignature::try_from(signature)?;
let signature_R = signature
.R
.decompress()
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
if signature_R.is_small_order() || self.point.is_small_order() {
return Err(InternalError::Verify.into());
}
let expected_R = RCompute::<Sha512>::compute(self, signature, None, message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
#[cfg(feature = "hazmat")]
pub fn verify_stream(
&self,
signature: &ed25519::Signature,
) -> Result<StreamVerifier, SignatureError> {
let signature = InternalSignature::try_from(signature)?;
Ok(StreamVerifier::new(*self, signature))
}
#[cfg(feature = "digest")]
#[allow(non_snake_case)]
pub fn verify_prehashed_strict<MsgDigest>(
&self,
prehashed_message: MsgDigest,
context: Option<&[u8]>,
signature: &ed25519::Signature,
) -> Result<(), SignatureError>
where
MsgDigest: Digest<OutputSize = U64>,
{
let signature = InternalSignature::try_from(signature)?;
let ctx: &[u8] = context.unwrap_or(b"");
debug_assert!(
ctx.len() <= 255,
"The context must not be longer than 255 octets."
);
let signature_R = signature
.R
.decompress()
.ok_or_else(|| SignatureError::from(InternalError::Verify))?;
if signature_R.is_small_order() || self.point.is_small_order() {
return Err(InternalError::Verify.into());
}
let message = prehashed_message.finalize();
let expected_R = RCompute::<Sha512>::compute(self, signature, Some(ctx), &message);
if expected_R == signature.R {
Ok(())
} else {
Err(InternalError::Verify.into())
}
}
pub fn to_montgomery(&self) -> MontgomeryPoint {
self.point.to_montgomery()
}
pub fn to_edwards(&self) -> EdwardsPoint {
self.point
}
}
pub(crate) struct RCompute<CtxDigest> {
key: VerifyingKey,
signature: InternalSignature,
h: CtxDigest,
}
#[allow(non_snake_case)]
impl<CtxDigest> RCompute<CtxDigest>
where
CtxDigest: Digest<OutputSize = U64>,
{
pub(crate) fn compute(
key: &VerifyingKey,
signature: InternalSignature,
prehash_ctx: Option<&[u8]>,
message: &[u8],
) -> CompressedEdwardsY {
let mut c = Self::new(key, signature, prehash_ctx);
c.update(message);
c.finish()
}
pub(crate) fn new(
key: &VerifyingKey,
signature: InternalSignature,
prehash_ctx: Option<&[u8]>,
) -> Self {
let R = &signature.R;
let A = &key.compressed;
let mut h = CtxDigest::new();
if let Some(c) = prehash_ctx {
h.update(b"SigEd25519 no Ed25519 collisions");
h.update([1]); h.update([c.len() as u8]);
h.update(c);
}
h.update(R.as_bytes());
h.update(A.as_bytes());
Self {
key: *key,
signature,
h,
}
}
pub(crate) fn update(&mut self, m: &[u8]) {
self.h.update(m)
}
pub(crate) fn finish(self) -> CompressedEdwardsY {
let k = Scalar::from_hash(self.h);
let minus_A: EdwardsPoint = -self.key.point;
EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &self.signature.s)
.compress()
}
}
impl Verifier<ed25519::Signature> for VerifyingKey {
fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
self.raw_verify::<Sha512>(message, signature)
}
}
#[cfg(feature = "digest")]
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for VerifyingKey
where
MsgDigest: Digest<OutputSize = U64>,
{
fn verify_digest(
&self,
msg_digest: MsgDigest,
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
self.verify_prehashed(msg_digest, None, signature)
}
}
#[cfg(feature = "digest")]
impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for Context<'_, '_, VerifyingKey>
where
MsgDigest: Digest<OutputSize = U64>,
{
fn verify_digest(
&self,
msg_digest: MsgDigest,
signature: &ed25519::Signature,
) -> Result<(), SignatureError> {
self.key()
.verify_prehashed(msg_digest, Some(self.value()), signature)
}
}
impl TryFrom<&[u8]> for VerifyingKey {
type Error = SignatureError;
#[inline]
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
let bytes = bytes.try_into().map_err(|_| InternalError::BytesLength {
name: "VerifyingKey",
length: PUBLIC_KEY_LENGTH,
})?;
Self::from_bytes(bytes)
}
}
impl From<VerifyingKey> for EdwardsPoint {
fn from(vk: VerifyingKey) -> EdwardsPoint {
vk.point
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::EncodePublicKey for VerifyingKey {
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
pkcs8::PublicKeyBytes::from(self).to_public_key_der()
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl pkcs8::spki::DynSignatureAlgorithmIdentifier for VerifyingKey {
fn signature_algorithm_identifier(
&self,
) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
Ok(ed25519::pkcs8::spki::AlgorithmIdentifierOwned {
oid: ed25519::pkcs8::ALGORITHM_OID,
parameters: None,
})
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::PublicKeyBytes> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
VerifyingKey::try_from(&pkcs8_key)
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
}
}
#[cfg(feature = "pkcs8")]
impl From<VerifyingKey> for pkcs8::PublicKeyBytes {
fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes {
pkcs8::PublicKeyBytes::from(&verifying_key)
}
}
#[cfg(feature = "pkcs8")]
impl From<&VerifyingKey> for pkcs8::PublicKeyBytes {
fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes {
pkcs8::PublicKeyBytes(verifying_key.to_bytes())
}
}
#[cfg(feature = "pkcs8")]
impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for VerifyingKey {
type Error = pkcs8::spki::Error;
fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
pkcs8::PublicKeyBytes::try_from(public_key)?.try_into()
}
}
#[cfg(feature = "serde")]
impl Serialize for VerifyingKey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_bytes(&self.as_bytes()[..])
}
}
#[cfg(feature = "serde")]
impl<'d> Deserialize<'d> for VerifyingKey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'d>,
{
struct VerifyingKeyVisitor;
impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor {
type Value = VerifyingKey;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, concat!("An ed25519 verifying (public) key"))
}
fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
VerifyingKey::try_from(bytes).map_err(E::custom)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut bytes = [0u8; 32];
#[allow(clippy::needless_range_loop)]
for i in 0..32 {
bytes[i] = seq
.next_element()?
.ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
}
let remaining = (0..)
.map(|_| seq.next_element::<u8>())
.take_while(|el| matches!(el, Ok(Some(_))))
.count();
if remaining > 0 {
return Err(serde::de::Error::invalid_length(
32 + remaining,
&"expected 32 bytes",
));
}
VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom)
}
}
deserializer.deserialize_bytes(VerifyingKeyVisitor)
}
}