use asn1_rs::FromDer;
use async_generic::async_generic;
use ciborium::value::Value;
use coset::{
iana::{self, EnumI64},
CoseSign1, CoseSign1Builder, Header, HeaderBuilder, Label, ProtectedHeader,
TaggedCborSerializable,
};
use serde_bytes::ByteBuf;
use x509_parser::prelude::X509Certificate;
use super::cert_chain_from_sign1;
use crate::{
cose::{add_sigtst_header, add_sigtst_header_async, CoseError, TimeStampStorage},
ec_utils::{der_to_p1363, ec_curve_from_public_key_der, parse_ec_der_sig},
raw_signature::{AsyncRawSigner, RawSigner, SigningAlg},
};
#[async_generic(async_signature(
signer: &dyn AsyncRawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage
))]
pub fn sign(
signer: &dyn RawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage,
) -> Result<Vec<u8>, CoseError> {
if _sync {
match tss {
TimeStampStorage::V1_sigTst => sign_v1(signer, data, box_size, tss),
TimeStampStorage::V2_sigTst2_CTT => sign_v2(signer, data, box_size, tss),
}
} else {
match tss {
TimeStampStorage::V1_sigTst => sign_v1_async(signer, data, box_size, tss).await,
TimeStampStorage::V2_sigTst2_CTT => sign_v2_async(signer, data, box_size, tss).await,
}
}
}
#[async_generic(async_signature(
signer: &dyn AsyncRawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage
))]
pub fn sign_v1(
signer: &dyn RawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage,
) -> Result<Vec<u8>, CoseError> {
let alg = signer.alg();
let protected_header = if _sync {
build_protected_header(signer, alg)?
} else {
build_protected_header_async(signer, alg).await?
};
let aad: &[u8; 0] = b"";
let unprotected_header = if _sync {
build_unprotected_header(signer, data, &protected_header, tss)?
} else {
build_unprotected_header_async(signer, data, &protected_header, tss).await?
};
let sign1_builder = CoseSign1Builder::new()
.protected(protected_header.header.clone())
.unprotected(unprotected_header)
.payload(data.to_vec());
let mut sign1 = sign1_builder.build();
let tbs = coset::sig_structure_data(
coset::SignatureContext::CoseSign1,
sign1.protected.clone(),
None,
aad,
sign1.payload.as_ref().unwrap_or(&vec![]),
);
let signature = if _sync {
signer.sign(&tbs)?
} else {
signer.sign(tbs).await?
};
sign1.signature = match alg {
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
if parse_ec_der_sig(&signature).is_ok() {
let certs = cert_chain_from_sign1(&sign1)?;
let signing_cert = certs.first().ok_or(CoseError::CborGenerationError(
"bad certificate chain".to_string(),
))?;
let (_, cert) = X509Certificate::from_der(signing_cert).map_err(|_e| {
CoseError::CborGenerationError("incorrect EC signature format".to_string())
})?;
let certificate_public_key = cert.public_key();
let curve = ec_curve_from_public_key_der(certificate_public_key.raw).ok_or(
CoseError::CborGenerationError("incorrect EC signature format".to_string()),
)?;
der_to_p1363(&signature, curve.p1363_sig_len())?
} else {
signature
}
}
_ => signature,
};
sign1.payload = None;
pad_cose_sig(&mut sign1, box_size)
}
#[async_generic(async_signature(
signer: &dyn AsyncRawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage
))]
pub fn sign_v2(
signer: &dyn RawSigner,
data: &[u8],
box_size: Option<usize>,
tss: TimeStampStorage,
) -> Result<Vec<u8>, CoseError> {
let alg = signer.alg();
let protected_header = if _sync {
build_protected_header(signer, alg)?
} else {
build_protected_header_async(signer, alg).await?
};
let aad: &[u8; 0] = b"";
let sign1_builder = CoseSign1Builder::new()
.protected(protected_header.header.clone())
.payload(data.to_vec());
let mut sign1 = sign1_builder.build();
let tbs = coset::sig_structure_data(
coset::SignatureContext::CoseSign1,
sign1.protected.clone(),
None,
aad,
sign1.payload.as_ref().unwrap_or(&vec![]),
);
let signature = if _sync {
signer.sign(&tbs)?
} else {
signer.sign(tbs).await?
};
sign1.signature = match alg {
SigningAlg::Es256 | SigningAlg::Es384 | SigningAlg::Es512 => {
if parse_ec_der_sig(&signature).is_ok() {
let certs = cert_chain_from_sign1(&sign1)?;
let signing_cert = certs.first().ok_or(CoseError::CborGenerationError(
"bad certificate chain".to_string(),
))?;
let (_, cert) = X509Certificate::from_der(signing_cert).map_err(|_e| {
CoseError::CborGenerationError("incorrect EC signature format".to_string())
})?;
let certificate_public_key = cert.public_key();
let curve = ec_curve_from_public_key_der(certificate_public_key.raw).ok_or(
CoseError::CborGenerationError("incorrect EC signature format".to_string()),
)?;
der_to_p1363(&signature, curve.p1363_sig_len())?
} else {
signature
}
}
_ => signature,
};
sign1.payload = None;
let sig_data = ByteBuf::from(sign1.signature.clone());
let mut sig_data_cbor: Vec<u8> = vec![];
ciborium::into_writer(&sig_data, &mut sig_data_cbor)
.map_err(|e| CoseError::CborGenerationError(e.to_string()))?;
let unprotected_header = if _sync {
build_unprotected_header(signer, &sig_data_cbor, &protected_header, tss)?
} else {
build_unprotected_header_async(signer, &sig_data_cbor, &protected_header, tss).await?
};
sign1.unprotected = unprotected_header;
pad_cose_sig(&mut sign1, box_size)
}
#[async_generic(async_signature(signer: &dyn AsyncRawSigner, alg: SigningAlg))]
fn build_protected_header(
signer: &dyn RawSigner,
alg: SigningAlg,
) -> Result<ProtectedHeader, CoseError> {
let mut protected_h = match alg {
SigningAlg::Ps256 => HeaderBuilder::new().algorithm(iana::Algorithm::PS256),
SigningAlg::Ps384 => HeaderBuilder::new().algorithm(iana::Algorithm::PS384),
SigningAlg::Ps512 => HeaderBuilder::new().algorithm(iana::Algorithm::PS512),
SigningAlg::Es256 => HeaderBuilder::new().algorithm(iana::Algorithm::ES256),
SigningAlg::Es384 => HeaderBuilder::new().algorithm(iana::Algorithm::ES384),
SigningAlg::Es512 => HeaderBuilder::new().algorithm(iana::Algorithm::ES512),
SigningAlg::Ed25519 => HeaderBuilder::new().algorithm(iana::Algorithm::EdDSA),
};
let certs = signer.cert_chain()?;
let sc_der_array_or_bytes = match certs.len() {
1 => Value::Bytes(certs[0].clone()),
_ => Value::Array(certs.into_iter().map(Value::Bytes).collect()),
};
protected_h = protected_h.value(
iana::HeaderParameter::X5Chain.to_i64(),
sc_der_array_or_bytes.clone(),
);
let protected_header = protected_h.build();
let ph2 = ProtectedHeader {
original_data: None,
header: protected_header.clone(),
};
Ok(ph2)
}
#[async_generic(async_signature(signer: &dyn AsyncRawSigner, data: &[u8], p_header: &ProtectedHeader, tss: TimeStampStorage,))]
fn build_unprotected_header(
signer: &dyn RawSigner,
data: &[u8],
p_header: &ProtectedHeader,
tss: TimeStampStorage,
) -> Result<Header, CoseError> {
let unprotected_h = HeaderBuilder::new();
let mut unprotected_h = if _sync {
add_sigtst_header(signer, data, p_header, unprotected_h, tss)?
} else {
add_sigtst_header_async(signer, data, p_header, unprotected_h, tss).await?
};
let ocsp_val = if _sync {
signer.ocsp_response()
} else {
signer.ocsp_response().await
};
if let Some(ocsp) = ocsp_val {
let mut ocsp_vec: Vec<Value> = Vec::new();
let mut r_vals: Vec<(Value, Value)> = vec![];
ocsp_vec.push(Value::Bytes(ocsp));
r_vals.push((Value::Text("ocspVals".to_string()), Value::Array(ocsp_vec)));
unprotected_h = unprotected_h.text_value("rVals".to_string(), Value::Map(r_vals));
}
Ok(unprotected_h.build())
}
const PAD: &str = "pad";
const PAD2: &str = "pad2";
const PAD_OFFSET: usize = 7;
fn pad_cose_sig(sign1: &mut CoseSign1, end_size: Option<usize>) -> Result<Vec<u8>, CoseError> {
let mut sign1_clone = sign1.clone();
let cur_vec = sign1_clone
.to_tagged_vec()
.map_err(|e| CoseError::CborGenerationError(e.to_string()))?;
let Some(end_size) = end_size else {
return Ok(cur_vec);
};
let cur_size = cur_vec.len();
if cur_size == end_size {
return Ok(cur_vec);
}
if cur_size + PAD_OFFSET > end_size {
return Err(CoseError::BoxSizeTooSmall);
}
let mut padding_found = false;
let mut last_pad = 0;
let mut target_guess = end_size - cur_size - PAD_OFFSET;
loop {
sign1_clone = sign1.clone();
for header_pair in &mut sign1_clone.unprotected.rest {
if header_pair.0 == Label::Text("pad".to_string()) {
if let Value::Bytes(b) = &header_pair.1 {
last_pad = b.len();
}
header_pair.1 = Value::Bytes(vec![0u8; target_guess]);
padding_found = true;
break;
}
}
if !padding_found {
sign1_clone.unprotected.rest.push((
Label::Text(PAD.to_string()),
Value::Bytes(vec![0u8; target_guess]),
));
return pad_cose_sig(&mut sign1_clone, Some(end_size));
}
let new_cbor = sign1_clone
.to_tagged_vec()
.map_err(|e| CoseError::CborGenerationError(e.to_string()))?;
match new_cbor.len() < end_size {
true => target_guess += 1,
false if new_cbor.len() == end_size => return Ok(new_cbor),
false => break,
}
}
sign1.unprotected.rest.push((
Label::Text(PAD2.to_string()),
Value::Bytes(vec![0u8; last_pad - 10]),
));
pad_cose_sig(sign1, Some(end_size))
}