use crate::admission_budget::{AdmissionBudgetError, SessionAdmissionBudget};
use crate::portable::{
PortableClaimsContext, PortableClaimsError, PortableEncodingError, VerifiedPortableClaims,
claims_hash, decode_claims, validate_claims_context,
};
use curve25519_dalek::scalar::Scalar;
use ed25519_dalek::{Signature, VerifyingKey};
use ic_auth_protocol_types::PortableMechanism;
use sha2::{Digest, Sha256};
use thiserror::Error;
#[derive(Debug, Error, PartialEq, Eq)]
pub enum PortableSignatureError {
#[error(transparent)]
Budget(#[from] AdmissionBudgetError),
#[error(transparent)]
Encoding(#[from] PortableEncodingError),
#[error(transparent)]
Claims(#[from] PortableClaimsError),
#[error("invalid or weak Ed25519 public key")]
InvalidPublicKey,
#[error("Ed25519 signature must contain exactly 64 bytes")]
MalformedSignature,
#[error("invalid Ed25519 signature")]
InvalidSignature,
}
pub fn verify_ed25519_claims(
budget: &mut SessionAdmissionBudget,
encoded_claims: &[u8],
signature: &[u8],
context: &PortableClaimsContext<'_>,
public_key: &[u8; 32],
) -> Result<VerifiedPortableClaims, PortableSignatureError> {
let claims = decode_claims(encoded_claims)?;
if context.mechanism != PortableMechanism::Ed25519 {
return Err(PortableClaimsError::MechanismMismatch.into());
}
validate_claims_context(&claims, context)?;
let key_id: [u8; 32] = Sha256::digest(public_key).into();
if context.key_binding_id != key_id {
return Err(PortableClaimsError::KeyMismatch.into());
}
let key = VerifyingKey::from_bytes(public_key)
.map_err(|_| PortableSignatureError::InvalidPublicKey)?;
if key.is_weak() {
return Err(PortableSignatureError::InvalidPublicKey);
}
let signature =
Signature::from_slice(signature).map_err(|_| PortableSignatureError::MalformedSignature)?;
if bool::from(Scalar::from_canonical_bytes(*signature.s_bytes()).is_none()) {
return Err(PortableSignatureError::InvalidSignature);
}
let fingerprint = claims_hash(&claims)?;
budget.consume(context.now_ns)?;
key.verify_strict(&fingerprint, &signature)
.map_err(|_| PortableSignatureError::InvalidSignature)?;
Ok(VerifiedPortableClaims::new(claims, fingerprint, context))
}