use cbor2::Cbor;
use crate::{
header::{decode_protected, encode_protected, validate_header_buckets},
iana, tag, util, Error, Header, Label, Signer, Value, Verifier,
};
#[derive(Clone, Debug, PartialEq, Cbor)]
#[cbor(tag = 18, array)]
struct Sign1Wire {
#[serde(with = "serde_bytes")]
protected: Vec<u8>,
unprotected: Header,
#[serde(with = "serde_bytes")]
payload: Option<Vec<u8>>,
#[serde(with = "serde_bytes")]
signature: Vec<u8>,
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Sign1Message {
pub protected: Header,
pub unprotected: Header,
pub payload: Option<Vec<u8>>,
signature: Vec<u8>,
protected_raw: Vec<u8>,
signed: bool,
}
impl Sign1Message {
pub fn new(payload: Option<Vec<u8>>) -> Self {
Sign1Message {
payload,
..Default::default()
}
}
pub fn to_be_signed(
protected_raw: &[u8],
external_aad: &[u8],
payload: &[u8],
) -> Result<Vec<u8>, Error> {
util::encode_structure(vec![
Value::from("Signature1"),
Value::Bytes(protected_raw.to_vec()),
Value::Bytes(external_aad.to_vec()),
util::payload_value(payload),
])
}
pub fn prepare_signature(
&mut self,
alg: Option<Label>,
kid: Option<&[u8]>,
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
let payload =
util::require_embedded_payload(&self.payload, "Sign1Message::prepare_signature")?
.to_vec();
self.prepare_signature_payload(alg, kid, &payload, external_aad.unwrap_or(&[]))
}
pub fn prepare_detached_signature(
&mut self,
alg: Option<Label>,
kid: Option<&[u8]>,
detached_payload: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
let tbs = self.prepare_signature_payload(
alg,
kid,
detached_payload,
external_aad.unwrap_or(&[]),
)?;
self.payload = None;
Ok(tbs)
}
fn prepare_signature_payload(
&mut self,
alg: Option<Label>,
kid: Option<&[u8]>,
payload: &[u8],
external_aad: &[u8],
) -> Result<Vec<u8>, Error> {
util::ensure_protected_alg(&mut self.protected, alg)?;
util::ensure_unprotected_kid(&mut self.unprotected, kid);
validate_header_buckets(&self.protected, &self.unprotected)?;
let protected_raw = encode_protected(&self.protected)?;
let tbs = Self::to_be_signed(&protected_raw, external_aad, payload)?;
self.protected_raw = protected_raw;
self.signature.clear();
self.signed = false;
Ok(tbs)
}
pub fn set_signature(&mut self, signature: impl Into<Vec<u8>>) -> Result<(), Error> {
validate_header_buckets(&self.protected, &self.unprotected)?;
if self.protected_raw.is_empty() && !self.protected.is_empty() {
self.protected_raw = encode_protected(&self.protected)?;
}
self.signature = signature.into();
self.signed = true;
Ok(())
}
pub fn sign(&mut self, signer: &dyn Signer, external_aad: Option<&[u8]>) -> Result<(), Error> {
let payload = util::require_embedded_payload(&self.payload, "Sign1Message::sign")?.to_vec();
self.sign_payload(signer, &payload, external_aad.unwrap_or(&[]))
}
pub fn sign_detached(
&mut self,
signer: &dyn Signer,
detached_payload: &[u8],
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
self.sign_payload(signer, detached_payload, external_aad.unwrap_or(&[]))?;
self.payload = None;
Ok(())
}
fn sign_payload(
&mut self,
signer: &dyn Signer,
payload: &[u8],
external_aad: &[u8],
) -> Result<(), Error> {
let tbs =
self.prepare_signature_payload(signer.alg(), signer.kid(), payload, external_aad)?;
let signature = signer.sign(&tbs)?;
self.set_signature(signature)
}
pub fn sign_and_encode(
&mut self,
signer: &dyn Signer,
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
self.sign(signer, external_aad)?;
self.to_vec()
}
pub fn sign_detached_and_encode(
&mut self,
signer: &dyn Signer,
detached_payload: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Vec<u8>, Error> {
self.sign_detached(signer, detached_payload, external_aad)?;
self.to_vec()
}
pub fn to_vec(&self) -> Result<Vec<u8>, Error> {
if !self.signed {
return Err(Error::Custom(
"Sign1Message must be signed before encoding".into(),
));
}
validate_header_buckets(&self.protected, &self.unprotected)?;
let wire = Sign1Wire {
protected: self.protected_raw.clone(),
unprotected: self.unprotected.clone(),
payload: self.payload.clone(),
signature: self.signature.clone(),
};
Ok(cbor2::to_canonical_vec(&wire)?)
}
pub fn from_slice(data: &[u8]) -> Result<Self, Error> {
let body = tag::strip_message_wrappers(data);
if !body.starts_with(tag::SIGN1_PREFIX) && tag::starts_with_cbor_tag(body) {
return Err(Error::Custom("unexpected CBOR tag for COSE_Sign1".into()));
}
let wire: Sign1Wire = cbor2::from_slice(body)?;
let protected = decode_protected(&wire.protected)?;
validate_header_buckets(&protected, &wire.unprotected)?;
Ok(Sign1Message {
protected,
unprotected: wire.unprotected,
payload: wire.payload,
signature: wire.signature,
protected_raw: wire.protected,
signed: true,
})
}
pub fn verify(
&self,
verifier: &dyn Verifier,
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
if !self.signed {
return Err(Error::Custom(
"Sign1Message must be decoded before verifying".into(),
));
}
let payload = util::require_embedded_payload(&self.payload, "Sign1Message::verify")?;
self.verify_payload(verifier, payload, external_aad.unwrap_or(&[]))
}
pub fn verify_detached(
&self,
verifier: &dyn Verifier,
detached_payload: &[u8],
external_aad: Option<&[u8]>,
) -> Result<(), Error> {
if !self.signed {
return Err(Error::Custom(
"Sign1Message must be decoded before verifying".into(),
));
}
if self.payload.is_some() {
return Err(Error::Custom(
"Sign1Message carries an embedded payload; use verify".into(),
));
}
self.verify_payload(verifier, detached_payload, external_aad.unwrap_or(&[]))
}
fn verify_payload(
&self,
verifier: &dyn Verifier,
payload: &[u8],
external_aad: &[u8],
) -> Result<(), Error> {
util::check_protected_alg(&self.protected, verifier.alg())?;
let tbs = Self::to_be_signed(&self.protected_raw, external_aad, payload)?;
verifier.verify(&tbs, &self.signature)
}
pub fn verify_and_decode(
verifier: &dyn Verifier,
data: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Self, Error> {
let msg = Self::from_slice(data)?;
msg.verify(verifier, external_aad)?;
Ok(msg)
}
pub fn verify_detached_and_decode(
verifier: &dyn Verifier,
data: &[u8],
detached_payload: &[u8],
external_aad: Option<&[u8]>,
) -> Result<Self, Error> {
let msg = Self::from_slice(data)?;
msg.verify_detached(verifier, detached_payload, external_aad)?;
Ok(msg)
}
pub fn signature(&self) -> &[u8] {
&self.signature
}
pub fn protected_raw(&self) -> &[u8] {
&self.protected_raw
}
pub const TAG: u64 = iana::CBORTagCOSESign1;
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_cbor_shape<T: cbor2::Cbor>(tag: Option<u64>, array: bool) {
assert_eq!(T::TAG, tag);
assert_eq!(T::ARRAY, array);
}
#[test]
fn wire_metadata_declares_tagged_array_shape() {
assert_cbor_shape::<Sign1Wire>(Some(iana::CBORTagCOSESign1), true);
}
}