Skip to content
Merged
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
30 changes: 29 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

[unreleased]

## [1.1.0] - 2026-02-06
### Added
- **Mifare Classic support**: New product types `MIFARE_CLASSIC_1K` and `MIFARE_CLASSIC_4K` in
`ProductType` enum to support NXP Mifare Classic 1K (64 blocks) and 4K (256 blocks) cards.
- **Authentication capability**: New `ProductType.hasAuthentication()` method to indicate whether a
storage card requires authentication before read/write operations.
- **Mifare Classic key types**: New `MifareClassicKeyType` enum with `KEY_A` and `KEY_B` constants
for Mifare Classic authentication.
- **Authentication methods**: New authentication methods for Mifare Classic cards:
- `StorageCardSelectionExtension.prepareMifareClassicAuthenticate(int blockAddress, MifareClassicKeyType, byte[] key)`
- `StorageCardSelectionExtension.prepareMifareClassicAuthenticate(int blockAddress, MifareClassicKeyType, int keyNumber)`
- `StorageCardTransactionManager.prepareMifareClassicAuthenticate(int blockAddress, MifareClassicKeyType, byte[] key)`
- `StorageCardTransactionManager.prepareMifareClassicAuthenticate(int blockAddress, MifareClassicKeyType, int keyNumber)`
- **Authentication exception**: New `SCAuthenticationFailedException` for handling Mifare Classic
authentication failures.
- **ST25-specific system block methods**: New dedicated methods for ST25/SRT512 system block access:
- `StorageCardTransactionManager.prepareSt25ReadSystemBlock()`
- `StorageCardTransactionManager.prepareSt25WriteSystemBlock(byte[] data)`
### Changed
- **System block documentation**: Enhanced `StorageCard.getSystemBlock()` documentation to clarify
it is specific to ST25/SRT512 cards.
### Deprecated
- `StorageCardTransactionManager.prepareReadSystemBlock()` - Use `prepareSt25ReadSystemBlock()`
instead.
- `StorageCardTransactionManager.prepareWriteSystemBlock(byte[])` - Use
`prepareSt25WriteSystemBlock(byte[])` instead.

## [1.0.0] - 2025-11-21
### Changed
- **Exception hierarchy refactoring**: `StorageCardException` transformed from abstract class to interface.
Expand Down Expand Up @@ -70,7 +97,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.1.0] - 2025-06-18
This is the initial release.

[unreleased]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/1.0.0...HEAD
[unreleased]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/1.1.0...HEAD
[1.1.0]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/0.3.0...1.0.0
[0.3.0]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/0.2.0...0.3.0
[0.2.0]: https://github.com/eclipse-keypop/keypop-storagecard-java-api/compare/0.1.0...0.2.0
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
group = org.eclipse.keypop
title = Keypop Storage Card Java API
description = API defining the needed interfaces to manage storage cards
version = 1.0.0-SNAPSHOT
version = 1.1.0-SNAPSHOT

# Java Configuration
javaSourceLevel = 1.8
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* **************************************************************************************
* Copyright (c) 2026 Calypso Networks Association https://calypsonet.org/
*
* See the NOTICE file(s) distributed with this work for additional information
* regarding copyright ownership.
*
* This program and the accompanying materials are made available under the terms of the
* MIT License which is available at https://opensource.org/licenses/MIT
*
* SPDX-License-Identifier: MIT
************************************************************************************** */
package org.eclipse.keypop.storagecard;

/**
* Enumeration of the Mifare Classic key types used for authentication.
*
* <p>Mifare Classic cards support two types of keys per sector for access control: Key A and Key B.
* Each sector can be protected independently using these keys, allowing fine-grained access
* control.
*
* @since 1.1.0
*/
public enum MifareClassicKeyType {

/**
* Key A type.
*
* <p>This is the primary key used for authentication to a Mifare Classic sector. In most
* configurations, Key A has read/write permissions while Key B may have restricted permissions.
*
* @since 1.1.0
*/
KEY_A,

/**
* Key B type.
*
* <p>This is the secondary key used for authentication to a Mifare Classic sector. Key B is often
* used for restricted operations or read-only access, depending on the sector's access
* conditions.
*
* @since 1.1.0
*/
KEY_B
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* **************************************************************************************
* Copyright (c) 2025 Calypso Networks Association https://calypsonet.org/
*
* See the NOTICE file(s) distributed with this work for additional information
* regarding copyright ownership.
*
* This program and the accompanying materials are made available under the terms of the
* MIT License which is available at https://opensource.org/licenses/MIT
*
* SPDX-License-Identifier: MIT
************************************************************************************** */
package org.eclipse.keypop.storagecard;

import org.eclipse.keypop.reader.CardCommunicationException;
import org.eclipse.keypop.storagecard.card.StorageCard;

/**
* Indicates that an authentication attempt on a {@link StorageCard} has failed.
*
* <p>This exception is thrown when authentication to a Mifare Classic sector fails, typically due
* to incorrect key data or key type. Authentication is required before reading from or writing to
* protected sectors on Mifare Classic cards.
*
* @since 1.1.0
*/
public final class SCAuthenticationFailedException extends CardCommunicationException
implements StorageCardException {

private final Integer blockAddress;

/**
* Creates a new exception indicating an authentication failure during the execution of a storage
* card command.
*
* @param blockAddress The block address involved in the error, or {@code null} if not relevant.
* @param message The message describing the exception context.
* @since 1.1.0
*/
public SCAuthenticationFailedException(Integer blockAddress, String message) {
super(message);
this.blockAddress = blockAddress;
}

/**
* Creates a new exception indicating an authentication failure during the execution of a storage
* card command, with an underlying cause.
*
* @param blockAddress The block address involved in the error, or {@code null} if not relevant.
* @param message The message describing the exception context.
* @param cause The underlying cause of the exception.
* @since 1.1.0
*/
public SCAuthenticationFailedException(Integer blockAddress, String message, Throwable cause) {
super(message, cause);
this.blockAddress = blockAddress;
}

/**
* {@inheritDoc}
*
* @since 1.1.0
*/
@Override
public Integer getBlockAddress() {
return blockAddress;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public final class StorageCardApiProperties {
*
* @since 1.0.0
*/
public static final String VERSION = "1.0";
public static final String VERSION = "1.1";

/** Private constructor */
private StorageCardApiProperties() {}
Expand Down
46 changes: 43 additions & 3 deletions src/main/java/org/eclipse/keypop/storagecard/card/ProductType.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,34 @@ public enum ProductType {
*
* @since 1.0.0
*/
MIFARE_ULTRALIGHT(16, 4, false, true),
MIFARE_ULTRALIGHT(16, 4, false, true, false),

/**
* NXP Mifare Classic 1K
*
* @since 1.1.0
*/
MIFARE_CLASSIC_1K(64, 16, false, true, true),

/**
* NXP Mifare Classic 4K
*
* @since 1.1.0
*/
MIFARE_CLASSIC_4K(256, 16, false, true, true),

/**
* ST Microelectronics ST25 / SRT512
*
* @since 1.0.0
*/
ST25_SRT512(16, 4, true, false);
ST25_SRT512(16, 4, true, false, false);

private final int blockCount;
private final int blockSize;
private final boolean hasSystemBlock;
private final boolean hasWriteAcknowledgment;
private final boolean hasAuthentication;

/**
* Constructor.
Expand All @@ -45,13 +60,20 @@ public enum ProductType {
* @param hasSystemBlock Whether this card type has an accessible system block.
* @param hasWriteAcknowledgment Whether this card provides a reliable acknowledgment confirming
* successful write operations.
* @param hasAuthentication Whether this card type requires authentication before read/write
* operations.
*/
ProductType(
int blockCount, int blockSize, boolean hasSystemBlock, boolean hasWriteAcknowledgment) {
int blockCount,
int blockSize,
boolean hasSystemBlock,
boolean hasWriteAcknowledgment,
boolean hasAuthentication) {
this.blockCount = blockCount;
this.blockSize = blockSize;
this.hasSystemBlock = hasSystemBlock;
this.hasWriteAcknowledgment = hasWriteAcknowledgment;
this.hasAuthentication = hasAuthentication;
}

/**
Expand Down Expand Up @@ -108,4 +130,22 @@ public boolean hasSystemBlock() {
public boolean hasWriteAcknowledgment() {
return hasWriteAcknowledgment;
}

/**
* Indicates whether this storage card type requires authentication before read/write operations.
*
* <p>If this method returns {@code true}, the card requires successful authentication to a sector
* before read or write operations can be performed on blocks within that sector. For Mifare
* Classic cards, authentication must be performed using Key A or Key B with the appropriate
* authentication methods.
*
* <p>If this method returns {@code false}, the card allows direct read/write operations without
* prior authentication.
*
* @return {@code true} if the card requires authentication, {@code false} otherwise.
* @since 1.1.0
*/
public boolean hasAuthentication() {
return hasAuthentication;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ public interface StorageCard extends SmartCard {
* Retrieves the system block from the storage card when available.
*
* <p>The system block contains card-specific metadata and configuration data such as access
* control settings. Not all storage card types provide access to system blocks.
* control settings. This feature is specific to ST25/SRT512 cards which provide access to a
* system block at address 255.
*
* <p>The system block must have been previously read using a the {{@link
* StorageCardTransactionManager#prepareReadSystemBlock()}) method.
* <p>The system block must have been previously read using the {@link
* StorageCardTransactionManager#prepareSt25ReadSystemBlock()} method (or the deprecated {@link
* StorageCardTransactionManager#prepareReadSystemBlock()} method).
*
* @return The system block data as a byte array, or null if the system block has not been read
* yet.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
package org.eclipse.keypop.storagecard.card;

import org.eclipse.keypop.reader.selection.spi.CardSelectionExtension;
import org.eclipse.keypop.storagecard.MifareClassicKeyType;

/**
* Extends the {@link CardSelectionExtension} interface of the "Keypop Reader API" to provide means
Expand Down Expand Up @@ -54,4 +55,57 @@ public interface StorageCardSelectionExtension extends CardSelectionExtension {
* @since 1.0.0
*/
StorageCardSelectionExtension prepareReadBlocks(int fromBlockAddress, int toBlockAddress);

/**
* Prepares a Mifare Classic authentication command using a provided key.
*
* <p>This method is specific to Mifare Classic cards and must be called before reading from or
* writing to protected sectors. The authentication applies to the entire sector containing the
* specified block address.
*
* <p>The key must be a 6-byte array representing the Mifare Classic key value.
*
* <p>When the key value is provided this way, it will be sent to the reader to be stored as a
* volatile key at index 0 (see Load Key command of the PC/SC standard). This volatile key is
* temporary and will be erased after usage, when the reader is powered off.
*
* <p><strong>Security Note:</strong> This method transmits the key value over the communication
* channel between the application and the reader. For production environments and
* security-sensitive applications, it is recommended to use {@link
* #prepareMifareClassicAuthenticate(int, MifareClassicKeyType, int)} instead, which references a
* pre-stored key in the reader without transmitting the key value.
*
* @param blockAddress The address of any block within the sector to authenticate.
* @param mifareClassicKeyType The type of key to use (Key A or Key B).
* @param key The 6-byte key data for authentication.
* @return The current instance.
* @throws IllegalArgumentException If the block address is out of range, or if the key is null or
* not exactly 6 bytes long.
* @throws UnsupportedOperationException If the current card type does not support authentication.
* @since 1.1.0
*/
StorageCardSelectionExtension prepareMifareClassicAuthenticate(
int blockAddress, MifareClassicKeyType mifareClassicKeyType, byte[] key);

/**
* Prepares a Mifare Classic authentication command using a key stored in the reader.
*
* <p>This method is specific to Mifare Classic cards and must be called before reading from or
* writing to protected sectors. The authentication applies to the entire sector containing the
* specified block address.
*
* <p>The key is referenced by its storage index in the reader's key storage. This allows using
* pre-configured keys without transmitting them over the communication channel.
*
* @param blockAddress The address of any block within the sector to authenticate.
* @param mifareClassicKeyType The type of key to use (Key A or Key B).
* @param keyNumber The index of the key in the reader's key storage.
* @return The current instance.
* @throws IllegalArgumentException If the block address is out of range, or if the key number is
* invalid.
* @throws UnsupportedOperationException If the current card type does not support authentication.
* @since 1.1.0
*/
StorageCardSelectionExtension prepareMifareClassicAuthenticate(
int blockAddress, MifareClassicKeyType mifareClassicKeyType, int keyNumber);
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/** Contains the factories and builders to create the public elements of the extension. */
/** Contains the factories, builders, and exceptions for the Storage Card API. */
package org.eclipse.keypop.storagecard;
Loading