use bcder::{decode, encode, Captured};
use bcder::{Mode, Oid, OctetString, Tag};
use bcder::decode::{DecodeError, IntoSource, Source};
use bcder::encode::PrimitiveContent;
use bytes::Bytes;
use crate::oid;
use crate::crypto::{
DigestAlgorithm, KeyIdentifier, RpkiSignature, RpkiSignatureAlgorithm,
SignatureAlgorithm, Signer, SigningError, PublicKey
};
use crate::repository::error::{
InspectionError, ValidationError, VerificationError
};
use crate::repository::sigobj::{MessageDigest, SignedAttrs};
use crate::repository::x509::{
Name, Serial, SignedData, Time, Validity, encode_extension,
};
use super::idcert::IdCert;
#[derive(Clone, Debug)]
pub struct SignedMessage {
digest_algorithm: DigestAlgorithm,
content_type: Oid<Bytes>,
content: OctetString,
ee_cert: IdCert,
crl: SignedMessageCrl,
sid: KeyIdentifier,
signed_attrs: SignedAttrs,
signature: RpkiSignature,
message_digest: MessageDigest,
}
impl SignedMessage {
pub fn content_type(&self) -> &Oid<Bytes> {
&self.content_type
}
pub fn content(&self) -> &OctetString {
&self.content
}
}
impl SignedMessage {
pub fn decode<S: IntoSource>(
source: S, strict: bool,
) -> Result<Self, DecodeError<<S::Source as Source>::Error>> {
if strict {
Mode::Der
}
else {
Mode::Ber
}.decode(source.into_source(), Self::take_from)
}
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(|cons| {
oid::SIGNED_DATA.skip_if(cons)?; cons.take_constructed_if(Tag::CTX_0, Self::take_signed_data)
})
}
fn take_signed_data<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(|cons| {
cons.skip_u8_if(3)?;
let digest_algorithm = DigestAlgorithm::take_set_from(cons)?;
let (content_type, content) = {
cons.take_sequence(|cons| {
Ok((
Oid::take_from(cons)?,
cons.take_constructed_if(
Tag::CTX_0,
OctetString::take_from
)?,
))
})?
};
if content_type != oid::PROTOCOL_CONTENT_TYPE {
return Err(cons.content_err("unexpected content type"));
}
let id_cert = Self::take_id_cert(cons)?;
let crl = Self::take_crl(cons)?;
let (sid, attrs, signature) = {
cons.take_set(|cons| {
cons.take_sequence(|cons| {
cons.skip_u8_if(3)?;
let sid = cons.take_value_if(Tag::CTX_0, |content| {
KeyIdentifier::from_content(content)
})?;
let alg = DigestAlgorithm::take_from(cons)?;
if alg != digest_algorithm {
return Err(cons.content_err(
"signer algorithm mismatch"
));
}
let attrs = SignedAttrs::take_from_signed_message(
cons
)?;
if attrs.2 != content_type {
return Err(cons.content_err(
"content type in signed attributes differs"
));
}
let signature = RpkiSignature::new(
RpkiSignatureAlgorithm::cms_take_from(cons)?,
OctetString::take_from(cons)?.into_bytes(),
);
Ok((sid, attrs, signature))
})
})?
};
Ok(Self {
digest_algorithm,
content_type,
content,
ee_cert: id_cert,
crl,
sid,
signed_attrs: attrs.0,
signature,
message_digest: attrs.1,
})
})
}
fn take_id_cert<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<IdCert, DecodeError<S::Error>> {
cons.take_constructed_if(Tag::CTX_0, |cons| {
cons.take_constructed(|tag, cons| match tag {
Tag::SEQUENCE => IdCert::from_constructed(cons),
_ => {
Err(cons.content_err(
"multiple embedded EE certificates not supported"
))
}
})
})
}
fn take_crl<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<SignedMessageCrl, DecodeError<S::Error>> {
cons.take_constructed_if(Tag::CTX_1, |cons| {
SignedMessageCrl::take_from(cons)
})
}
}
impl SignedMessage {
pub fn validate(
&self, issuer_key: &PublicKey
) -> Result<(), ValidationError> {
self.validate_at(issuer_key, Time::now())
}
pub fn validate_at(
&self, issuer_key: &PublicKey, when: Time
) -> Result<(), ValidationError> {
self.inspect()?;
self.verify()?;
self.ee_cert.validate_ee_at(issuer_key, when)?;
self.crl.validate(issuer_key, when)?;
self.crl.verify_not_revoked(&self.ee_cert)?;
Ok(())
}
fn inspect(
&self,
) -> Result<(), InspectionError> {
if self.sid != self.ee_cert.subject_key_identifier() {
return Err(InspectionError::new(
"Subject Key Identifier mismatch in signed object"
))
}
Ok(())
}
fn verify(&self) -> Result<(), VerificationError> {
let digest = {
let mut context = self.digest_algorithm.start();
self.content.iter().for_each(|x| context.update(x));
context.finish()
};
if digest.as_ref() != self.message_digest.as_ref() {
return Err(VerificationError::new(
"message digest mismatch in signed object"
))
}
let msg = self.signed_attrs.encode_verify();
self.ee_cert
.subject_public_key_info()
.verify(&msg, &self.signature)
.map_err(Into::into)
}
}
impl SignedMessage {
pub fn create<S: Signer>(
data: Bytes,
validity: Validity,
issuing_key_id: &S::KeyId,
signer: &S,
) -> Result<Self, SigningError<S::Error>> {
let digest_algorithm = DigestAlgorithm::default();
let content_type = Oid(oid::PROTOCOL_CONTENT_TYPE.0.into());
let message_digest = digest_algorithm.digest(&data).into();
let signing_time = Time::now();
let signed_attrs = SignedAttrs::new(
&content_type,
&message_digest,
signing_time,
);
let (signature, ee_key) = signer.sign_one_off(
RpkiSignatureAlgorithm::default(), &signed_attrs.encode_verify()
)?;
let sid = ee_key.key_identifier();
let crl = SignedMessageCrl::create(
&validity,
issuing_key_id,
signer
)?;
let ee_cert = IdCert::new_ee(
&ee_key,
validity,
issuing_key_id,
signer
)?;
let content = OctetString::new(data);
Ok(SignedMessage {
digest_algorithm,
content_type,
content,
ee_cert,
crl,
sid,
signed_attrs,
signature,
message_digest,
})
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence((
oid::SIGNED_DATA.encode(), encode::sequence_as(Tag::CTX_0,
encode::sequence((
3u8.encode(), self.digest_algorithm.encode_set(),
encode::sequence(( self.content_type.encode_ref(),
encode::sequence_as(Tag::CTX_0,
self.content.encode_ref()
),
)),
encode::sequence_as(Tag::CTX_0, self.ee_cert.encode_ref(),
),
encode::sequence_as(Tag::CTX_1, self.crl.encode_ref(),
),
encode::set( encode::sequence(( 3u8.encode(), self.sid.encode_ref_as(Tag::CTX_0),
self.digest_algorithm.encode(), self.signed_attrs.encode_ref(), self.signature.algorithm().cms_encode(),
OctetString::encode_slice( self.signature.value().as_ref()
),
))
)
))
)
))
}
pub fn to_captured(&self) -> Captured {
Captured::from_values(Mode::Der, self.encode_ref())
}
}
#[derive(Clone, Debug)]
struct SignedMessageCrl {
signed_data: SignedData,
tbs: SignedMessageTbsCrl,
}
impl SignedMessageCrl {
pub fn validate(
&self,
issuer_key: &PublicKey,
when: Time
) -> Result<(), ValidationError> {
if self.tbs.signature != *self.signed_data.signature().algorithm() {
return Err(VerificationError::new(
"CRL signature algorithm mismatch"
).into())
}
self.signed_data.verify_signature(
issuer_key
).map_err(VerificationError::from)?;
self.tbs.validate(issuer_key, when)
}
fn verify_not_revoked(
&self, id_cert: &IdCert
) -> Result<(), VerificationError> {
if self.tbs.revoked_certs.contains(id_cert.serial_number()) {
Err(VerificationError::new(
"signed object EE certificate revoked"
))
} else {
Ok(())
}
}
}
impl SignedMessageCrl {
fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(Self::from_constructed)
}
fn from_constructed<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let signed_data = SignedData::from_constructed(cons)?;
let tbs = signed_data.data().clone().decode(
SignedMessageTbsCrl::take_from
).map_err(DecodeError::convert)?;
Ok(Self { signed_data, tbs })
}
}
impl SignedMessageCrl {
fn create<S: Signer>(
validity: &Validity,
issuing_key_id: &S::KeyId,
signer: &S,
) -> Result<Self, SigningError<S::Error>> {
let issuing_pub_key = signer.get_key_info(issuing_key_id)?;
let signature = RpkiSignatureAlgorithm::default();
let issuer = Name::from_pub_key(&issuing_pub_key);
let this_update = validity.not_before();
let next_update = validity.not_after();
let revoked_certs = RevokedCertificates::empty();
let authority_key_id = Some(issuing_pub_key.key_identifier());
let crl_number = Some(Serial::from(
Time::now().timestamp_millis() as u64
));
let tbs = SignedMessageTbsCrl {
signature,
issuer,
this_update,
next_update,
revoked_certs,
authority_key_id,
crl_number,
};
let data = Captured::from_values(Mode::Der, tbs.encode_ref());
let signature = signer.sign(issuing_key_id, tbs.signature, &data)?;
let signed_data = SignedData::new(data, signature);
Ok(SignedMessageCrl {
signed_data,
tbs,
})
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
self.signed_data.encode_ref()
}
}
#[derive(Clone, Debug)]
struct SignedMessageTbsCrl {
signature: RpkiSignatureAlgorithm,
issuer: Name,
this_update: Time,
next_update: Time,
revoked_certs: RevokedCertificates,
authority_key_id: Option<KeyIdentifier>,
crl_number: Option<Serial>,
}
impl SignedMessageTbsCrl {
fn validate(
&self,
issuer_key: &PublicKey,
when: Time,
) -> Result<(), ValidationError> {
if self.this_update > when {
Err(VerificationError::new(
"CRL thisUpdate time in the future"
).into())
}
else if self.next_update < when {
Err(VerificationError::new(
"CRL nextUpdate time in the past"
).into())
}
else {
match self.authority_key_id {
None => Ok(()),
Some(aki) => if issuer_key.key_identifier() == aki {
Ok(())
} else {
Err(VerificationError::new(
"CRL's Authority Key Identifier doesn't match \
issuer key"
).into())
}
}
}
}
}
impl SignedMessageTbsCrl {
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
cons.take_sequence(|cons| {
cons.skip_u8_if(1)?;
let signature = RpkiSignatureAlgorithm::x509_take_from(cons)?;
let issuer = Name::take_from(cons)?;
let this_update = Time::take_from(cons)?;
let next_update = Time::take_from(cons)?;
let revoked_certs = RevokedCertificates::take_from(cons)?;
let mut authority_key_id = None;
let mut crl_number = None;
cons.take_constructed_if(Tag::CTX_0, |cons| {
cons.take_sequence(|cons| {
while let Some(()) = cons.take_opt_sequence(|cons| {
let id = Oid::take_from(cons)?;
let _critical = cons.take_opt_bool()?.unwrap_or(false);
let value = OctetString::take_from(cons)?;
Mode::Der.decode(value.into_source(), |content| {
if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
Self::take_authority_key_identifier(
content, &mut authority_key_id
)
}
else if id == oid::CE_CRL_NUMBER {
Self::take_crl_number(
content, &mut crl_number
)
}
else {
content.skip_all()
}
}).map_err(DecodeError::convert)
})? { }
Ok(())
})
})?;
Ok(Self {
signature,
issuer,
this_update,
next_update,
revoked_certs,
authority_key_id,
crl_number
})
})
}
fn take_authority_key_identifier<S: decode::Source>(
cons: &mut decode::Constructed<S>,
authority_key_id: &mut Option<KeyIdentifier>,
) -> Result<(), DecodeError<S::Error>> {
if authority_key_id.is_some() {
Err(cons.content_err(
"duplicate Authority Key Identifier extension"
))
}
else {
*authority_key_id = Some(
cons.take_sequence(|cons| {
cons.take_value_if(
Tag::CTX_0, KeyIdentifier::from_content
)
})?
);
Ok(())
}
}
fn take_crl_number<S: decode::Source>(
cons: &mut decode::Constructed<S>,
crl_number: &mut Option<Serial>,
) -> Result<(), DecodeError<S::Error>> {
if crl_number.is_some() {
Err(cons.content_err("duplicate CRL number extension"))
}
else {
*crl_number = Some(
Serial::take_from(cons)?
);
Ok(())
}
}
}
impl SignedMessageTbsCrl {
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence((
1.encode(), self.signature.x509_encode(),
self.issuer.encode_ref(),
self.this_update.encode_varied(),
self.next_update.encode_varied(),
self.revoked_certs.encode_ref(),
encode::sequence_as(Tag::CTX_0,
encode::sequence((
self.authority_key_id.as_ref().map(|authority_key_id| {
encode_extension(
&oid::CE_AUTHORITY_KEY_IDENTIFIER, false,
encode::sequence(
authority_key_id.encode_ref_as(Tag::CTX_0)
)
)
}),
self.crl_number.map(|crl_number| {
encode_extension(
&oid::CE_CRL_NUMBER, false,
crl_number.encode()
)
}),
))
)
))
}
}
#[derive(Clone, Debug)]
struct RevokedCertificates(Captured);
impl RevokedCertificates {
pub fn empty() -> Self {
let entries: Vec<CrlEntry> = vec![];
Self::from_iter(entries)
}
pub fn take_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let res = cons.take_opt_sequence(|cons| {
cons.capture(|cons| {
while CrlEntry::take_opt_from(cons)?.is_some() { }
Ok(())
})
})?;
Ok(RevokedCertificates(match res {
Some(res) => res,
None => Captured::empty(Mode::Der)
}))
}
pub fn contains(&self, serial: Serial) -> bool {
Mode::Der.decode(self.0.as_ref(), |cons| {
while let Some(entry) =
CrlEntry::take_opt_from(cons).unwrap() {
if entry.user_certificate == serial {
return Ok(true)
}
}
Ok(false)
}).unwrap()
}
pub fn encode_ref(&self) -> impl encode::Values + '_ {
encode::sequence(&self.0)
}
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = CrlEntry>,
<I as IntoIterator>::IntoIter: Clone
{
RevokedCertificates(Captured::from_values(
Mode::Der, encode::iter(
iter.into_iter().map(CrlEntry::encode)
)
))
}
}
#[derive(Clone, Copy, Debug)]
struct CrlEntry {
user_certificate: Serial,
revocation_date: Time,
}
impl CrlEntry {
pub fn take_opt_from<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Option<Self>, DecodeError<S::Error>> {
cons.take_opt_sequence(Self::from_constructed)
}
pub fn from_constructed<S: decode::Source>(
cons: &mut decode::Constructed<S>
) -> Result<Self, DecodeError<S::Error>> {
let entry = CrlEntry {
user_certificate: Serial::take_from(cons)?,
revocation_date: Time::take_from(cons)?,
};
cons.take_opt_sequence(|cons| cons.skip_all())?;
Ok(entry)
}
pub fn encode(self) -> impl encode::Values {
encode::sequence((
self.user_certificate.encode(),
self.revocation_date.encode_varied(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_and_validate_signed_message() {
let der = include_bytes!("../../test-data/ca/sigmsg/pdu_200.der");
let msg = SignedMessage::decode(Bytes::from_static(der), false).unwrap();
let b = include_bytes!("../../test-data/ca/sigmsg/cms_ta.cer");
let id_cert = IdCert::decode(Bytes::from_static(b)).unwrap();
msg.validate_at(
id_cert.public_key(),
Time::utc(2012, 1, 1, 0, 0, 0)
).unwrap();
}
}
#[cfg(all(test, feature="softkeys"))]
mod signer_test {
use crate::crypto::{softsigner::OpenSslSigner, PublicKeyFormat};
use super::*;
#[test]
fn encode_and_sign_signed_message() {
let signer = OpenSslSigner::new();
let ta_key = signer.create_key(PublicKeyFormat::Rsa).unwrap();
let ta_cert = IdCert::new_ta(
Validity::from_secs(60),
&ta_key,
&signer
).unwrap();
let content = Bytes::from_static(b"euj");
let validity = Validity::from_secs(60);
let signed_message = SignedMessage::create(
content,
validity,
&ta_key,
&signer
).unwrap();
let bytes = signed_message.to_captured().into_bytes();
let decoded = SignedMessage::decode(bytes, false).unwrap();
decoded.validate(ta_cert.public_key()).unwrap();
}
}