use cbor2::Cbor;
use crate::{
header::{decode_protected, encode_protected},
iana, tag, util, Error, Header, Signer, Value, Verifier,
};
#[derive(Clone, Debug, PartialEq, Cbor)]
struct SignatureWire(
#[serde(with = "serde_bytes")] Vec<u8>,
Header,
#[serde(with = "serde_bytes")] Vec<u8>,
);
#[derive(Clone, Debug, PartialEq, Cbor)]
struct SignWire(
#[serde(with = "serde_bytes")] Vec<u8>,
Header,
#[serde(with = "serde_bytes")] Option<Vec<u8>>,
Vec<SignatureWire>,
);
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Signature {
pub protected: Header,
pub unprotected: Header,
signature: Vec<u8>,
protected_raw: Vec<u8>,
}
impl Signature {
fn kid(&self) -> &[u8] {
match self.unprotected.get_bytes(iana::HeaderParameterKid) {
Ok(Some(kid)) => kid,
_ => &[],
}
}
pub fn signature(&self) -> &[u8] {
&self.signature
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct SignMessage {
pub protected: Header,
pub unprotected: Header,
pub payload: Option<Vec<u8>>,
pub signatures: Vec<Signature>,
protected_raw: Vec<u8>,
signed: bool,
}
impl SignMessage {
pub fn new(payload: Option<Vec<u8>>) -> Self {
SignMessage {
payload,
..Default::default()
}
}
fn to_be_signed(
body_protected: &[u8],
sign_protected: &[u8],
external_aad: &[u8],
payload: &Option<Vec<u8>>,
) -> Vec<u8> {
util::encode_structure(vec![
Value::from("Signature"),
Value::Bytes(body_protected.to_vec()),
Value::Bytes(sign_protected.to_vec()),
Value::Bytes(external_aad.to_vec()),
util::payload_value(payload),
])
}
pub fn sign(
&mut self,
signers: &[&dyn Signer],
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
if signers.is_empty() {
return Err(Error::Custom(
"SignMessage requires at least one signer".into(),
));
}
let aad = external_aad.unwrap_or(&[]);
self.protected_raw = encode_protected(&self.protected);
self.signatures.clear();
for signer in signers {
let mut protected = Header::new();
if signer.alg() != iana::AlgorithmReserved {
protected.insert(iana::HeaderParameterAlg, signer.alg());
}
let mut unprotected = Header::new();
util::ensure_unprotected_kid(&mut unprotected, signer.kid());
let protected_raw = encode_protected(&protected);
let tbs = Self::to_be_signed(&self.protected_raw, &protected_raw, aad, &self.payload);
let signature = signer.sign(&tbs)?;
self.signatures.push(Signature {
protected,
unprotected,
signature,
protected_raw,
});
}
self.signed = true;
Ok(())
}
pub fn sign_and_encode(
&mut self,
signers: &[&dyn Signer],
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
self.sign(signers, external_aad)?;
self.to_vec()
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
if !self.signed {
return Err(Error::Custom(
"SignMessage must be signed before encoding".into(),
));
}
if self.signatures.is_empty() {
return Err(Error::Custom("SignMessage has no signatures".into()));
}
let signatures = self
.signatures
.iter()
.map(|s| {
SignatureWire(
s.protected_raw.clone(),
s.unprotected.clone(),
s.signature.clone(),
)
})
.collect();
let wire = SignWire(
self.protected_raw.clone(),
self.unprotected.clone(),
self.payload.clone(),
signatures,
);
Ok(tag::with_tag(
tag::SIGN_PREFIX,
&cbor2::to_canonical_vec(&wire)?,
))
}
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
let body = tag::untag(data, tag::SIGN_PREFIX);
let wire: SignWire = cbor2::from_slice(body)?;
if wire.3.is_empty() {
return Err(Error::Custom("SignMessage has no signatures".into()));
}
let protected = decode_protected(&wire.0)?;
let mut signatures = Vec::with_capacity(wire.3.len());
for sw in wire.3 {
let sig_protected = decode_protected(&sw.0)?;
signatures.push(Signature {
protected: sig_protected,
unprotected: sw.1,
signature: sw.2,
protected_raw: sw.0,
});
}
Ok(SignMessage {
protected,
unprotected: wire.1,
payload: wire.2,
signatures,
protected_raw: wire.0,
signed: true,
})
}
pub fn verify(
&self,
verifiers: &[&dyn Verifier],
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
if !self.signed {
return Err(Error::Custom(
"SignMessage must be decoded before verifying".into(),
));
}
if verifiers.is_empty() {
return Err(Error::Custom(
"SignMessage requires at least one verifier".into(),
));
}
if self.signatures.is_empty() {
return Err(Error::Custom("SignMessage has no signatures".into()));
}
let aad = external_aad.unwrap_or(&[]);
for sig in &self.signatures {
let kid = sig.kid();
let verifier = verifiers
.iter()
.find(|v| v.kid() == kid)
.ok_or_else(|| Error::verify("no verifier for signature kid"))?;
util::check_protected_alg(&sig.protected, verifier.alg())?;
let tbs =
Self::to_be_signed(&self.protected_raw, &sig.protected_raw, aad, &self.payload);
verifier.verify(&tbs, &sig.signature)?;
}
Ok(())
}
pub fn verify_and_decode(
verifiers: &[&dyn Verifier],
data: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Self, Error> {
let msg = Self::from_slice(data)?;
msg.verify(verifiers, external_aad)?;
Ok(msg)
}
pub const TAG: u64 = iana::CBORTagCOSESign;
}