1 //! Context-specific field.
2 
3 use crate::{
4     asn1::Any, Choice, Decodable, Encodable, Encoder, Error, ErrorKind, Header, Length, Result,
5     Tag, TagNumber,
6 };
7 use core::convert::TryFrom;
8 
9 /// Context-specific field.
10 ///
11 /// This type encodes a field which is specific to a particular context,
12 /// and is identified by a [`TagNumber`].
13 ///
14 /// Any context-specific field can be decoded/encoded with this type.
15 /// The intended use is to dynamically dispatch off of the context-specific
16 /// tag number when decoding, which allows support for extensions, which are
17 /// denoted in an ASN.1 schema using the `...` ellipsis extension marker.
18 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
19 pub struct ContextSpecific<'a> {
20     /// Context-specific tag number sans the leading `0b10000000` class
21     /// identifier bit and `0b100000` constructed flag.
22     pub tag_number: TagNumber,
23 
24     /// Value of the field.
25     pub value: Any<'a>,
26 }
27 
28 impl<'a> Choice<'a> for ContextSpecific<'a> {
can_decode(tag: Tag) -> bool29     fn can_decode(tag: Tag) -> bool {
30         matches!(tag, Tag::ContextSpecific(_))
31     }
32 }
33 
34 impl<'a> Encodable for ContextSpecific<'a> {
encoded_len(&self) -> Result<Length>35     fn encoded_len(&self) -> Result<Length> {
36         self.value.encoded_len()?.for_tlv()
37     }
38 
encode(&self, encoder: &mut Encoder<'_>) -> Result<()>39     fn encode(&self, encoder: &mut Encoder<'_>) -> Result<()> {
40         let tag = Tag::ContextSpecific(self.tag_number);
41         Header::new(tag, self.value.encoded_len()?)?.encode(encoder)?;
42         self.value.encode(encoder)
43     }
44 }
45 
46 impl<'a> From<&ContextSpecific<'a>> for ContextSpecific<'a> {
from(value: &ContextSpecific<'a>) -> ContextSpecific<'a>47     fn from(value: &ContextSpecific<'a>) -> ContextSpecific<'a> {
48         *value
49     }
50 }
51 
52 impl<'a> TryFrom<Any<'a>> for ContextSpecific<'a> {
53     type Error = Error;
54 
try_from(any: Any<'a>) -> Result<ContextSpecific<'a>>55     fn try_from(any: Any<'a>) -> Result<ContextSpecific<'a>> {
56         match any.tag() {
57             Tag::ContextSpecific(tag_number) => Ok(Self {
58                 tag_number,
59                 value: Any::from_der(any.as_bytes())?,
60             }),
61             actual => Err(ErrorKind::UnexpectedTag {
62                 expected: None,
63                 actual,
64             }
65             .into()),
66         }
67     }
68 }
69 
70 #[cfg(test)]
71 mod tests {
72     use super::ContextSpecific;
73     use crate::{Decodable, Encodable, Tag};
74     use hex_literal::hex;
75 
76     // Public key data from `pkcs8` crate's `ed25519-pkcs8-v2.der`
77     const EXAMPLE_BYTES: &[u8] =
78         &hex!("A123032100A3A7EAE3A8373830BC47E1167BC50E1DB551999651E0E2DC587623438EAC3F31");
79 
80     #[test]
round_trip()81     fn round_trip() {
82         let field = ContextSpecific::from_der(EXAMPLE_BYTES).unwrap();
83         assert_eq!(field.tag_number.value(), 1);
84         assert_eq!(field.value.tag(), Tag::BitString);
85         assert_eq!(field.value.as_bytes(), &EXAMPLE_BYTES[5..]);
86 
87         let mut buf = [0u8; 128];
88         let encoded = field.encode_to_slice(&mut buf).unwrap();
89         assert_eq!(encoded, EXAMPLE_BYTES);
90     }
91 }
92