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;
#[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
}
}
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 mut data_to_sign: Vec<u8> = Vec::new();
data_to_sign.extend_from_slice(&self.payload_type.clone().into_bytes());
data_to_sign.extend_from_slice(&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_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]);
}
}