use std::fmt::Write as fmtWrite;
use std::fs;
use std::path::Path;
use ct_codecs::{Base64, Decoder, Encoder};
use crate::constants::*;
use crate::errors::*;
use crate::signature::*;
#[derive(Clone)]
pub struct SignatureBox {
pub(crate) untrusted_comment: String,
pub(crate) signature: Signature,
pub(crate) sig_and_trusted_comment: Option<Vec<u8>>,
pub(crate) global_sig: Option<Vec<u8>>,
pub(crate) is_prehashed: bool,
}
impl From<SignatureBox> for String {
fn from(sig_box: SignatureBox) -> String {
sig_box.into_string()
}
}
impl From<String> for SignatureBox {
fn from(s: String) -> SignatureBox {
SignatureBox::from_string(&s).unwrap()
}
}
impl SignatureBox {
pub fn is_prehashed(&self) -> bool {
self.is_prehashed
}
pub fn untrusted_comment(&self) -> Result<String> {
Ok(self.untrusted_comment.clone())
}
pub fn trusted_comment(&self) -> Result<String> {
let sig_and_trusted_comment = match &self.sig_and_trusted_comment {
None => {
return Err(PError::new(
ErrorKind::Misc,
"trusted comment is not present",
))
}
Some(sig_and_trusted_comment) => sig_and_trusted_comment,
};
if sig_and_trusted_comment.len() < SIGNATURE_BYTES {
return Err(PError::new(
ErrorKind::Encoding,
"invalid trusted comment encoding",
));
}
let just_comment = String::from_utf8(sig_and_trusted_comment[SIGNATURE_BYTES..].to_vec())?;
Ok(just_comment)
}
pub fn keynum(&self) -> &[u8] {
&self.signature.keynum[..]
}
pub fn from_string(s: &str) -> Result<SignatureBox> {
let mut lines = s.lines();
let untrusted_comment = lines
.next()
.ok_or_else(|| PError::new(ErrorKind::Io, "Missing untrusted comment"))?
.to_string();
let signature_str = lines
.next()
.ok_or_else(|| PError::new(ErrorKind::Io, "Missing signature"))?
.to_string();
let mut trusted_comment_str = lines
.next()
.ok_or_else(|| PError::new(ErrorKind::Io, "Missing trusted comment"))?
.to_string();
let global_sig = lines
.next()
.ok_or_else(|| PError::new(ErrorKind::Io, "Missing global signature"))?
.to_string();
if !untrusted_comment.starts_with(COMMENT_PREFIX) {
return Err(PError::new(
ErrorKind::Verify,
format!("Untrusted comment must start with: {COMMENT_PREFIX}"),
));
}
let untrusted_comment = untrusted_comment[COMMENT_PREFIX.len()..].to_string();
let sig_bytes = Base64::decode_to_vec(signature_str.trim().as_bytes(), None)
.map_err(|e| PError::new(ErrorKind::Io, e))?;
let signature = Signature::from_bytes(&sig_bytes)?;
if !trusted_comment_str.starts_with(TRUSTED_COMMENT_PREFIX) {
return Err(PError::new(
ErrorKind::Verify,
format!("Trusted comment should start with: {TRUSTED_COMMENT_PREFIX}"),
));
}
let is_prehashed = match signature.sig_alg {
SIGALG => false,
SIGALG_PREHASHED => true,
_ => {
return Err(PError::new(
ErrorKind::Verify,
"Unsupported signature algorithm".to_string(),
))
}
};
let _ = trusted_comment_str
.drain(..TRUSTED_COMMENT_PREFIX_LEN)
.count();
let mut sig_and_trusted_comment = signature.sig.to_vec();
sig_and_trusted_comment.extend_from_slice(trusted_comment_str.trim().as_bytes());
let global_sig = Base64::decode_to_vec(global_sig.trim().as_bytes(), None)
.map_err(|e| PError::new(ErrorKind::Io, e))?;
Ok(SignatureBox {
untrusted_comment,
signature,
sig_and_trusted_comment: Some(sig_and_trusted_comment),
global_sig: Some(global_sig),
is_prehashed,
})
}
#[allow(clippy::inherent_to_string)]
pub fn to_string(&self) -> String {
let mut signature_box = String::new();
writeln!(
signature_box,
"{}{}",
COMMENT_PREFIX, self.untrusted_comment
)
.unwrap();
writeln!(signature_box, "{}", self.signature).unwrap();
writeln!(
signature_box,
"{}{}",
TRUSTED_COMMENT_PREFIX,
self.trusted_comment()
.expect("Incomplete SignatureBox: trusted comment is missing")
)
.unwrap();
let global_sig = self
.global_sig
.as_ref()
.expect("Incomplete SignatureBox: global signature is missing");
writeln!(
signature_box,
"{}",
Base64::encode_to_string(&global_sig[..]).unwrap()
)
.unwrap();
signature_box
}
pub fn into_string(self) -> String {
self.to_string()
}
pub fn to_bytes(&self) -> Vec<u8> {
self.to_string().as_bytes().to_vec()
}
pub fn from_file<P>(sig_path: P) -> Result<SignatureBox>
where
P: AsRef<Path>,
{
let s = fs::read_to_string(sig_path)?;
SignatureBox::from_string(&s)
}
}