Skip to content

Commit df9f17c

Browse files
committed
add more validation and test
1 parent 2b87b26 commit df9f17c

1 file changed

Lines changed: 156 additions & 2 deletions

File tree

fastcrypto/src/tests/nitro_attestation_tests.rs

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,141 @@ fn test_with_malformed_attestation() {
127127
));
128128
}
129129

130+
fn int(v: i128) -> Value {
131+
Value::Integer(ciborium::value::Integer::try_from(v).unwrap())
132+
}
133+
134+
/// A minimal document map that passes `validate_document_map` with all required fields.
135+
fn valid_document_map() -> BTreeMap<String, Value> {
136+
let mut map = BTreeMap::new();
137+
map.insert("module_id".to_string(), Value::Text("some".to_string()));
138+
map.insert("digest".to_string(), Value::Text("SHA384".to_string()));
139+
map.insert("certificate".to_string(), Value::Bytes(vec![1]));
140+
map.insert("timestamp".to_string(), int(1731627987382));
141+
map.insert(
142+
"pcrs".to_string(),
143+
Value::Map(vec![(int(0), Value::Bytes(vec![1; 32]))]),
144+
);
145+
map.insert(
146+
"cabundle".to_string(),
147+
Value::Array(vec![Value::Bytes(vec![1])]),
148+
);
149+
map
150+
}
151+
152+
/// Assert that upgraded-mode validation of `map` fails with `InvalidAttestationDoc(msg)`.
153+
fn assert_invalid(map: &BTreeMap<String, Value>, msg: &str) {
154+
assert_eq!(
155+
AttestationDocument::validate_document_map(map, true, true, true).unwrap_err(),
156+
NitroAttestationVerifyError::InvalidAttestationDoc(msg.to_string())
157+
);
158+
}
159+
160+
#[test]
161+
fn test_timestamp_validity() {
162+
let mut map = valid_document_map();
163+
map.remove("timestamp");
164+
assert_invalid(&map, "timestamp not found");
165+
map.insert("timestamp".to_string(), Value::Text("nope".to_string()));
166+
assert_invalid(&map, "timestamp is not an integer");
167+
map.insert("timestamp".to_string(), int(-1));
168+
assert_invalid(&map, "timestamp not u64");
169+
}
170+
171+
#[test]
172+
fn test_empty_and_nonce_fields() {
173+
// Empty certificate is rejected.
174+
let mut map = valid_document_map();
175+
map.insert("certificate".to_string(), Value::Bytes(vec![]));
176+
assert_invalid(&map, "invalid certificate");
177+
178+
// nonce is rejected above 512 bytes, accepted at the boundary.
179+
let mut map = valid_document_map();
180+
map.insert("nonce".to_string(), Value::Bytes(vec![1; 513]));
181+
assert_invalid(&map, "invalid nonce");
182+
map.insert("nonce".to_string(), Value::Bytes(vec![1; 512]));
183+
assert!(AttestationDocument::validate_document_map(&map, true, true, true).is_ok());
184+
185+
// Empty public_key is rejected in legacy parsing but accepted in upgraded parsing.
186+
let mut map = valid_document_map();
187+
map.insert("public_key".to_string(), Value::Bytes(vec![]));
188+
assert_eq!(
189+
AttestationDocument::validate_document_map(&map, false, false, false).unwrap_err(),
190+
NitroAttestationVerifyError::InvalidAttestationDoc("invalid public key".to_string())
191+
);
192+
assert!(AttestationDocument::validate_document_map(&map, true, true, true).is_ok());
193+
}
194+
195+
#[test]
196+
fn test_pcr_validation() {
197+
let set_pcrs = |map: &mut BTreeMap<String, Value>, pcrs| {
198+
map.insert("pcrs".to_string(), Value::Map(pcrs));
199+
};
200+
let mut map = valid_document_map();
201+
202+
// missing / wrong type.
203+
map.remove("pcrs");
204+
assert_invalid(&map, "pcrs not found");
205+
map.insert("pcrs".to_string(), Value::Array(vec![]));
206+
assert_invalid(&map, "invalid pcrs format");
207+
208+
// too many entries (> 32) fail the length check.
209+
set_pcrs(
210+
&mut map,
211+
(0..33)
212+
.map(|i| (int(i), Value::Bytes(vec![1; 32])))
213+
.collect(),
214+
);
215+
assert_invalid(&map, "invalid PCRs length");
216+
217+
// key must be an integer.
218+
set_pcrs(
219+
&mut map,
220+
vec![(Value::Text("0".to_string()), Value::Bytes(vec![1; 32]))],
221+
);
222+
assert_invalid(&map, "invalid PCR key format");
223+
224+
// negative and duplicate indices are rejected.
225+
set_pcrs(&mut map, vec![(int(-1), Value::Bytes(vec![1; 32]))]);
226+
assert_invalid(&map, "invalid PCR index");
227+
set_pcrs(
228+
&mut map,
229+
vec![
230+
(int(0), Value::Bytes(vec![1; 32])),
231+
(int(0), Value::Bytes(vec![2; 32])),
232+
],
233+
);
234+
assert_invalid(&map, "duplicate PCR index 0");
235+
236+
// index outside 0..=31 is skipped (not inserted), but the document still parses.
237+
set_pcrs(&mut map, vec![(int(32), Value::Bytes(vec![1; 32]))]);
238+
let doc = AttestationDocument::validate_document_map(&map, true, true, true).unwrap();
239+
assert!(doc.pcr_map.is_empty());
240+
}
241+
242+
#[test]
243+
fn test_cabundle_validity() {
244+
let mut map = valid_document_map();
245+
246+
// too many entries (> MAX_CERT_CHAIN_LENGTH of 10).
247+
map.insert(
248+
"cabundle".to_string(),
249+
Value::Array((0..11).map(|_| Value::Bytes(vec![1])).collect()),
250+
);
251+
assert_invalid(&map, "invalid ca chain length");
252+
253+
// entry too long (1025).
254+
map.insert(
255+
"cabundle".to_string(),
256+
Value::Array(vec![Value::Bytes(vec![1; 1025])]),
257+
);
258+
assert_invalid(&map, "invalid ca length");
259+
260+
// wrong type.
261+
map.insert("cabundle".to_string(), Value::Map(vec![]));
262+
assert_invalid(&map, "invalid cabundle");
263+
}
264+
130265
#[test]
131266
fn test_attestation_fields_validity() {
132267
let mut map = BTreeMap::new();
@@ -232,13 +367,32 @@ fn bad_signature_cose() {
232367
true,
233368
)
234369
.unwrap();
235-
let mut bad_sig = parsed.1.clone();
236-
bad_sig[0] ^= 0x00;
370+
371+
// A well-formed (96-byte) but tampered signature must fail the ECDSA verification.
372+
let mut bad_sig = parsed.0.clone();
373+
bad_sig[0] ^= 0x01;
237374
let res = verify_nitro_attestation(&bad_sig, &parsed.1, &parsed.2, 1731627987382);
375+
assert_eq!(
376+
res.unwrap_err(),
377+
FastCryptoError::GeneralError("SignatureFailedToVerify".to_string())
378+
);
379+
380+
// A signature of the wrong length is rejected before verification.
381+
let res = verify_nitro_attestation(&parsed.1, &parsed.1, &parsed.2, 1731627987382);
238382
assert_eq!(
239383
res.unwrap_err(),
240384
FastCryptoError::GeneralError("InvalidSignature".to_string())
241385
);
386+
387+
// Tampering with the signed message also fails verification.
388+
let mut bad_msg = parsed.1.clone();
389+
let last = bad_msg.len() - 1;
390+
bad_msg[last] ^= 0x01;
391+
let res = verify_nitro_attestation(&parsed.0, &bad_msg, &parsed.2, 1731627987382);
392+
assert_eq!(
393+
res.unwrap_err(),
394+
FastCryptoError::GeneralError("SignatureFailedToVerify".to_string())
395+
);
242396
}
243397

244398
#[test]

0 commit comments

Comments
 (0)