@@ -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]
131266fn 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