use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ByteRange {
ranges: Vec<(u64, u64)>,
}
impl ByteRange {
pub fn new(ranges: Vec<(u64, u64)>) -> Self {
Self { ranges }
}
pub fn from_array(values: &[i64]) -> Result<Self, String> {
if values.len() % 2 != 0 {
return Err("ByteRange array must have even number of elements".to_string());
}
if values.len() < 4 {
return Err("ByteRange array must have at least 4 elements".to_string());
}
let mut ranges = Vec::with_capacity(values.len() / 2);
for chunk in values.chunks(2) {
let offset = chunk[0];
let length = chunk[1];
if offset < 0 {
return Err(format!("ByteRange offset cannot be negative: {}", offset));
}
if length < 0 {
return Err(format!("ByteRange length cannot be negative: {}", length));
}
ranges.push((offset as u64, length as u64));
}
Ok(Self { ranges })
}
pub fn ranges(&self) -> &[(u64, u64)] {
&self.ranges
}
pub fn len(&self) -> usize {
self.ranges.len()
}
pub fn is_empty(&self) -> bool {
self.ranges.is_empty()
}
pub fn total_bytes(&self) -> u64 {
self.ranges.iter().map(|(_, len)| len).sum()
}
pub fn validate(&self) -> Result<(), String> {
if self.ranges.len() != 2 {
return Err(format!(
"Expected 2 ranges for signature, got {}",
self.ranges.len()
));
}
let (offset1, _len1) = self.ranges[0];
if offset1 != 0 {
return Err(format!(
"First range should start at offset 0, got {}",
offset1
));
}
let (offset1, len1) = self.ranges[0];
let (offset2, _len2) = self.ranges[1];
if offset2 < offset1 + len1 {
return Err("ByteRange ranges overlap".to_string());
}
Ok(())
}
}
impl fmt::Display for ByteRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "[")?;
for (i, (offset, length)) in self.ranges.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{} {}", offset, length)?;
}
write!(f, "]")
}
}
#[derive(Debug, Clone)]
pub struct SignatureField {
pub name: Option<String>,
pub byte_range: ByteRange,
pub contents: Vec<u8>,
pub filter: String,
pub sub_filter: Option<String>,
pub reason: Option<String>,
pub location: Option<String>,
pub contact_info: Option<String>,
pub signing_time: Option<String>,
}
impl SignatureField {
pub fn new(filter: String, byte_range: ByteRange, contents: Vec<u8>) -> Self {
Self {
name: None,
byte_range,
contents,
filter,
sub_filter: None,
reason: None,
location: None,
contact_info: None,
signing_time: None,
}
}
pub fn is_pades(&self) -> bool {
self.sub_filter
.as_ref()
.map(|sf| sf.contains("CAdES") || sf.contains("cades"))
.unwrap_or(false)
}
pub fn is_pkcs7_detached(&self) -> bool {
self.sub_filter
.as_ref()
.map(|sf| sf.contains("pkcs7.detached"))
.unwrap_or(false)
}
pub fn contents_size(&self) -> usize {
self.contents.len()
}
}
impl fmt::Display for SignatureField {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"SignatureField {{ name: {:?}, filter: {}, sub_filter: {:?}, contents: {} bytes }}",
self.name,
self.filter,
self.sub_filter,
self.contents.len()
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byterange_from_array_valid() {
let values = vec![0, 1000, 2000, 500];
let br = ByteRange::from_array(&values).unwrap();
assert_eq!(br.len(), 2);
assert_eq!(br.ranges()[0], (0, 1000));
assert_eq!(br.ranges()[1], (2000, 500));
}
#[test]
fn test_byterange_from_array_odd_elements() {
let values = vec![0, 1000, 2000];
let result = ByteRange::from_array(&values);
assert!(result.is_err());
assert!(result.unwrap_err().contains("even"));
}
#[test]
fn test_byterange_from_array_too_few_elements() {
let values = vec![0, 1000];
let result = ByteRange::from_array(&values);
assert!(result.is_err());
assert!(result.unwrap_err().contains("at least 4"));
}
#[test]
fn test_byterange_from_array_negative_offset() {
let values = vec![-1, 1000, 2000, 500];
let result = ByteRange::from_array(&values);
assert!(result.is_err());
assert!(result.unwrap_err().contains("negative"));
}
#[test]
fn test_byterange_from_array_negative_length() {
let values = vec![0, -100, 2000, 500];
let result = ByteRange::from_array(&values);
assert!(result.is_err());
assert!(result.unwrap_err().contains("negative"));
}
#[test]
fn test_byterange_total_bytes() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
assert_eq!(br.total_bytes(), 1500);
}
#[test]
fn test_byterange_validate_valid() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
assert!(br.validate().is_ok());
}
#[test]
fn test_byterange_validate_wrong_range_count() {
let br = ByteRange::new(vec![(0, 1000)]);
let result = br.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("2 ranges"));
}
#[test]
fn test_byterange_validate_first_not_zero() {
let br = ByteRange::new(vec![(100, 1000), (2000, 500)]);
let result = br.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("offset 0"));
}
#[test]
fn test_byterange_validate_overlapping() {
let br = ByteRange::new(vec![(0, 1000), (500, 500)]);
let result = br.validate();
assert!(result.is_err());
assert!(result.unwrap_err().contains("overlap"));
}
#[test]
fn test_byterange_display() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
assert_eq!(br.to_string(), "[0 1000 2000 500]");
}
#[test]
fn test_byterange_is_empty() {
let empty = ByteRange::new(vec![]);
assert!(empty.is_empty());
let non_empty = ByteRange::new(vec![(0, 100)]);
assert!(!non_empty.is_empty());
}
#[test]
fn test_signature_field_new() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let contents = vec![0x30, 0x82]; let sig = SignatureField::new("Adobe.PPKLite".to_string(), br, contents);
assert_eq!(sig.filter, "Adobe.PPKLite");
assert!(sig.name.is_none());
assert!(sig.sub_filter.is_none());
}
#[test]
fn test_signature_field_is_pades() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let mut sig = SignatureField::new("Adobe.PPKLite".to_string(), br, vec![]);
assert!(!sig.is_pades());
sig.sub_filter = Some("ETSI.CAdES.detached".to_string());
assert!(sig.is_pades());
}
#[test]
fn test_signature_field_is_pkcs7_detached() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let mut sig = SignatureField::new("Adobe.PPKLite".to_string(), br, vec![]);
assert!(!sig.is_pkcs7_detached());
sig.sub_filter = Some("adbe.pkcs7.detached".to_string());
assert!(sig.is_pkcs7_detached());
}
#[test]
fn test_signature_field_contents_size() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let contents = vec![0u8; 4096];
let sig = SignatureField::new("Adobe.PPKLite".to_string(), br, contents);
assert_eq!(sig.contents_size(), 4096);
}
#[test]
fn test_signature_field_display() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let mut sig = SignatureField::new("Adobe.PPKLite".to_string(), br, vec![0u8; 100]);
sig.name = Some("Signature1".to_string());
sig.sub_filter = Some("adbe.pkcs7.detached".to_string());
let display = sig.to_string();
assert!(display.contains("Signature1"));
assert!(display.contains("Adobe.PPKLite"));
assert!(display.contains("100 bytes"));
}
#[test]
fn test_signature_field_clone() {
let br = ByteRange::new(vec![(0, 1000), (2000, 500)]);
let sig = SignatureField::new("Adobe.PPKLite".to_string(), br, vec![1, 2, 3]);
let cloned = sig.clone();
assert_eq!(sig.filter, cloned.filter);
assert_eq!(sig.contents, cloned.contents);
}
}