Repository navigation
[test] add fuzz targets for sequences of tbor commands and dispatcher #787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
David Zimmermann (zimmy87)
wants to merge
10
commits into
main
Choose a base branch
from
user/v-davidz/add_tbor_app_cmd_dispatcher
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
f795e9e
GPT-6 Luna generation: add 4 more fuzz targets
zimmy87 ae16592
Merge branch 'main' into user/v-davidz/add_tbor_app_cmd_dispatcher
zimmy87 daf62b2
Apply batched suggestions from code review
zimmy87 023d44c
Apply batched suggestions from code review
zimmy87 8bd3328
refactor: randomize key scope in fuzz_tbor_cmd_reqs
zimmy87 9378b27
refactor: fuzz key scope in fuzz_tbor_dispatcher_aes_fp
zimmy87 28f5bd0
refactor: fuzz key scope in fuzz_tbor_dispatcher
zimmy87 0ba9f42
refactor: fuzz key_usage & key_label in fuzz_tbor_dispatcher_aes_fp
zimmy87 d31c501
refactor: fuzz key_usage & key_label in fuzz_tbor_dispatcher
zimmy87 116fad3
copilot feedback
zimmy87 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,134 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #![no_main] | ||
|
|
||
| #[path = "../../common.rs"] | ||
| mod common; | ||
|
|
||
| use azihsm_ddi_interface::DdiError; | ||
| use azihsm_ddi_tbor_test_harness::CO_PSK_ID; | ||
| use azihsm_ddi_tbor_test_harness::CU_PSK_ID; | ||
| use azihsm_ddi_tbor_test_harness::ROTATED_CO_PSK; | ||
| use azihsm_ddi_tbor_test_harness::TestCtx; | ||
| use azihsm_ddi_tbor_test_harness::bootstrap_rotated_co; | ||
| use azihsm_ddi_tbor_test_harness::build_mac_fin; | ||
| use azihsm_ddi_tbor_types::MAC_FIN_LEN; | ||
| use azihsm_ddi_tbor_types::SessionType; | ||
| use azihsm_ddi_tbor_types::TborStatus; | ||
| use libfuzzer_sys::arbitrary; | ||
| use libfuzzer_sys::arbitrary::Arbitrary; | ||
| use libfuzzer_sys::fuzz_target; | ||
|
|
||
| #[derive(Arbitrary, Debug)] | ||
| enum TestAppOps { | ||
| OpenSession([u8; 16], [u8; 16]), | ||
| CloseSession(u16), | ||
| } | ||
|
|
||
| fuzz_target!(|ops: Vec<TestAppOps>| { | ||
| common::common_fuzz_test(&|ctx: &TestCtx, path: &str| { | ||
| let session = bootstrap_rotated_co(ctx, &ROTATED_CO_PSK); | ||
| let mut file_handles = Vec::new(); | ||
|
|
||
| for op in &ops { | ||
| match op { | ||
| TestAppOps::OpenSession(user_id, pin) => { | ||
| // The existing TBOR session occupies this file handle, so | ||
| // another SessionOpenInit must hit the per-handle limit. | ||
| let result = ctx.session_open_init(CO_PSK_ID, SessionType::Authenticated); | ||
| assert!( | ||
| matches!( | ||
| result, | ||
| Err(DdiError::TborStatus(status)) | ||
| if status == TborStatus::FileHandleSessionLimitReached | ||
| ), | ||
| "SessionOpenInit on an occupied file handle should hit the session limit" | ||
| ); | ||
|
|
||
| // The bootstrap occupies the single CO slot. Open the | ||
| // secondary session as CU so Phase 1 can reach Phase 2. | ||
| let secondary = TestCtx::new_with_path(path); | ||
| let pending = | ||
| match secondary.session_open_init(CU_PSK_ID, SessionType::PlainText) { | ||
| Ok(pending) => pending, | ||
| Err(DdiError::TborStatus(status)) | ||
| if status == TborStatus::VaultSessionLimitReached => | ||
| { | ||
| continue; | ||
| } | ||
| Err(_error) => { | ||
| panic!("CU SessionOpenInit with available capacity failed") | ||
| } | ||
| }; | ||
| let pending_session_id = pending.session_id; | ||
| let expected_mac = build_mac_fin(&pending) | ||
| .expect("building a valid SessionOpenFinish MAC should succeed"); | ||
|
|
||
| if user_id[0] & 1 == 0 { | ||
| let opened = secondary | ||
| .session_open_finish_with_mac(pending, expected_mac) | ||
| .expect("SessionOpenFinish with the valid MAC should succeed"); | ||
| file_handles.push((secondary, opened.session_id)); | ||
| } else { | ||
| let mut invalid_mac = expected_mac; | ||
| for (index, byte) in user_id.iter().chain(pin.iter()).enumerate() { | ||
| invalid_mac[index % MAC_FIN_LEN] ^= *byte; | ||
| } | ||
| invalid_mac[MAC_FIN_LEN - 1] ^= 1; | ||
|
|
||
| let finish_result = | ||
| secondary.session_open_finish_with_mac(pending, invalid_mac); | ||
| assert!( | ||
| matches!( | ||
| finish_result, | ||
| Err(DdiError::TborStatus(status)) | ||
| if status == TborStatus::SessionAuthFailure | ||
| ), | ||
| "SessionOpenFinish with a mutated MAC should fail authentication" | ||
| ); | ||
|
|
||
| let close_result = secondary.session_close(pending_session_id); | ||
| assert!( | ||
| matches!( | ||
| close_result, | ||
| Err(DdiError::TborStatus(status)) | ||
| if status == TborStatus::SessionNotFound | ||
| ), | ||
| "SessionClose on a failed handshake should report SessionNotFound" | ||
| ); | ||
| } | ||
| } | ||
| TestAppOps::CloseSession(session_id) => { | ||
| if let Some((file_handle, opened_session_id)) = file_handles.pop() { | ||
| let result = file_handle.session_close(*session_id); | ||
| if *session_id == opened_session_id { | ||
| result.expect("SessionClose for the matching session should succeed"); | ||
| } else { | ||
| assert!( | ||
| matches!( | ||
| result, | ||
| Err(DdiError::TborStatus(status)) | ||
| if status == TborStatus::FileHandleSessionIdDoesNotMatch | ||
| ), | ||
| "SessionClose for a different session ID should be rejected" | ||
| ); | ||
| file_handle | ||
| .session_close(opened_session_id) | ||
| .expect("closing the tracked session should succeed"); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| for (file_handle, session_id) in file_handles { | ||
| file_handle | ||
| .session_close(session_id) | ||
| .expect("closing the tracked session should succeed"); | ||
| } | ||
|
|
||
| ctx.session_close(session.session_id) | ||
| .expect("closing the bootstrap session should succeed"); | ||
| }); | ||
| }); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,179 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| #![no_main] | ||
|
|
||
| #[path = "../../common.rs"] | ||
| mod common; | ||
|
|
||
| use azihsm_ddi_tbor_test_harness::ROTATED_CO_PSK; | ||
| use azihsm_ddi_tbor_test_harness::TestCtx; | ||
| use azihsm_ddi_tbor_test_harness::bootstrap_rotated_co; | ||
| use azihsm_ddi_tbor_types::*; | ||
| use common::KeyScope; | ||
| use libfuzzer_sys::arbitrary; | ||
| use libfuzzer_sys::arbitrary::Arbitrary; | ||
| use libfuzzer_sys::fuzz_target; | ||
|
|
||
| const MIN_NUMBER_OF_REQS: usize = 1; | ||
| const MAX_NUMBER_OF_REQS: usize = 32; | ||
|
|
||
| #[derive(Debug, Arbitrary)] | ||
| struct FuzzInput { | ||
| /// Seed used to choose a deterministic sequence of TBOR requests. | ||
| rand_seed: u64, | ||
| /// Whether to bind requests to the open session or use an invalid id. | ||
| use_valid_header: bool, | ||
| } | ||
|
|
||
| #[derive(Clone, Copy)] | ||
| enum Command { | ||
| AesGenerateKey, | ||
| EccGenerateKey, | ||
| HmacGenerateKey, | ||
| } | ||
|
|
||
| fn next_random(state: &mut u64) -> u64 { | ||
| *state = state.wrapping_add(0x9E3779B97F4A7C15); | ||
| let mut value = *state; | ||
| value = (value ^ (value >> 30)).wrapping_mul(0xBF58476D1CE4E5B9); | ||
| value = (value ^ (value >> 27)).wrapping_mul(0x94D049BB133111EB); | ||
| value ^ (value >> 31) | ||
| } | ||
|
|
||
| fuzz_target!(|input: FuzzInput| { | ||
| common::common_fuzz_test(&|ctx: &TestCtx, _path: &str| { | ||
|
zimmy87 marked this conversation as resolved.
|
||
| let session = bootstrap_rotated_co(ctx, &ROTATED_CO_PSK); | ||
| let number_of_reqs = (input.rand_seed as usize | ||
| % (MAX_NUMBER_OF_REQS - MIN_NUMBER_OF_REQS + 1)) | ||
| + MIN_NUMBER_OF_REQS; | ||
| let mut state = input.rand_seed; | ||
| let mut requests = Vec::with_capacity(number_of_reqs); | ||
|
|
||
| for _ in 0..number_of_reqs { | ||
| let command = match next_random(&mut state) % 3 { | ||
| 0 => Command::AesGenerateKey, | ||
| 1 => Command::EccGenerateKey, | ||
| _ => Command::HmacGenerateKey, | ||
| }; | ||
| let key_scope = match next_random(&mut state) % 6 { | ||
| 0 => KeyScope::Unspecified, | ||
| 1 => KeyScope::Session, | ||
| 2 => KeyScope::Ephemeral, | ||
| 3 => KeyScope::Local, | ||
| 4 => KeyScope::SecurityDomain, | ||
| _ => KeyScope::Internal, | ||
| }; | ||
| requests.push((command, key_scope)); | ||
| } | ||
|
|
||
| if requests | ||
| .iter() | ||
| .any(|(_, scope)| matches!(scope, KeyScope::SecurityDomain)) | ||
| { | ||
| common::create_test_security_domain(ctx, &session); | ||
| } else if requests | ||
| .iter() | ||
| .any(|(_, scope)| matches!(scope, KeyScope::Ephemeral | KeyScope::Local)) | ||
| { | ||
| common::finalize_partition(ctx, &session); | ||
| } | ||
|
|
||
| for (command, key_scope) in requests { | ||
| let session_id = if input.use_valid_header { | ||
| session.session_id | ||
| } else { | ||
| session.session_id ^ 0x8000 | ||
| }; | ||
| let scope = key_scope.to_tbor(); | ||
|
|
||
| let succeeded = match command { | ||
| Command::AesGenerateKey => { | ||
| let req = TborAesGenerateKeyReq { | ||
| session_id, | ||
| scope, | ||
| key_size: AES_KEY_SIZE_128, | ||
| key_usage: KEY_USAGE_ENCRYPT | KEY_USAGE_DECRYPT, | ||
| key_label: Vec::new(), | ||
| }; | ||
| match ctx.tbor(&req) { | ||
| Ok(resp) => { | ||
| assert!( | ||
| (MASKED_AES_KEY_MIN_LEN..=MASKED_AES_KEY_MAX_LEN) | ||
| .contains(&resp.masked_key.len()), | ||
| "AES key generation returned an invalid masked-key length" | ||
| ); | ||
| true | ||
| } | ||
| Err(err @ azihsm_ddi_interface::DdiError::DriverError(_)) => { | ||
| panic!("Crash Detected: {err}") | ||
| } | ||
| Err(_) => false, | ||
|
zimmy87 marked this conversation as resolved.
|
||
| } | ||
| } | ||
| Command::EccGenerateKey => { | ||
| let req = TborEccGenerateKeyReq { | ||
| session_id, | ||
| scope, | ||
| curve: ECC_CURVE_P256, | ||
| key_usage: KEY_USAGE_SIGN, | ||
| key_label: Vec::new(), | ||
| }; | ||
| match ctx.tbor(&req) { | ||
| Ok(resp) => { | ||
| assert_eq!(resp.pub_key.len(), 64); | ||
| assert!( | ||
| (MASKED_ECC_KEY_MIN_LEN..=MASKED_ECC_KEY_MAX_LEN) | ||
| .contains(&resp.masked_key.len()), | ||
| "ECC key generation returned an invalid masked-key length" | ||
| ); | ||
| true | ||
| } | ||
| Err(err @ azihsm_ddi_interface::DdiError::DriverError(_)) => { | ||
| panic!("Crash Detected: {err}") | ||
| } | ||
| Err(_) => false, | ||
| } | ||
| } | ||
| Command::HmacGenerateKey => { | ||
| let req = TborHmacGenerateKeyReq { | ||
| session_id, | ||
| scope, | ||
| hash_algo: HMAC_HASH_SHA256, | ||
| key_length: 32, | ||
| key_label: Vec::new(), | ||
| }; | ||
| match ctx.tbor(&req) { | ||
| Ok(resp) => { | ||
| assert_eq!(resp.masked_key.len(), MASKED_HMAC_KEY_MIN_LEN); | ||
| true | ||
| } | ||
| Err(err @ azihsm_ddi_interface::DdiError::DriverError(_)) => { | ||
| panic!("Crash Detected: {err}") | ||
| } | ||
| Err(_) => false, | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| if input.use_valid_header { | ||
| let scope_is_supported = matches!( | ||
| key_scope, | ||
| KeyScope::Session | ||
| | KeyScope::Ephemeral | ||
| | KeyScope::Local | ||
| | KeyScope::SecurityDomain | ||
| ); | ||
| assert_eq!( | ||
| succeeded, scope_is_supported, | ||
| "TBOR request success did not match the key-scope support" | ||
| ); | ||
| } else { | ||
| assert!(!succeeded, "request with an invalid session id succeeded"); | ||
| } | ||
| } | ||
|
|
||
| ctx.session_close(session.session_id) | ||
| .expect("session close should succeed"); | ||
| }); | ||
| }); | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.