Skip to content

Commit 6434fe5

Browse files
cms: tag TimeStampedData / ERS types implicitly, as RFC 5544 and RFC 4998 define (#2465)
Both ASN.1 modules are DEFINITIONS IMPLICIT TAGS, but the context-tagged fields of Evidence, EvidenceRecord and ArchiveTimeStamp used the derive's default, EXPLICIT. So cms could not decode an RFC-encoded evidence record: e.g. EvidenceRecord.encryptionInfo [1] fails with "expected SEQUENCE, got OBJECT IDENTIFIER", and encoding produced bytes other implementations don't read. Mark the eight fields tag_mode = "IMPLICIT" (constructed for the CHOICE alternatives, as in revocation.rs). New tests decode and re-encode an EvidenceRecord produced by pyasn1 from the RFC 4998 module, standalone and as Evidence::ErsEvidence.
1 parent e0acafd commit 6434fe5

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

‎cms/src/timestamped_data.rs‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ pub struct MetaData {
7878
#[derive(Clone, Debug, Eq, PartialEq, Choice)]
7979
#[allow(missing_docs)]
8080
pub enum Evidence {
81-
#[asn1(context_specific = "0")]
81+
#[asn1(context_specific = "0", tag_mode = "IMPLICIT", constructed = "true")]
8282
TstEvidence(TimeStampTokenEvidence),
83-
#[asn1(context_specific = "1")]
83+
#[asn1(context_specific = "1", tag_mode = "IMPLICIT", constructed = "true")]
8484
ErsEvidence(EvidenceRecord),
85-
#[asn1(context_specific = "2")]
85+
#[asn1(context_specific = "2", tag_mode = "IMPLICIT", constructed = "true")]
8686
OtherEvidence(OtherEvidence),
8787
}
8888

@@ -124,9 +124,9 @@ pub type TimeStampToken = ContentInfo;
124124
pub struct EvidenceRecord {
125125
pub version: TsdVersion,
126126
pub digest_algorithm: Vec<AlgorithmIdentifierOwned>,
127-
#[asn1(context_specific = "0", optional = "true")]
127+
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
128128
pub crypto_infos: Option<CryptoInfos>,
129-
#[asn1(context_specific = "1", optional = "true")]
129+
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
130130
pub encryption_info: Option<EncryptionInfo>,
131131
pub archive_timestamp_sequence: ArchiveTimeStampSequence,
132132
}
@@ -171,11 +171,11 @@ pub type ArchiveTimeStampChain = Vec<ArchiveTimeStamp>;
171171
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
172172
#[allow(missing_docs)]
173173
pub struct ArchiveTimeStamp {
174-
#[asn1(context_specific = "0", optional = "true")]
174+
#[asn1(context_specific = "0", optional = "true", tag_mode = "IMPLICIT")]
175175
digest_algorithm: Option<AlgorithmIdentifierOwned>,
176-
#[asn1(context_specific = "1", optional = "true")]
176+
#[asn1(context_specific = "1", optional = "true", tag_mode = "IMPLICIT")]
177177
attributes: Option<Attributes>,
178-
#[asn1(context_specific = "2", optional = "true")]
178+
#[asn1(context_specific = "2", optional = "true", tag_mode = "IMPLICIT")]
179179
reduced_hashtree: Option<Vec<PartialHashtree>>,
180180
time_stamp: ContentInfo,
181181
}

‎cms/tests/timestamped_data.rs‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//! TimeStampedData / EvidenceRecord tests.
2+
//!
3+
//! RFC 4998 (ERS) and RFC 5544 (TimeStampedData) define their ASN.1 modules with
4+
//! `DEFINITIONS IMPLICIT TAGS`, so their context-specific tags replace the inner type's tag.
5+
6+
use cms::timestamped_data::{Evidence, EvidenceRecord};
7+
use der::{Decode, Encode};
8+
use hex_literal::hex;
9+
10+
/// An `EvidenceRecord` encoded by pyasn1 from the RFC 4998 ASN.1 module, with
11+
/// `encryptionInfo [1]` and one `ArchiveTimeStamp` carrying `digestAlgorithm [0]` and
12+
/// `reducedHashtree [2]`.
13+
const EVIDENCE_RECORD: &[u8] = &hex!(
14+
"3064020101300d300b0609608648016503040201a10606022a030500304830463044a00b0609608648016503040201"
15+
"a224302204201111111111111111111111111111111111111111111111111111111111111111"
16+
"300f06092a864886f70d010702a0020500"
17+
);
18+
19+
#[test]
20+
fn evidence_record_implicit_tags_round_trip() {
21+
let record = EvidenceRecord::from_der(EVIDENCE_RECORD).unwrap();
22+
assert!(record.encryption_info.is_some());
23+
assert_eq!(record.archive_timestamp_sequence.len(), 1);
24+
assert_eq!(record.archive_timestamp_sequence[0].len(), 1);
25+
assert_eq!(record.to_der().unwrap(), EVIDENCE_RECORD);
26+
}
27+
28+
#[test]
29+
fn evidence_choice_implicit_tags_round_trip() {
30+
// ersEvidence [1] EvidenceRecord: the SEQUENCE tag (0x30) is replaced by [1] (0xa1).
31+
let mut ers = EVIDENCE_RECORD.to_vec();
32+
ers[0] = 0xa1;
33+
let evidence = Evidence::from_der(&ers).unwrap();
34+
assert!(matches!(evidence, Evidence::ErsEvidence(_)));
35+
assert_eq!(evidence.to_der().unwrap(), ers);
36+
}

0 commit comments

Comments
 (0)