use crate::agent::CoseAgent;
use crate::algs;
use crate::common;
use crate::cose_struct;
use crate::errors::{CoseError, CoseResult, CoseResultWithRet};
use crate::headers::{CoseHeader, COUNTER_SIG};
use crate::keys;
use cbor::{decoder::DecodeError, types::Tag, types::Type, Config, Decoder, Encoder};
use std::io::Cursor;
const SIG: usize = 0;
const MAC: usize = 1;
const ENC: usize = 2;
const CONTEXTS: [&str; 3] = [
cose_struct::SIGNATURE,
cose_struct::MAC_RECIPIENT,
cose_struct::ENCRYPT_RECIPIENT,
];
const KO: [[i32; 2]; 3] = [
[keys::KEY_OPS_SIGN, keys::KEY_OPS_VERIFY],
[keys::KEY_OPS_MAC, keys::KEY_OPS_MAC_VERIFY],
[keys::KEY_OPS_ENCRYPT, keys::KEY_OPS_DECRYPT],
];
pub const ENC0_TAG: u64 = 16;
pub const MAC0_TAG: u64 = 17;
pub const SIG1_TAG: u64 = 18;
pub const ENC_TAG: u64 = 96;
pub const MAC_TAG: u64 = 97;
pub const SIG_TAG: u64 = 98;
pub const ENC0_TYPE: &str = "cose-encrypt0";
pub const MAC0_TYPE: &str = "cose-mac0";
pub const SIG1_TYPE: &str = "cose-sign1";
pub const ENC_TYPE: &str = "cose-encrypt";
pub const MAC_TYPE: &str = "cose-mac";
pub const SIG_TYPE: &str = "cose-sign";
const SIZES: [[usize; 2]; 3] = [[4, 4], [4, 5], [3, 4]];
const TAGS: [[Tag; 2]; 3] = [
[Tag::Unassigned(SIG1_TAG), Tag::Unassigned(SIG_TAG)],
[Tag::Unassigned(MAC0_TAG), Tag::Unassigned(MAC_TAG)],
[Tag::Unassigned(ENC0_TAG), Tag::Unassigned(ENC_TAG)],
];
pub struct CoseMessage {
pub header: CoseHeader,
pub payload: Vec<u8>,
secured: Vec<u8>,
pub bytes: Vec<u8>,
ph_bstr: Vec<u8>,
pub_key: Vec<u8>,
priv_key: Vec<u8>,
key_encode: bool,
key_decode: bool,
crv: Option<i32>,
base_iv: Option<Vec<u8>>,
pub agents: Vec<CoseAgent>,
context: usize,
}
impl CoseMessage {
pub fn new_sign() -> CoseMessage {
CoseMessage {
bytes: Vec::new(),
header: CoseHeader::new(),
payload: Vec::new(),
secured: Vec::new(),
ph_bstr: Vec::new(),
pub_key: Vec::new(),
priv_key: Vec::new(),
key_encode: false,
key_decode: false,
crv: None,
base_iv: None,
agents: Vec::new(),
context: SIG,
}
}
pub fn new_encrypt() -> CoseMessage {
CoseMessage {
bytes: Vec::new(),
header: CoseHeader::new(),
payload: Vec::new(),
secured: Vec::new(),
ph_bstr: Vec::new(),
pub_key: Vec::new(),
priv_key: Vec::new(),
key_encode: false,
key_decode: false,
crv: None,
base_iv: None,
agents: Vec::new(),
context: ENC,
}
}
pub fn new_mac() -> CoseMessage {
CoseMessage {
bytes: Vec::new(),
header: CoseHeader::new(),
payload: Vec::new(),
secured: Vec::new(),
ph_bstr: Vec::new(),
pub_key: Vec::new(),
priv_key: Vec::new(),
key_encode: false,
key_decode: false,
crv: None,
base_iv: None,
agents: Vec::new(),
context: MAC,
}
}
pub fn add_header(&mut self, header: CoseHeader) {
self.header = header;
}
pub fn payload(&mut self, payload: Vec<u8>) {
self.payload = payload;
}
pub fn add_agent(&mut self, agent: &mut CoseAgent) -> CoseResult {
if self.context == SIG {
agent.context = cose_struct::SIGNATURE.to_string();
if !algs::SIGNING_ALGS.contains(&agent.header.alg.ok_or(CoseError::MissingAlg())?) {
return Err(CoseError::InvalidAlg());
}
if !agent.key_ops.contains(&keys::KEY_OPS_SIGN) {
return Err(CoseError::KeyOpNotSupported());
}
self.agents.push(agent.clone());
Ok(())
} else if self.context == MAC {
agent.context = cose_struct::MAC_RECIPIENT.to_string();
self.agents.push(agent.clone());
Ok(())
} else {
agent.context = cose_struct::ENCRYPT_RECIPIENT.to_string();
if !algs::KEY_DISTRIBUTION_ALGS
.contains(&agent.header.alg.ok_or(CoseError::MissingAlg())?)
{
return Err(CoseError::InvalidAlg());
}
self.agents.push(agent.clone());
Ok(())
}
}
pub fn get_agent(&self, kid: &Vec<u8>) -> CoseResultWithRet<Vec<usize>> {
let mut keys: Vec<usize> = Vec::new();
for i in 0..self.agents.len() {
if self.agents[i]
.header
.kid
.as_ref()
.ok_or(CoseError::MissingKID())?
== kid
{
keys.push(i);
}
}
Ok(keys)
}
pub fn key(&mut self, cose_key: &keys::CoseKey) -> CoseResult {
if self.agents.len() > 0 {
return Err(CoseError::InvalidMethodForContext());
}
cose_key.verify_kty()?;
if cose_key.alg.ok_or(CoseError::MissingAlg())?
!= self.header.alg.ok_or(CoseError::MissingAlg())?
{
return Err(CoseError::AlgsDontMatch());
}
if self.context == SIG {
self.crv = cose_key.crv;
if cose_key.key_ops.contains(&keys::KEY_OPS_SIGN) {
let priv_key = cose_key.get_s_key()?;
if priv_key.len() > 0 {
self.key_encode = true;
self.priv_key = priv_key;
}
}
if cose_key.key_ops.contains(&keys::KEY_OPS_VERIFY) {
let pub_key = cose_key.get_pub_key()?;
if pub_key.len() > 0 {
self.key_decode = true;
self.pub_key = pub_key;
}
}
} else {
if self.context == ENC {
self.base_iv = cose_key.base_iv.clone();
}
let key = cose_key.get_s_key()?;
if key.len() > 0 {
if (self.context == ENC && cose_key.key_ops.contains(&keys::KEY_OPS_ENCRYPT))
|| (self.context == MAC && cose_key.key_ops.contains(&keys::KEY_OPS_MAC))
{
self.key_encode = true;
}
if (self.context == ENC && cose_key.key_ops.contains(&keys::KEY_OPS_DECRYPT))
|| (self.context == MAC && cose_key.key_ops.contains(&keys::KEY_OPS_MAC_VERIFY))
{
self.key_decode = true;
}
self.priv_key = key;
}
}
if !self.key_encode && !self.key_decode {
return Err(CoseError::KeyOpNotSupported());
}
Ok(())
}
pub fn counter_sig(
&self,
external_aad: Option<Vec<u8>>,
counter: &mut CoseAgent,
) -> CoseResult {
let to_sig;
if self.context != ENC {
to_sig = &self.payload;
} else {
to_sig = &self.secured;
}
if to_sig.len() == 0 {
if self.context == ENC {
Err(CoseError::MissingCiphertext())
} else {
Err(CoseError::MissingPayload())
}
} else {
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
counter.sign(to_sig, &aead, &self.ph_bstr)?;
Ok(())
}
}
pub fn get_to_sign(
&self,
external_aad: Option<Vec<u8>>,
counter: &mut CoseAgent,
) -> CoseResultWithRet<Vec<u8>> {
let to_sig;
if self.context != ENC {
to_sig = &self.payload;
} else {
to_sig = &self.secured;
}
if to_sig.len() == 0 {
if self.context == ENC {
Err(CoseError::MissingCiphertext())
} else {
Err(CoseError::MissingPayload())
}
} else {
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
counter.get_sign_content(to_sig, &aead, &self.ph_bstr)
}
}
pub fn get_to_verify(
&mut self,
external_aad: Option<Vec<u8>>,
counter: &usize,
) -> CoseResultWithRet<Vec<u8>> {
let to_sig;
if self.context != ENC {
to_sig = &self.payload;
} else {
to_sig = &self.secured;
}
if to_sig.len() == 0 {
if self.context == ENC {
Err(CoseError::MissingCiphertext())
} else {
Err(CoseError::MissingPayload())
}
} else {
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
self.header.counters[*counter].get_sign_content(to_sig, &aead, &self.ph_bstr)
}
}
pub fn counters_verify(&mut self, external_aad: Option<Vec<u8>>, counter: usize) -> CoseResult {
let to_sig;
if self.context != ENC {
to_sig = &self.payload;
} else {
to_sig = &self.secured;
}
if to_sig.len() == 0 {
if self.context == ENC {
Err(CoseError::MissingCiphertext())
} else {
Err(CoseError::MissingPayload())
}
} else {
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
if self.header.counters[counter].verify(to_sig, &aead, &self.ph_bstr)? {
Ok(())
} else {
Err(CoseError::InvalidCounterSignature())
}
}
}
pub fn add_counter_sig(&mut self, counter: CoseAgent) -> CoseResult {
if !algs::SIGNING_ALGS.contains(&counter.header.alg.ok_or(CoseError::MissingAlg())?) {
return Err(CoseError::InvalidAlg());
}
if counter.context != cose_struct::COUNTER_SIGNATURE {
return Err(CoseError::InvalidContext());
}
if self.header.unprotected.contains(&COUNTER_SIG) {
self.header.counters.push(counter);
Ok(())
} else {
self.header.counters.push(counter);
self.header.remove_label(COUNTER_SIG);
self.header.unprotected.push(COUNTER_SIG);
Ok(())
}
}
pub fn secure_content(&mut self, external_aad: Option<Vec<u8>>) -> CoseResult {
if self.payload.len() <= 0 {
return Err(CoseError::MissingPayload());
}
self.ph_bstr = self.header.get_protected_bstr(true)?;
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
if self.agents.len() <= 0 {
if !self.key_encode {
return Err(CoseError::KeyOpNotSupported());
}
let alg = self.header.alg.ok_or(CoseError::MissingAlg())?;
if self.context == SIG {
if !algs::SIGNING_ALGS.contains(&alg) {
Err(CoseError::InvalidAlg())
} else {
self.secured = cose_struct::gen_sig(
&self.priv_key,
&alg,
&self.crv,
&aead,
cose_struct::SIGNATURE1,
&self.ph_bstr,
&Vec::new(),
&self.payload,
)?;
Ok(())
}
} else if self.context == ENC {
if !algs::ENCRYPT_ALGS.contains(&alg) {
Err(CoseError::InvalidAlg())
} else {
let iv = match self.base_iv.clone() {
Some(v) => algs::gen_iv(
self.header
.partial_iv
.as_ref()
.ok_or(CoseError::MissingPartialIV())?,
&v,
&alg,
)?,
None => self.header.iv.clone().ok_or(CoseError::MissingIV())?,
};
self.secured = cose_struct::gen_cipher(
&self.priv_key,
&alg,
&iv,
&aead,
cose_struct::ENCRYPT0,
&self.ph_bstr,
&self.payload,
)?;
Ok(())
}
} else {
if !algs::MAC_ALGS.contains(&alg) {
Err(CoseError::InvalidAlg())
} else {
self.secured = cose_struct::gen_mac(
&self.priv_key,
&alg,
&aead,
cose_struct::MAC0,
&self.ph_bstr,
&self.payload,
)?;
Ok(())
}
}
} else {
if self.context == SIG {
for i in 0..self.agents.len() {
if !algs::SIGNING_ALGS
.contains(&self.agents[i].header.alg.ok_or(CoseError::MissingAlg())?)
{
return Err(CoseError::InvalidAlg());
} else if !self.agents[i].key_ops.contains(&keys::KEY_OPS_SIGN) {
return Err(CoseError::KeyOpNotSupported());
} else {
self.agents[i].sign(&self.payload, &aead, &self.ph_bstr)?;
self.agents[i].enc = true;
}
}
Ok(())
} else {
let alg = self.header.alg.ok_or(CoseError::MissingAlg())?;
let mut cek;
if algs::DIRECT == self.agents[0].header.alg.ok_or(CoseError::MissingAlg())? {
if self.agents.len() > 1 {
return Err(CoseError::AlgOnlySupportsOneRecipient());
}
if !self.agents[0].key_ops.contains(&KO[self.context][0]) {
return Err(CoseError::KeyOpNotSupported());
} else {
if self.context == ENC {
self.secured = self.agents[0].enc(
&self.payload,
&aead,
&self.ph_bstr,
&alg,
self.header.iv.as_ref().ok_or(CoseError::MissingIV())?,
)?;
self.agents[0].enc = true;
return Ok(());
} else {
self.agents[0].mac(&self.payload, &aead, &self.ph_bstr)?;
self.agents[0].enc = true;
return Ok(());
}
}
} else if algs::ECDH_H.contains(
self.agents[0]
.header
.alg
.as_ref()
.ok_or(CoseError::MissingAlg())?,
) {
if self.agents.len() > 1 {
return Err(CoseError::AlgOnlySupportsOneRecipient());
}
let size = algs::get_cek_size(&alg)?;
cek = self.agents[0].derive_key(&Vec::new(), size, true, &alg)?;
self.agents[0].enc = true;
} else {
cek = algs::gen_random_key(&alg)?;
for i in 0..self.agents.len() {
if algs::DIRECT == self.agents[i].header.alg.unwrap()
|| algs::ECDH_H.contains(self.agents[i].header.alg.as_ref().unwrap())
{
return Err(CoseError::AlgOnlySupportsOneRecipient());
}
cek = self.agents[i].derive_key(&cek, cek.len(), true, &alg)?;
self.agents[i].enc = true;
}
}
if self.context == ENC {
let iv = match self.agents[0].base_iv.clone() {
Some(v) => algs::gen_iv(
self.header
.partial_iv
.as_ref()
.ok_or(CoseError::MissingPartialIV())?,
&v,
&alg,
)?,
None => self.header.iv.clone().ok_or(CoseError::MissingIV())?,
};
self.secured = cose_struct::gen_cipher(
&cek,
&alg,
&iv,
&aead,
cose_struct::ENCRYPT,
&self.ph_bstr,
&self.payload,
)?;
Ok(())
} else {
self.secured = cose_struct::gen_mac(
&cek,
&alg,
&aead,
cose_struct::MAC,
&self.ph_bstr,
&self.payload,
)?;
Ok(())
}
}
}
}
pub fn encode(&mut self, data: bool) -> CoseResult {
if self.agents.len() <= 0 {
if self.secured.len() <= 0 {
if self.context == SIG {
Err(CoseError::MissingSignature())
} else if self.context == MAC {
Err(CoseError::MissingTag())
} else {
Err(CoseError::MissingCiphertext())
}
} else {
let mut e = Encoder::new(Vec::new());
e.tag(TAGS[self.context][0])?;
e.array(SIZES[self.context][0])?;
e.bytes(self.ph_bstr.as_slice())?;
self.header.encode_unprotected(&mut e)?;
if data {
if self.context == ENC {
e.bytes(self.secured.as_slice())?;
} else {
e.bytes(self.payload.as_slice())?;
}
} else {
e.null()?;
}
if self.context != ENC {
e.bytes(self.secured.as_slice())?;
}
self.bytes = e.into_writer().to_vec();
self.header.labels_found = Vec::new();
Ok(())
}
} else {
let mut e = Encoder::new(Vec::new());
e.tag(TAGS[self.context][1])?;
e.array(SIZES[self.context][1])?;
e.bytes(self.ph_bstr.as_slice())?;
self.header.encode_unprotected(&mut e)?;
if data {
if self.context == ENC {
e.bytes(self.secured.as_slice())?;
} else {
e.bytes(self.payload.as_slice())?;
}
} else {
e.null()?;
}
if self.context == MAC {
e.bytes(self.secured.as_slice())?;
}
let a_len = self.agents.len();
e.array(a_len)?;
for i in 0..a_len {
self.agents[i].encode(&mut e)?;
}
self.bytes = e.into_writer().to_vec();
self.header.labels_found = Vec::new();
Ok(())
}
}
pub fn init_decoder(&mut self, data: Option<Vec<u8>>) -> CoseResult {
let input = Cursor::new(self.bytes.clone());
let mut d = Decoder::new(Config::default(), input);
let mut tag: Option<Tag> = None;
match d.tag() {
Ok(v) => {
if !TAGS[self.context].contains(&v) {
return Err(CoseError::InvalidTag());
} else {
tag = Some(v);
d.array()?;
}
}
Err(ref err) => match err {
DecodeError::UnexpectedType { datatype, info } => {
if *datatype != Type::Array && *info != SIZES[self.context][0] as u8 {
return Err(CoseError::InvalidCoseStructure());
}
}
_ => {
return Err(CoseError::InvalidCoseStructure());
}
},
};
self.ph_bstr = common::ph_bstr(d.bytes())?;
if self.ph_bstr.len() > 0 {
self.header.decode_protected_bstr(&self.ph_bstr)?;
}
self.header.decode_unprotected(&mut d, false)?;
self.header.labels_found = Vec::new();
match data {
None => {
if self.context == ENC {
self.secured = d.bytes()?.to_vec();
} else {
self.payload = d.bytes()?.to_vec();
}
}
Some(v) => {
d.skip()?;
if self.context == ENC {
self.secured = v;
} else {
self.payload = v;
}
}
};
if (self.context == ENC && self.secured.len() <= 0)
|| (self.context != ENC && self.payload.len() <= 0)
{
if self.context == ENC {
return Err(CoseError::MissingCiphertext());
} else {
return Err(CoseError::MissingPayload());
}
}
if self.context != SIG {
if self.header.alg.ok_or(CoseError::MissingAlg())? == algs::DIRECT
&& self.ph_bstr.len() > 0
{
return Err(CoseError::InvalidCoseStructure());
} else if algs::A_KW.contains(self.header.alg.as_ref().ok_or(CoseError::MissingAlg())?)
&& self.ph_bstr.len() > 0
{
return Err(CoseError::InvalidCoseStructure());
}
}
if self.context == MAC {
self.secured = d.bytes()?.to_vec();
if self.secured.len() <= 0 {
return Err(CoseError::MissingPayload());
}
}
match d.kernel().typeinfo() {
Ok(type_info) => {
if type_info.0 == Type::Array
&& (tag == None || tag.unwrap() == TAGS[self.context][1])
{
let r_len = type_info.1;
let mut agent: CoseAgent;
for _ in 0..r_len {
agent = CoseAgent::new();
agent.context = CONTEXTS[self.context].to_string();
d.array()?;
agent.ph_bstr = common::ph_bstr(d.bytes())?;
agent.decode(&mut d)?;
agent.enc = true;
self.agents.push(agent);
}
} else if type_info.0 == Type::Bytes
&& (tag == None || tag.unwrap() == TAGS[self.context][0])
{
if self.context == SIG {
self.secured = d.kernel().raw_data(type_info.1, 0x500000)?;
}
if self.secured.len() <= 0 {
if self.context == SIG {
return Err(CoseError::MissingSignature());
} else if self.context == MAC {
return Err(CoseError::MissingTag());
} else {
return Err(CoseError::MissingCiphertext());
}
}
}
}
Err(_) => {}
}
Ok(())
}
pub fn decode(
&mut self,
external_aad: Option<Vec<u8>>,
agent: Option<usize>,
) -> CoseResultWithRet<Vec<u8>> {
let aead = match external_aad {
None => Vec::new(),
Some(v) => v,
};
if self.agents.len() <= 0 {
if !self.key_decode {
return Err(CoseError::KeyOpNotSupported());
} else {
if self.context == SIG {
if !cose_struct::verify_sig(
&self.pub_key,
&self.header.alg.ok_or(CoseError::MissingAlg())?,
&self.crv,
&aead,
cose_struct::SIGNATURE1,
&self.ph_bstr,
&Vec::new(),
&self.payload,
&self.secured,
)? {
Err(CoseError::InvalidSignature())
} else {
Ok(self.payload.clone())
}
} else if self.context == MAC {
if !cose_struct::verify_mac(
&self.priv_key,
&self.header.alg.ok_or(CoseError::MissingAlg())?,
&aead,
cose_struct::MAC0,
&self.ph_bstr,
&self.secured,
&self.payload,
)? {
return Err(CoseError::InvalidMAC());
} else {
Ok(self.payload.clone())
}
} else {
let iv = match self.base_iv.clone() {
Some(v) => algs::gen_iv(
self.header
.partial_iv
.as_ref()
.ok_or(CoseError::MissingPartialIV())?,
&v,
&self.header.alg.ok_or(CoseError::MissingAlg())?,
)?,
None => self.header.iv.clone().ok_or(CoseError::MissingIV())?,
};
Ok(cose_struct::dec_cipher(
&self.priv_key,
&self.header.alg.ok_or(CoseError::MissingAlg())?,
&iv,
&aead,
cose_struct::ENCRYPT0,
&self.ph_bstr,
&self.secured,
)?)
}
}
} else if agent != None {
let index = agent.ok_or(CoseError::MissingSigner())?;
if self.context == SIG {
if self.agents[index].pub_key.len() == 0
|| !self.agents[index].key_ops.contains(&keys::KEY_OPS_VERIFY)
{
Err(CoseError::KeyOpNotSupported())
} else {
if !self.agents[index].verify(&self.payload, &aead, &self.ph_bstr)? {
Err(CoseError::InvalidSignature())
} else {
Ok(self.payload.clone())
}
}
} else {
let alg = self.header.alg.ok_or(CoseError::MissingAlg())?;
let cek;
if algs::DIRECT
== self.agents[index]
.header
.alg
.ok_or(CoseError::MissingAlg())?
{
if !self.agents[index].key_ops.contains(&KO[self.context][1]) {
return Err(CoseError::KeyOpNotSupported());
} else {
if self.agents[index].s_key.len() > 0 {
cek = self.agents[index].s_key.clone();
} else {
return Err(CoseError::KeyOpNotSupported());
}
}
} else {
let size = algs::get_cek_size(&alg)?;
let payload = self.agents[index].payload.clone();
cek = self.agents[index].derive_key(&payload, size, false, &alg)?;
}
if self.context == ENC {
let iv = match self.agents[index].base_iv.clone() {
Some(v) => algs::gen_iv(
self.header
.partial_iv
.as_ref()
.ok_or(CoseError::MissingPartialIV())?,
&v,
&alg,
)?,
None => self.header.iv.clone().ok_or(CoseError::MissingIV())?,
};
Ok(cose_struct::dec_cipher(
&cek,
&alg,
&iv,
&aead,
cose_struct::ENCRYPT,
&self.ph_bstr,
&self.secured,
)?)
} else {
if !cose_struct::verify_mac(
&cek,
&alg,
&aead,
cose_struct::MAC,
&self.ph_bstr,
&self.secured,
&self.payload,
)? {
Err(CoseError::InvalidMAC())
} else {
Ok(self.payload.clone())
}
}
}
} else {
return Err(CoseError::MissingSigner());
}
}
}