use crate::error::{Error, Result};
use crate::signing;
use crate::signing::signable::Signable;
use atlas_c2pa_lib::cose::HashAlgorithm;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
pub fn pae(payload_type: &str, payload: &[u8]) -> Vec<u8> {
let mut result = Vec::new();
result.extend_from_slice(b"DSSEv1 ");
result.extend_from_slice(format!("{} ", payload_type.len()).as_bytes());
result.extend_from_slice(payload_type.as_bytes());
result.push(b' ');
result.extend_from_slice(format!("{} ", payload.len()).as_bytes());
result.extend_from_slice(payload);
result
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Signature {
#[serde_as(as = "serde_with::base64::Base64")]
sig: Vec<u8>,
keyid: String,
}
impl Signature {
pub fn new(sig: Vec<u8>, keyid: String) -> Self {
Self {
sig: sig,
keyid: keyid,
}
}
pub fn sig(&self) -> &[u8] {
&self.sig
}
pub fn keyid(&self) -> &str {
&self.keyid
}
}
#[serde_as]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Envelope {
#[serde_as(as = "serde_with::base64::Base64")]
payload: Vec<u8>,
payload_type: String,
signatures: Vec<Signature>,
}
impl Envelope {
pub fn new(payload: &Vec<u8>, payload_type: String) -> Self {
Self {
payload: payload.to_vec(),
payload_type: payload_type,
signatures: vec![],
}
}
pub fn add_signature(&mut self, sig: Vec<u8>, keyid: String) -> Result<()> {
if sig.is_empty() {
return Err(Error::Signing("DSSE signature cannot be empty".to_string()));
}
let sig_struct = Signature::new(sig, keyid);
self.signatures.push(sig_struct);
Ok(())
}
pub fn validate(&self) -> bool {
if self.payload.is_empty() || self.payload_type.is_empty() || self.signatures.is_empty() {
return false;
}
for signature in &self.signatures {
if signature.sig.is_empty() {
return false;
}
}
true
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn payload_type(&self) -> &str {
&self.payload_type
}
pub fn signatures(&self) -> &[Signature] {
&self.signatures
}
pub fn to_sigstore_dsse(&self) -> sigstore_types::DsseEnvelope {
sigstore_types::DsseEnvelope::new(
self.payload_type.clone(),
sigstore_types::PayloadBytes::from_bytes(&self.payload),
self.signatures
.iter()
.map(|s| sigstore_types::DsseSignature {
sig: sigstore_types::SignatureBytes::from_bytes(s.sig()),
keyid: sigstore_types::KeyId::new(s.keyid().to_string()),
})
.collect(),
)
}
}
impl Signable for Envelope {
fn sign(&mut self, key_path: PathBuf, hash_alg: HashAlgorithm) -> Result<()> {
let private_key = signing::load_private_key(&key_path)?;
let data_to_sign = pae(&self.payload_type, &self.payload);
let signature = signing::sign_data_with_algorithm(&data_to_sign, &private_key, &hash_alg)?;
self.add_signature(signature, "".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use base64::Engine;
use base64::prelude::BASE64_STANDARD;
use serde_json::{from_slice, from_str, json, to_string, to_vec};
#[test]
fn test_pae_spec_vector() {
let result = pae("application/example", b"hello world");
assert_eq!(result, b"DSSEv1 19 application/example 11 hello world");
}
#[test]
fn test_pae_empty_payload() {
let result = pae("application/json", b"");
assert_eq!(result, b"DSSEv1 16 application/json 0 ");
}
#[test]
fn test_to_sigstore_dsse() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test/type".to_string());
envelope
.add_signature(vec![0xab, 0xcd], "key1".to_string())
.unwrap();
let sigstore_dsse = envelope.to_sigstore_dsse();
assert_eq!(sigstore_dsse.payload_type, "test/type");
assert_eq!(sigstore_dsse.payload.as_bytes(), &[1, 2, 3]);
assert_eq!(sigstore_dsse.signatures.len(), 1);
assert_eq!(sigstore_dsse.signatures[0].sig.as_bytes(), &[0xab, 0xcd]);
}
#[test]
fn test_signature_new() {
let sig_bytes = vec![0xde, 0xad, 0xbe, 0xef];
let keyid = "test-key".to_string();
let signature = Signature::new(sig_bytes.clone(), keyid.clone());
assert_eq!(signature.sig(), &sig_bytes);
assert_eq!(signature.keyid(), &keyid);
}
#[test]
fn test_signature_empty_keyid() {
let sig_bytes = vec![0x12, 0x34];
let signature = Signature::new(sig_bytes.clone(), "".to_string());
assert_eq!(signature.sig(), &sig_bytes);
assert_eq!(signature.keyid(), "");
}
#[test]
fn test_signature_getters() {
let sig_bytes = vec![0xab, 0xcd, 0xef, 0x01, 0x23];
let keyid = "signing-key-2024".to_string();
let signature = Signature::new(sig_bytes.clone(), keyid.clone());
assert_eq!(signature.sig().len(), 5);
assert_eq!(signature.sig()[0], 0xab);
assert_eq!(signature.keyid().len(), 16);
assert!(signature.keyid().contains("2024"));
}
#[test]
fn test_envelope_new() {
let payload = b"test payload".to_vec();
let payload_type = "text/plain".to_string();
let envelope = Envelope::new(&payload, payload_type.clone());
assert_eq!(envelope.payload(), &payload);
assert_eq!(envelope.payload_type(), &payload_type);
assert!(envelope.signatures().is_empty());
}
#[test]
fn test_envelope_add_signature_success() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
let sig_bytes = vec![0xab, 0xcd, 0xef];
let keyid = "key-1".to_string();
let result = envelope.add_signature(sig_bytes.clone(), keyid.clone());
assert!(result.is_ok());
assert_eq!(envelope.signatures().len(), 1);
assert_eq!(envelope.signatures()[0].sig(), &sig_bytes);
assert_eq!(envelope.signatures()[0].keyid(), &keyid);
}
#[test]
fn test_envelope_add_signature_empty_fails() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
let result = envelope.add_signature(vec![], "key-1".to_string());
assert!(result.is_err());
if let Err(Error::Signing(msg)) = result {
assert_eq!(msg, "DSSE signature cannot be empty");
}
assert_eq!(envelope.signatures().len(), 0);
}
#[test]
fn test_envelope_add_multiple_signatures() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
envelope
.add_signature(vec![0x01, 0x02], "key-1".to_string())
.unwrap();
envelope
.add_signature(vec![0x03, 0x04], "key-2".to_string())
.unwrap();
envelope
.add_signature(vec![0x05, 0x06], "".to_string())
.unwrap();
assert_eq!(envelope.signatures().len(), 3);
assert_eq!(envelope.signatures()[0].keyid(), "key-1");
assert_eq!(envelope.signatures()[1].keyid(), "key-2");
assert_eq!(envelope.signatures()[2].keyid(), "");
}
#[test]
fn test_envelope_validate_valid() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
envelope
.add_signature(vec![0xab, 0xcd], "key".to_string())
.unwrap();
assert!(envelope.validate());
}
#[test]
fn test_envelope_validate_no_signatures() {
let envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
assert!(!envelope.validate());
}
#[test]
fn test_envelope_validate_empty_payload() {
let mut envelope = Envelope::new(&vec![], "test".to_string());
envelope
.add_signature(vec![0xab, 0xcd], "key".to_string())
.unwrap();
assert!(!envelope.validate());
}
#[test]
fn test_envelope_validate_empty_payload_type() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "".to_string());
envelope
.add_signature(vec![0xab, 0xcd], "key".to_string())
.unwrap();
assert!(!envelope.validate());
}
#[test]
fn test_envelope_validate_empty_signature_bytes() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
let empty_sig = Signature::new(vec![], "key".to_string());
envelope.signatures.push(empty_sig);
assert!(!envelope.validate());
}
#[test]
fn test_envelope_validate_mixed_signatures() {
let mut envelope = Envelope::new(&vec![1, 2, 3], "test".to_string());
envelope
.add_signature(vec![0xab, 0xcd], "key1".to_string())
.unwrap();
let empty_sig = Signature::new(vec![], "key2".to_string());
envelope.signatures.push(empty_sig);
assert!(!envelope.validate()); }
#[test]
fn test_envelope_getters() {
let payload = b"Hello, DSSE world!".to_vec();
let payload_type = "text/plain".to_string();
let mut envelope = Envelope::new(&payload, payload_type.clone());
assert_eq!(envelope.payload(), &payload);
assert_eq!(envelope.payload_type(), &payload_type);
assert_eq!(envelope.signatures().len(), 0);
envelope
.add_signature(vec![0x01, 0x02, 0x03], "key1".to_string())
.unwrap();
envelope
.add_signature(vec![0x04, 0x05, 0x06], "key2".to_string())
.unwrap();
assert_eq!(envelope.signatures().len(), 2);
assert_eq!(envelope.signatures()[0].keyid(), "key1");
assert_eq!(envelope.signatures()[1].keyid(), "key2");
}
#[test]
fn test_envelope_json_payload_type() {
let json_payload = json!({"field1": "hello", "field2": "world"});
let envelope = Envelope::new(
&to_vec(&json_payload).unwrap(),
"application/json".to_string(),
);
let deserialized_payload: serde_json::Value = from_slice(envelope.payload()).unwrap();
assert_eq!(envelope.payload_type(), "application/json");
assert_eq!(deserialized_payload["field1"], "hello");
}
#[test]
fn test_signature_serialization_fields() {
let sig_bytes = vec![0xde, 0xad, 0xbe, 0xef];
let keyid = "test-key-id".to_string();
let signature = Signature::new(sig_bytes.clone(), keyid.clone());
assert_eq!(signature.sig().len(), 4);
assert_eq!(signature.keyid().len(), 11);
assert_eq!(signature.sig(), &sig_bytes);
assert_eq!(signature.keyid(), &keyid);
}
#[test]
fn test_envelope_large_payload() {
let large_payload = vec![0x42; 10000]; let envelope = Envelope::new(&large_payload, "application/test".to_string());
assert_eq!(envelope.payload().len(), 10000);
assert_eq!(envelope.payload()[0], 0x42);
assert_eq!(envelope.payload()[9999], 0x42);
assert_eq!(envelope.payload_type(), "application/test");
}
#[test]
fn test_envelope_json_serialization() {
let payload = json!({"field1": "hello", "field2": "world"});
let mut envelope =
Envelope::new(&to_vec(&payload).unwrap(), "application/json".to_string());
envelope
.add_signature(vec![0x01, 0x02, 0x03], "key1".to_string())
.unwrap();
let json_str = to_string(&envelope).unwrap();
BASE64_STANDARD
.decode(
&from_str::<serde_json::Value>(&json_str).unwrap()["payload"]
.as_str()
.unwrap(),
)
.unwrap();
let deserialized: Envelope = serde_json::from_str(&json_str).unwrap();
let deserialized_payload: serde_json::Value = from_slice(deserialized.payload()).unwrap();
assert_eq!(deserialized_payload["field1"], "hello");
assert_eq!(deserialized.payload_type(), "application/json");
assert_eq!(deserialized.signatures().len(), 1);
assert_eq!(deserialized.signatures()[0].keyid(), "key1");
assert_eq!(deserialized.signatures()[0].sig(), &[0x01, 0x02, 0x03]);
}
}