Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
default (5)
- Fix the inverted parsing of the touch requirement in SET MANAGEMENT KEY's P2
(0xFF means no touch, 0xFE means touch required)
- Delete a data object on PUT DATA with an empty object

## [v0.7.0][] (2026-08-17)

Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,11 @@ impl<T: Client> LoadedAuthenticator<'_, T> {
};

use state::ContainerStorage;
// An empty object deletes
if data.is_empty() {
ContainerStorage(container).remove(self.trussed, self.options.storage);
return Ok(());
}
ContainerStorage(container).save(self.trussed, data, self.options.storage)
}

Expand Down
14 changes: 13 additions & 1 deletion src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,8 @@ impl ContainerStorage {

fn default(self) -> Option<Vec<u8, MAX_MESSAGE_LENGTH>> {
match self.0 {
Container::CardHolderUniqueIdentifier => panic!("CHUID should alway be set"),
// Written at initialization, absent only once deleted
Container::CardHolderUniqueIdentifier => None,
Container::CardCapabilityContainer => Some(Vec::from_slice(&CARD_CAP).unwrap()),
Container::DiscoveryObject => Some(Vec::from_slice(&DISCOVERY_OBJECT).unwrap()),
Container::PrintedInformation => Some(Vec::from_slice(&PRINTED_INFORMATION).unwrap()),
Expand Down Expand Up @@ -1250,6 +1251,17 @@ impl ContainerStorage {
}
}

pub fn remove(self, client: &mut impl crate::Client, storage: Location) {
// Already absent is fine
try_syscall!(client.remove_file(storage, self.path())).ok();
if matches!(
self.0.contact_access_rule(),
ReadAccessRule::PinOrOcc | ReadAccessRule::Pin
) {
try_syscall!(client.remove_file(storage, self.path_key())).ok();
}
}

pub fn save(
self,
client: &mut impl crate::Client,
Expand Down
65 changes: 65 additions & 0 deletions tests/command_response.ron
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,71 @@
)
]
),
IoTest(
name: "Empty PUT DATA deletes",
cmd_resp: [
AuthenticateManagement(
key: (
algorithm: Tdes,
key: "0102030405060708 0102030405060708 0102030405060708"
),
mutual: false,
),
PutData(
input: "5C 03 5FC10B 53 04 00010203",
),
GetData(
input: "5C 03 5FC10B",
output: Data("53 04 00010203")
),
PutData(
input: "5C 03 5FC10B 53 00",
),
GetData(
input: "5C 03 5FC10B",
output: All(),
expected_status: NotFound
),
PutData(
input: "5C 03 5FC10D 53 04 00010203",
),
GetData(
input: "5C 03 5FC10C",
output: Data("53 08 C1 01 01 C2 01 00 FE 00")
),
PutData(
input: "5C 03 5FC10D 53 00",
),
GetData(
input: "5C 03 5FC10C",
output: Data("53 08 C1 01 00 C2 01 00 FE 00")
),
PutData(
input: "5C 03 5FC103 53 04 00010203",
),
VerifyApplicationPin(),
GetData(
input: "5C 03 5FC103",
output: Data("53 04 00010203")
),
PutData(
input: "5C 03 5FC103 53 00",
),
GetData(
input: "5C 03 5FC103",
output: All(),
expected_status: NotFound
),
PutData(
input: "5C 03 5FC102 53 00",
),
GetData(
input: "5C 03 5FC102",
output: All(),
expected_status: NotFound
),
]
),
IoTest(
name: "PUT DATA",
cmd_resp: [
Expand Down
Loading