Skip to content

Commit 3ba8068

Browse files
authored
test(se050): refactor and add tests for ExtraData parsing (#1165)
added tests for extradata. Also refactored a bit.
1 parent 9c2204f commit 3ba8068

7 files changed

Lines changed: 428 additions & 369 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deny.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ ignore = [
1717
{ id = "RUSTSEC-2026-0097", reason = "transitive dep, unsound only under rare conditions with custom logger accessing rand::rng(), upgrade blocked by upstream" },
1818
{ id = "RUSTSEC-2026-0098", reason = "transitive dep via aws-sdk and reqwest 0.11, low severity, upgrade blocked by upstream" },
1919
{ id = "RUSTSEC-2026-0099", reason = "transitive dep via aws-sdk and reqwest 0.11, low severity, upgrade blocked by upstream" },
20+
{ id = "RUSTSEC-2026-0104", reason = "transitive dep via aws-sdk and reqwest 0.11, low severity, upgrade blocked by upstream" },
2021
]
2122
unmaintained = "workspace" # only warn for direct dependencies (not transitive ones)
2223
version = 2

se050/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ rust-version.workspace = true
1414
clap.workspace = true
1515
color-eyre.workspace = true
1616
derive_more = { workspace = true, features = ["from", "into"] }
17+
hex-literal.workspace = true
1718
orb-telemetry.workspace = true
1819
owo-colors.workspace = true
1920
thiserror.workspace = true

se050/src/attributes.rs

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
use std::fmt::Debug;
2+
3+
use thiserror::Error;
4+
use zerocopy::{big_endian, FromBytes, Immutable, KnownLayout, TryFromBytes};
5+
6+
/// See section 4.3.6 of AN12413
7+
#[derive(TryFromBytes, Immutable, KnownLayout, Debug, Eq, PartialEq, Clone, Copy)]
8+
#[repr(u8)]
9+
#[expect(non_camel_case_types)]
10+
pub enum SecureObjectType {
11+
EC_KEY_PAIR = 0x01,
12+
EC_PRIV_KEY = 0x02,
13+
EC_PUB_KEY = 0x03,
14+
}
15+
16+
#[derive(
17+
FromBytes,
18+
Immutable,
19+
KnownLayout,
20+
Eq,
21+
PartialEq,
22+
Clone,
23+
Copy,
24+
derive_more::From,
25+
derive_more::Into,
26+
)]
27+
#[repr(transparent)]
28+
pub struct ObjectId(pub big_endian::U32);
29+
30+
struct U32Formatter(big_endian::U32);
31+
32+
impl Debug for U32Formatter {
33+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34+
write!(f, "{:#010X}", self.0)
35+
}
36+
}
37+
38+
impl core::fmt::Debug for ObjectId {
39+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40+
f.debug_tuple("ObjectId")
41+
.field(&U32Formatter(self.0))
42+
.finish()
43+
}
44+
}
45+
46+
impl core::fmt::Display for ObjectId {
47+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48+
write!(f, "{:?}", U32Formatter(self.0))
49+
}
50+
}
51+
52+
impl ObjectId {
53+
pub fn new(id: u32) -> Self {
54+
Self(id.into())
55+
}
56+
}
57+
58+
impl PartialEq<u32> for ObjectId {
59+
fn eq(&self, other: &u32) -> bool {
60+
self.0 == *other
61+
}
62+
}
63+
64+
/// See section 4.3.29 of AN12413
65+
#[derive(TryFromBytes, Immutable, KnownLayout, Debug, Eq, PartialEq, Clone, Copy)]
66+
#[repr(u8)]
67+
#[expect(non_camel_case_types)]
68+
pub enum SetIndicator {
69+
NOT_SET = 0x01,
70+
SET = 0x02,
71+
}
72+
73+
#[derive(TryFromBytes, KnownLayout, Immutable, Debug, PartialEq, Eq)]
74+
#[repr(C, align(1))]
75+
pub struct ObjectAttributes {
76+
pub object_identifier: ObjectId,
77+
pub object_class: SecureObjectType,
78+
pub authentication_indicator: SetIndicator,
79+
pub authentication_attempts_counter: big_endian::U16,
80+
pub authentication_object_identifier: ObjectId,
81+
pub maximum_authentication_attempts: big_endian::U16,
82+
pub policy_set: AttributesSuffix,
83+
}
84+
85+
/// See section 4.3.8 of AN12413
86+
#[derive(TryFromBytes, Immutable, KnownLayout, Debug, Eq, PartialEq, Clone, Copy)]
87+
#[repr(u8)]
88+
#[expect(non_camel_case_types)]
89+
pub enum Origin {
90+
ORIGIN_EXTERNAL = 0x01,
91+
ORIGIN_INTERNAL = 0x02,
92+
ORIGIN_PROVISIONED = 0x03,
93+
}
94+
95+
#[derive(Debug, Error, Eq, PartialEq)]
96+
#[error("failed to parse origin")]
97+
pub struct OriginParseErr;
98+
99+
#[derive(TryFromBytes, KnownLayout, Immutable, Eq, PartialEq)]
100+
#[repr(C)]
101+
pub struct AttributesSuffix([u8]);
102+
103+
impl core::fmt::Debug for AttributesSuffix {
104+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105+
struct PolicySetFormatter<'a>(&'a AttributesSuffix);
106+
107+
impl core::fmt::Debug for PolicySetFormatter<'_> {
108+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109+
let mut list = f.debug_list();
110+
for policy in self.0.policies() {
111+
list.entry(&policy);
112+
}
113+
114+
list.finish()
115+
}
116+
}
117+
118+
f.debug_struct("AttributesSuffix")
119+
.field("origin", &self.origin())
120+
.field("policy_set", &PolicySetFormatter(self))
121+
.finish()
122+
}
123+
}
124+
125+
impl AttributesSuffix {
126+
pub fn origin(&self) -> Result<Origin, OriginParseErr> {
127+
let v = self.0.last().unwrap();
128+
129+
Origin::try_read_from_bytes(&[*v]).map_err(|_| OriginParseErr)
130+
}
131+
132+
pub fn policies(&self) -> PolicySetIter<'_> {
133+
PolicySetIter {
134+
attributes_suffix: self,
135+
idx: 0,
136+
}
137+
}
138+
}
139+
140+
pub struct PolicySetIter<'a> {
141+
attributes_suffix: &'a AttributesSuffix,
142+
idx: usize,
143+
}
144+
145+
impl<'a> Iterator for PolicySetIter<'a> {
146+
type Item = &'a Policy;
147+
148+
fn next(&mut self) -> Option<Self::Item> {
149+
// subtract 1 to account for origin suffix
150+
let policy_set_len = self.attributes_suffix.0.len() - 1;
151+
if self.idx >= policy_set_len {
152+
return None;
153+
}
154+
155+
let policy_bytes = &self.attributes_suffix.0[self.idx..policy_set_len];
156+
let (header, _suffix) =
157+
zerocopy::Ref::<_, Policy>::from_prefix_with_elems(policy_bytes, 0)
158+
.unwrap();
159+
let (policy, remaining_bytes) =
160+
zerocopy::Ref::<_, Policy>::from_prefix_with_elems(
161+
policy_bytes,
162+
usize::from(
163+
header.length_in_bytes
164+
- (core::mem::size_of::<ObjectId>() as u8)
165+
- 4, // length of access_rule header
166+
),
167+
)
168+
.unwrap();
169+
170+
self.idx = policy_set_len - remaining_bytes.len();
171+
172+
Some(zerocopy::Ref::into_ref(policy))
173+
}
174+
}
175+
176+
#[derive(FromBytes, KnownLayout, Immutable, Debug, Eq, PartialEq)]
177+
#[repr(C)]
178+
pub struct Policy {
179+
pub length_in_bytes: u8,
180+
pub authentication_object_id: ObjectId,
181+
pub access_rule: AccessRule,
182+
}
183+
184+
#[derive(FromBytes, KnownLayout, Immutable, Debug, Eq, PartialEq)]
185+
#[repr(C)]
186+
pub struct AccessRule {
187+
pub header: [u8; 4],
188+
pub extension: [u8],
189+
}
190+
191+
#[cfg(test)]
192+
mod test {
193+
use crate::{
194+
example_data::{ORB_ATTESTATION_KEY, ORB_IRIS_KEY, ORB_SESSION_KEY},
195+
extra_data::ExtraData,
196+
};
197+
198+
use super::*;
199+
200+
#[test]
201+
fn test_object_id_debug() {
202+
let obj = ObjectId::new(0x6000_0000);
203+
assert_eq!(format!("{obj:?}"), "ObjectId(0x60000000)");
204+
205+
let obj = ObjectId::new(0x0000_6000);
206+
assert_eq!(format!("{obj:?}"), "ObjectId(0x00006000)");
207+
}
208+
209+
#[test]
210+
fn test_object_id_display() {
211+
let obj = ObjectId::new(0x6000_0000);
212+
assert_eq!(format!("{obj}"), "0x60000000");
213+
214+
let obj = ObjectId::new(0x0000_6000);
215+
assert_eq!(format!("{obj}"), "0x00006000");
216+
}
217+
218+
#[test]
219+
fn test_orb_session_key_parses() {
220+
let extra_data =
221+
ExtraData::try_from(ORB_SESSION_KEY).expect("failed to parse extradata");
222+
let attrs = extra_data.object_attributes;
223+
224+
assert_eq!(attrs.object_identifier, 0x60000000);
225+
assert_eq!(attrs.object_class, SecureObjectType::EC_PUB_KEY);
226+
assert_eq!(attrs.authentication_indicator, SetIndicator::SET);
227+
assert_eq!(attrs.authentication_attempts_counter, 0);
228+
assert_eq!(attrs.authentication_object_identifier, 0x00000000);
229+
assert_eq!(attrs.maximum_authentication_attempts, 0);
230+
assert_eq!(attrs.policy_set.origin(), Ok(Origin::ORIGIN_EXTERNAL));
231+
232+
let mut it = attrs.policy_set.policies();
233+
234+
assert!(it.next().is_none());
235+
}
236+
237+
#[test]
238+
fn test_orb_attestation_key_parses() {
239+
let extra_data = ExtraData::try_from(ORB_ATTESTATION_KEY)
240+
.expect("failed to parse extradata");
241+
let attrs = extra_data.object_attributes;
242+
243+
assert_eq!(attrs.object_identifier, 0x60000001);
244+
assert_eq!(attrs.object_class, SecureObjectType::EC_KEY_PAIR);
245+
assert_eq!(attrs.authentication_indicator, SetIndicator::NOT_SET);
246+
assert_eq!(attrs.authentication_attempts_counter, 0);
247+
assert_eq!(attrs.authentication_object_identifier, 0x60000000);
248+
assert_eq!(attrs.maximum_authentication_attempts, 0);
249+
assert_eq!(attrs.policy_set.origin(), Ok(Origin::ORIGIN_INTERNAL));
250+
251+
let mut it = attrs.policy_set.policies();
252+
253+
let p1 = it.next().unwrap();
254+
assert_eq!(p1.length_in_bytes, 8);
255+
assert_eq!(p1.authentication_object_id, 0x60000000);
256+
assert_eq!(core::mem::size_of_val(&p1.access_rule), 4);
257+
258+
assert!(it.next().is_none())
259+
}
260+
261+
#[test]
262+
fn test_orb_iris_key_parses() {
263+
let extra_data =
264+
ExtraData::try_from(ORB_IRIS_KEY).expect("failed to parse extradata");
265+
let attrs = extra_data.object_attributes;
266+
267+
assert_eq!(attrs.object_identifier, 0x60000002);
268+
assert_eq!(attrs.object_class, SecureObjectType::EC_KEY_PAIR);
269+
assert_eq!(attrs.authentication_indicator, SetIndicator::NOT_SET);
270+
assert_eq!(attrs.authentication_attempts_counter, 0);
271+
assert_eq!(attrs.authentication_object_identifier, 0x60000000);
272+
assert_eq!(attrs.maximum_authentication_attempts, 0);
273+
assert_eq!(attrs.policy_set.origin(), Ok(Origin::ORIGIN_INTERNAL));
274+
275+
let mut it = attrs.policy_set.policies();
276+
277+
let p1 = it.next().unwrap();
278+
assert_eq!(p1.length_in_bytes, 8);
279+
assert_eq!(p1.authentication_object_id, 0x60000000);
280+
assert_eq!(core::mem::size_of_val(&p1.access_rule), 4);
281+
282+
assert!(it.next().is_none())
283+
}
284+
}

0 commit comments

Comments
 (0)