use alloc::vec::Vec;
use crate::claims::{Claims, MessageRole, ValidationParams};
use crate::codec::{self, ClaimsExpectation, NONCE_LEN, Sign1Layer};
use crate::encrypt::{EncryptCore, Opened, build_encrypt_core, random_array};
use crate::error::{BuildError, DecodeError, OpenError, VerifyError};
use crate::kdf::KdfParties;
use crate::keys::X25519RecipientPublic;
use crate::traits::{OpenRequest, Recipient, Signer, Verifier};
use crate::types::{ContentType, CoseBytes, ExternalAad, KeyId, SealedAad};
#[cfg(feature = "fixtures")]
use crate::encrypt::SealParts;
use zeroize::Zeroizing;
#[derive(Debug, Clone)]
pub struct SealParams<'a> {
pub content_type: ContentType,
pub plaintext: &'a [u8],
pub claims: Claims,
pub role: MessageRole,
pub recipient: X25519RecipientPublic,
pub content_algorithm: ContentAlgorithm,
pub aad: SealedAad,
pub kdf_parties: KdfParties,
}
use crate::alg::ContentAlgorithm;
fn check_seal_claims<S: Signer>(params: &SealParams<'_>, signer: &S) -> Result<(), BuildError> {
params
.claims
.validate_role(params.role)
.map_err(BuildError::RoleShape)?;
if params.claims.sender_key_id.as_ref() != Some(signer.key_id()) {
return Err(BuildError::SenderKeyMismatch);
}
if let Some(k) = ¶ms.claims.response_key_id
&& k.as_catalog_name().is_none()
{
return Err(BuildError::ResponseKeyNotText);
}
Ok(())
}
async fn build_sealed_inner<S: Signer>(
params: &SealParams<'_>,
signer: &S,
ephemeral_private: &Zeroizing<[u8; 32]>,
nonce: [u8; NONCE_LEN],
) -> Result<CoseBytes, BuildError> {
check_seal_claims(params, signer)?;
let encrypt_bytes = build_encrypt_core(
&EncryptCore {
content_algorithm: params.content_algorithm,
content_type: ¶ms.content_type,
claims: Some(¶ms.claims),
plaintext: params.plaintext,
recipient: ¶ms.recipient,
external_aad: ¶ms.aad.encryption,
kdf_parties: ¶ms.kdf_parties,
},
ephemeral_private,
nonce,
)?;
let protected = codec::encode_sign1_protected_sealed_outer(signer.algorithm(), signer.key_id())
.map_err(|codec::CodecError| BuildError::Codec)?;
let sig_structure =
codec::sig_structure(&protected, params.aad.signature.as_bytes(), &encrypt_bytes)
.map_err(|codec::CodecError| BuildError::Codec)?;
let signature = signer.sign(&sig_structure).await?;
codec::assemble_sign1(&protected, &encrypt_bytes, signature.as_bytes())
.map(CoseBytes::new)
.map_err(|codec::CodecError| BuildError::Codec)
}
pub async fn build_sealed<S: Signer>(
params: &SealParams<'_>,
signer: &S,
) -> Result<CoseBytes, BuildError> {
let ephemeral = random_array::<32>()?;
let nonce = random_array::<NONCE_LEN>()?;
build_sealed_inner(params, signer, &ephemeral, *nonce).await
}
#[cfg(feature = "fixtures")]
pub async fn build_sealed_with_parts<S: Signer>(
params: &SealParams<'_>,
signer: &S,
parts: &SealParts,
) -> Result<CoseBytes, BuildError> {
build_sealed_inner(params, signer, &parts.ephemeral_private, parts.nonce).await
}
#[derive(Debug, Clone)]
pub struct VerifySealedParams<'a> {
pub signature_aad: ExternalAad,
pub validation: &'a ValidationParams,
}
#[derive(Debug, Clone)]
pub struct VerifiedSealed {
pub claims: Claims,
pub content_type: ContentType,
pub signer_key_id: KeyId,
pub recipient_key_id: KeyId,
pub content_algorithm: ContentAlgorithm,
pub parties: KdfParties,
encrypt_bytes: Vec<u8>,
}
pub async fn verify_sealed<V: Verifier>(
bytes: &[u8],
verifier: &V,
params: &VerifySealedParams<'_>,
) -> Result<VerifiedSealed, VerifyError> {
let outer = codec::decode_sign1_strict(bytes, Sign1Layer::SealedOuter)?;
let embedded = codec::decode_encrypt_strict(&outer.payload, ClaimsExpectation::Required)
.map_err(|e| match e {
DecodeError::NotTagged | DecodeError::WrongTag { .. } => {
VerifyError::Decode(DecodeError::EmbeddedNotEncrypt)
}
other => VerifyError::Decode(other),
})?;
let sig_structure = codec::sig_structure(
&outer.protected,
params.signature_aad.as_bytes(),
&outer.payload,
)
.map_err(|codec::CodecError| VerifyError::Decode(DecodeError::Malformed))?;
let signature = crate::types::Signature::from_bytes(outer.signature.clone())
.map_err(|_| VerifyError::SignatureInvalid)?;
verifier
.verify(
&outer.kid,
outer.algorithm,
&outer.protected_headers,
&sig_structure,
&signature,
)
.await?;
let claims = embedded
.claims
.ok_or(VerifyError::Decode(DecodeError::MissingHeader {
label: crate::label::HDR_CWT_CLAIMS,
}))?;
if claims.sender_key_id.as_ref() != Some(&outer.kid) {
return Err(VerifyError::SenderKeyMismatch);
}
claims.validate(params.validation)?;
Ok(VerifiedSealed {
claims,
content_type: embedded.content_type,
signer_key_id: outer.kid,
recipient_key_id: embedded.recipient_kid,
content_algorithm: embedded.content_algorithm,
parties: embedded.parties,
encrypt_bytes: outer.payload,
})
}
impl VerifiedSealed {
pub async fn open<R: Recipient>(
&self,
recipient: &R,
aad: &ExternalAad,
expected_parties: Option<&KdfParties>,
) -> Result<Opened, OpenError> {
if recipient.key_id() != &self.recipient_key_id {
return Err(OpenError::RecipientKeyMismatch);
}
if let Some(expected) = expected_parties
&& *expected != self.parties
{
return Err(OpenError::PartyMismatch);
}
let request = OpenRequest {
cose_encrypt: &self.encrypt_bytes,
external_aad: aad,
expected_parties,
};
let plaintext = recipient.open(&request).await?;
Ok(Opened {
plaintext,
content_type: self.content_type.clone(),
})
}
#[must_use]
pub fn encrypt_bytes(&self) -> &[u8] {
&self.encrypt_bytes
}
}