Skip to content
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

Conversion issue with maximum-length APDUs, serialization #6

Closed
mistial-dev opened this issue Jan 3, 2023 · 4 comments
Closed

Conversion issue with maximum-length APDUs, serialization #6

mistial-dev opened this issue Jan 3, 2023 · 4 comments
Assignees

Comments

@mistial-dev
Copy link
Contributor

mistial-dev commented Jan 3, 2023

Sorry to bring this up immediately after a release.

Automatic conversion between short and extended length APDUs is a bit tricky when Le is zero. Per ISO7816, the value has different meanings for short and extended APDUs.

For example, case 2:

Case 2S ⎯ The short Le field consists of C(5) encoding Ne from 1 to 256 ('00' means the maximum, 256). Consequently, n = 5.
Case 2E ⎯ The extended Le field consists of C(5) = '00' and C(6) C(7) encoding Ne from 1 to 65 536 ('0000' means the maximum, 65 536). Consequently, n = 7.

This causes issues with automatic conversion of extended APDUs to non-extended APDUs, as well as intentional conversion from non-extended to extended. Changing an APDU with an Le encoding of zero from short to extended should change the encoded Le to a value of 0x0100, preserving the length. Otherwise, the meaning changes significantly instead of just the case.

An encoding of 0x00 meaning 256 also introduces an off-by one error in some of the testing (in terms of conversion). 0x0100 can be encoded in a single byte.

Additionally, automatic downconversion results in situations where valid APDUs (extended format, small Le) can't be represented, or a deserialized value ends up not serializing to the same value. The handling is a bit inconsistent, which is why the case 3 test didn't catch it, despite being an extended APDU with a short Lc.

private const string Case3EApdu = "00A4 0400 000004 0A0B0C0D";

I have a commit which demonstrates one possible approach to achieving consistency. It keeps extended APDUs as extended, ensures that all valid APDUs can be represented and encode and decode to the same value. It also properly converts between short and extended form while maintaining the semantic meaning. Additionally, exceptions have been added for conversions that can't be performed properly (Le > 0x0101).

https://github.com/mistial-dev/WSCT-Core/commit/16d485111f8b4b4a152f7caa8736b244ffd0875c

I have not put this as a pull request at this point because of one potentially breaking change, specifically in how Le is handled.

ISO7816-4 differentiates between the value of a field and its encoding. A value may be encoded in 0, 1, 2, or 3 bytes depending on the specifics. An Le with a value of zero is encoded as an absent encoded value, and a Le with an encoding of 0x00 has a value of 256. 0x0000 has a value of 65536.

By switching to using the actual value of Le, rather than the one-byte encoded value of Le, it's possible to switch back and forth between extended and non-extended without losing any meaning. This also ensures that a conversion from extended Le with encoded value 0x00 (65536) can fail when an attempt is made to convert it to short form. This also helps with consistency on the constructor, as Le = 0 means a case 1 APDU (which is the standards-compliant encoding for an APDU with no expected length) instead of a case 2 APDU.

The potentially breaking aspect of this approach is that software looking to use the one-byte encoded value in constructors or set commands will end up with a case they may not want. This is not an issue if they set Le explicitly, but is potentially breaking if they assume that setting Lc to 0 actually means maximum.

It's possible to work around this (for example, by automatically converting back and forth, and using flags to internally tell the difference), but it's a bit of a hack and isn't necessary on the extended APDU side, since nothing is using it yet. It doesn't really make sense with extended APDUs either, as a values are encoded multi-byte anyway. Unlike short APDUs, the encoded value and the numerical value will always differ by at least one byte.

@wsct
Copy link
Owner

wsct commented Jan 4, 2023

Don't be sorry, it's interesting to update WSCT to address some new concrete needs.

To sum up:

From the standard:

  • CC2 : Le can be short (with Le='00' for Ne=255+1) or extended (with Le='(00)0000' for Ne=65535+1)
  • CC3 : Lc can be short (with Lc='00' forbidden) or extended (with Lc='000000' forbidden).
  • CC4 : if Lc or Le must be extended, then both must be extended (with the same constraints on Lc and Le than CC2 and CC3)

From your use cases:

  • Automatic upgrade from short CC to extended CC is not a problem
  • Automatic upgrade from extended CC to short CC is a concern for you and should not happen
  • When upgrading from short CC2 or CC4 to extended CC, Le='00' should be upgraded to Le='(00)0100'
  • We must be able to force extended CC while using Nc and Ne <= 255 (as allowed by the standard).

From my constraint:

  • I don't want to introduce a breaking change for existing plugins and use cases using short CC.

Notation based on ISO/IEC 7816:

  • Ne (1 or 2 bytes) is the value encoded in Le (0 to 3 bytes)
  • Nc (1 or 2 bytes) is the value encoded in Lc (0, 1 or 3 bytes)

I'll have a look at your commit today and come back to you.

@wsct
Copy link
Owner

wsct commented Jan 4, 2023

Could you see this commit 3e7d3ee answers all your concerns?

wsct added a commit that referenced this issue Jan 4, 2023
  - unchanged: short C-APDU are automatically upgraded to extended when Nc or Ne is > FF
  - changed: extended C-APDU are never upgraded to short when both Nc and Ne are <= FF
  - new: short C-APDU can be enforced extended even when both Nc and Ne are <= FF (EnforceExtended() method)
  - new: when short CC2 or CC4 with Le='00' (Ne=FF+1) is upgraded to extended because Nc > FF, Le is set to FF+1
@mistial-dev
Copy link
Contributor Author

I’m traveling but will take a look after.

@wsct wsct self-assigned this Jan 19, 2023
@wsct
Copy link
Owner

wsct commented Jan 26, 2023

Hi,
The nuget packages WSCT.Core (6.5.0) and WSCT.Wrapper.Desktop (1.3.1) are now updated with these changes.

@wsct wsct closed this as completed Jan 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants