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