1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! High-level, early-stage parsed structures for a section: the header, then everything else.
16 
17 use crate::extended::{
18     deserialize, salt::ShortV1Salt, V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN,
19     V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN,
20     V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, V1_ENCODING_UNENCRYPTED,
21     V1_IDENTITY_TOKEN_LEN,
22 };
23 use crate::helpers::parse_byte_array;
24 use nom::{combinator, error, number, sequence};
25 use np_hkdf::v1_salt::{ExtendedV1Salt, EXTENDED_SALT_LEN};
26 
27 #[cfg(test)]
28 mod tests;
29 
30 #[derive(PartialEq, Eq, Debug)]
31 pub(crate) enum SectionHeader {
32     Unencrypted,
33     Encrypted(EncryptedSectionHeader),
34 }
35 
36 impl SectionHeader {
37     /// Returns the parsed header and the remaining length of the section contents
38     ///
39     /// This structure makes it easy to get a slice of the entire header with
40     /// [combinator::consumed] for later inclusion in signatures etc, but also
41     /// to [nom::bytes::complete::take] the rest of the section with a minimum of
42     /// error-prone length calculations.
parse(input: &[u8]) -> nom::IResult<&[u8], (&[u8], Self, u8)>43     pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], (&[u8], Self, u8)> {
44         // Consume first 1-2 bytes of format
45         // 0bERRRSSSS first header byte
46         let (input, (format_bytes, (first_header_byte, maybe_second_byte))) =
47             combinator::consumed(nom::branch::alt((
48                 // Extended bit not set, verify all reserved bits are 0
49                 combinator::map(
50                     combinator::verify(number::complete::u8, |b| {
51                         !deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
52                     }),
53                     |b| (b, None),
54                 ),
55                 // Extended bit is set, take another byte and verify all reserved bits are 0
56                 combinator::map(
57                     nom::sequence::pair(
58                         combinator::verify(number::complete::u8, |b| {
59                             deserialize::hi_bit_set(*b) && (*b & 0x70) == 0
60                         }),
61                         combinator::verify(number::complete::u8, |second_byte| *second_byte == 0u8),
62                     ),
63                     |(b1, b2)| (b1, Some(b2)),
64                 ),
65             )))(input)?;
66 
67         let encoding = first_header_byte & 0x0F;
68 
69         let (input, section_header) = match (encoding, maybe_second_byte) {
70             (V1_ENCODING_UNENCRYPTED, None) => Ok((input, (SectionHeader::Unencrypted))),
71             (V1_ENCODING_ENCRYPTED_MIC_WITH_SHORT_SALT_AND_TOKEN, None) => combinator::map(
72                 sequence::tuple((ShortV1Salt::parse, CiphertextExtendedIdentityToken::parse)),
73                 |(salt, token)| {
74                     SectionHeader::Encrypted(EncryptedSectionHeader::MicShortSalt { salt, token })
75                 },
76             )(input),
77             (V1_ENCODING_ENCRYPTED_MIC_WITH_EXTENDED_SALT_AND_TOKEN, None) => combinator::map(
78                 sequence::tuple((parse_v1_extended_salt, CiphertextExtendedIdentityToken::parse)),
79                 |(salt, token)| {
80                     SectionHeader::Encrypted(EncryptedSectionHeader::MicExtendedSalt {
81                         salt,
82                         token,
83                     })
84                 },
85             )(input),
86             (V1_ENCODING_ENCRYPTED_SIGNATURE_WITH_EXTENDED_SALT_AND_TOKEN, None) => {
87                 combinator::map(
88                     sequence::tuple((
89                         parse_v1_extended_salt,
90                         CiphertextExtendedIdentityToken::parse,
91                     )),
92                     |(salt, token)| {
93                         SectionHeader::Encrypted(EncryptedSectionHeader::SigExtendedSalt {
94                             salt,
95                             token,
96                         })
97                     },
98                 )(input)
99             }
100             _ => Err(nom::Err::Error(error::Error::new(input, error::ErrorKind::Alt))),
101         }?;
102 
103         //finally parse the section payload length, this is the same regardless of encoding scheme
104         let (input, section_len) = combinator::verify(number::complete::u8, |b| {
105             // length cannot be 0 for an unencrypted section as this is meaningless
106             !(encoding == V1_ENCODING_UNENCRYPTED && *b == 0u8)
107         })(input)?;
108 
109         Ok((input, (format_bytes, section_header, section_len)))
110     }
111 }
112 
parse_v1_extended_salt(input: &[u8]) -> nom::IResult<&[u8], ExtendedV1Salt>113 fn parse_v1_extended_salt(input: &[u8]) -> nom::IResult<&[u8], ExtendedV1Salt> {
114     combinator::map(parse_byte_array::<{ EXTENDED_SALT_LEN }>, ExtendedV1Salt::from)(input)
115 }
116 
117 #[allow(clippy::enum_variant_names)]
118 #[derive(PartialEq, Eq, Debug)]
119 pub(crate) enum EncryptedSectionHeader {
120     MicShortSalt { salt: ShortV1Salt, token: CiphertextExtendedIdentityToken },
121     MicExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
122     SigExtendedSalt { salt: ExtendedV1Salt, token: CiphertextExtendedIdentityToken },
123 }
124 
125 /// 16-byte identity token, straight out of the section.
126 ///
127 /// If identity resolution succeeds, decrypted to an [ExtendedIdentityToken](crate::extended::V1IdentityToken).
128 #[derive(Debug, Clone, Copy, Eq, PartialEq)]
129 pub(crate) struct CiphertextExtendedIdentityToken(pub(crate) [u8; V1_IDENTITY_TOKEN_LEN]);
130 
131 impl CiphertextExtendedIdentityToken {
parse(input: &[u8]) -> nom::IResult<&[u8], Self>132     pub(crate) fn parse(input: &[u8]) -> nom::IResult<&[u8], Self> {
133         combinator::map(parse_byte_array::<V1_IDENTITY_TOKEN_LEN>, Self)(input)
134     }
135 }
136 
137 impl From<[u8; V1_IDENTITY_TOKEN_LEN]> for CiphertextExtendedIdentityToken {
from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self138     fn from(value: [u8; V1_IDENTITY_TOKEN_LEN]) -> Self {
139         Self(value)
140     }
141 }
142