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