use std::{error::Error, fmt, marker::PhantomData};
use crate::{error::CryptError, protocol::envelope::Envelope, sign::SignAlgorithm};
pub const SIGNED_ENVELOPE_V1_LABEL: &[u8] = b"crypt_guard:signed-envelope";
pub const SIGNED_ENVELOPE_V1: u16 = 1;
pub struct SignedEnvelopeParts<S> {
pub version: u16,
pub envelope: Envelope,
pub recipient_key_id: Vec<u8>,
pub metadata: Vec<u8>,
pub signature: S,
}
#[derive(Debug)]
pub enum SignedEnvelopeError {
UnsupportedVersion {
version: u16,
},
FieldTooLong {
field: &'static str,
length: usize,
},
EnvelopeSerialization(CryptError),
Signing(CryptError),
SignatureVerification(CryptError),
}
impl fmt::Display for SignedEnvelopeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::UnsupportedVersion { version } => {
write!(f, "unsupported signed-envelope version {version}")
}
Self::FieldTooLong { field, length } => write!(
f,
"signed-envelope field {field} is {length} bytes and exceeds the version-1 u32 limit"
),
Self::EnvelopeSerialization(error) => {
write!(f, "signed-envelope canonical serialization failed: {error}")
}
Self::Signing(error) => write!(f, "signed-envelope signing failed: {error}"),
Self::SignatureVerification(error) => {
write!(f, "signed-envelope signature verification failed: {error}")
}
}
}
}
impl Error for SignedEnvelopeError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::EnvelopeSerialization(error)
| Self::Signing(error)
| Self::SignatureVerification(error) => Some(error),
Self::UnsupportedVersion { .. } | Self::FieldTooLong { .. } => None,
}
}
}
pub struct SignedEnvelope<A: SignAlgorithm> {
version: u16,
envelope: Envelope,
recipient_key_id: Vec<u8>,
metadata: Vec<u8>,
signature: A::Sig,
}
impl<A: SignAlgorithm> SignedEnvelope<A> {
pub fn sign(
signing_key: &A::SigningKey,
envelope: Envelope,
recipient_key_id: &[u8],
metadata: &[u8],
) -> Result<Self, SignedEnvelopeError> {
let transcript =
canonical_transcript(SIGNED_ENVELOPE_V1, &envelope, recipient_key_id, metadata)?;
let signature = A::sign(signing_key, &transcript).map_err(SignedEnvelopeError::Signing)?;
Ok(Self {
version: SIGNED_ENVELOPE_V1,
envelope,
recipient_key_id: recipient_key_id.to_vec(),
metadata: metadata.to_vec(),
signature,
})
}
pub fn from_parts(parts: SignedEnvelopeParts<A::Sig>) -> Self {
Self {
version: parts.version,
envelope: parts.envelope,
recipient_key_id: parts.recipient_key_id,
metadata: parts.metadata,
signature: parts.signature,
}
}
pub fn signature(&self) -> &A::Sig {
&self.signature
}
pub fn verify(
&self,
verifying_key: &A::VerifyingKey,
) -> Result<VerifiedSignedEnvelope<'_, A>, SignedEnvelopeError> {
if self.version != SIGNED_ENVELOPE_V1 {
return Err(SignedEnvelopeError::UnsupportedVersion {
version: self.version,
});
}
let transcript = canonical_transcript(
self.version,
&self.envelope,
&self.recipient_key_id,
&self.metadata,
)?;
A::verify(verifying_key, &transcript, &self.signature)
.map_err(SignedEnvelopeError::SignatureVerification)?;
Ok(VerifiedSignedEnvelope {
inner: self,
_marker: PhantomData,
})
}
}
pub struct VerifiedSignedEnvelope<'a, A: SignAlgorithm> {
inner: &'a SignedEnvelope<A>,
_marker: PhantomData<A>,
}
impl<'a, A: SignAlgorithm> VerifiedSignedEnvelope<'a, A> {
pub const fn version(&self) -> u16 {
self.inner.version
}
pub fn envelope(&self) -> &Envelope {
&self.inner.envelope
}
pub fn recipient_key_id(&self) -> &[u8] {
&self.inner.recipient_key_id
}
pub fn metadata(&self) -> &[u8] {
&self.inner.metadata
}
}
fn canonical_transcript(
version: u16,
envelope: &Envelope,
recipient_key_id: &[u8],
metadata: &[u8],
) -> Result<Vec<u8>, SignedEnvelopeError> {
let envelope_bytes = envelope
.try_to_bytes()
.map_err(SignedEnvelopeError::EnvelopeSerialization)?;
Envelope::from_bytes(&envelope_bytes).map_err(SignedEnvelopeError::EnvelopeSerialization)?;
let mut transcript = Vec::with_capacity(
SIGNED_ENVELOPE_V1_LABEL.len()
+ 2
+ (4 * 3)
+ envelope_bytes.len()
+ recipient_key_id.len()
+ metadata.len(),
);
transcript.extend_from_slice(SIGNED_ENVELOPE_V1_LABEL);
transcript.extend_from_slice(&version.to_be_bytes());
append_field(&mut transcript, "envelope", &envelope_bytes)?;
append_field(&mut transcript, "recipient_key_id", recipient_key_id)?;
append_field(&mut transcript, "metadata", metadata)?;
Ok(transcript)
}
fn append_field(
transcript: &mut Vec<u8>,
field: &'static str,
value: &[u8],
) -> Result<(), SignedEnvelopeError> {
let length = u32::try_from(value.len()).map_err(|_| SignedEnvelopeError::FieldTooLong {
field,
length: value.len(),
})?;
transcript.extend_from_slice(&length.to_be_bytes());
transcript.extend_from_slice(value);
Ok(())
}