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

DESFire key diversification does not agree with AN10922 #91

Closed
dodgambit opened this issue Jun 5, 2018 · 8 comments
Closed

DESFire key diversification does not agree with AN10922 #91

dodgambit opened this issue Jun 5, 2018 · 8 comments
Labels

Comments

@dodgambit
Copy link

mifare_key_deriver does not compute the right diversified key when the input is less than the key block size.

This problem will be very common for something like UID-based diversification, since the UID will always be less than the key block size.

For example, AN10922 specifies that the input data for AES128 key diversification input must consist of exactly 32 bytes: 0x01 | M | Padding.

However, if M is, say, 7 bytes long, mifare_key_deriver_end_raw() will only pass 8 bytes (0x01 | M) to the libfreefare cmac() function, which will pad that input to 16 bytes and compute the CMAC of just those 16 bytes.

According to AN10922, the input should have been padded to 32 bytes, so that 2 encryptions are done, flowing through the IV, presumably producing a "more encrypted" or "more difficult to reverse" result.

There is no way to tell the existing cmac() function to pad to 32 bytes in this case when the input is less than 16 bytes. This is only a problem with key diversification -- cmac() pads just fine when computing the CMAC for a secure channel.

  • This problem also exists for DES key derivation when M is < 8 bytes.
  • Conversely if M is >= 32 bytes (AES) or M >= 16 bytes (DES), then too many bytes will be sent to the cmac() function.

Solutions:

  • Document that this is not actually AN10922 key diversification.
  • Document that it is the caller's responsibility to ensure that for AES, M is at least 16 bytes, so that the mifare_key_deriver will pad to 32 bytes, and similarly for DES.
  • Fix the implementation to bring it into compliance with AN10922.
@dodgambit
Copy link
Author

:( It is unfortunately too late for me, since I already have cards deployed with the wrong (possibly less secure?) derivation function.

@dodgambit
Copy link
Author

dodgambit commented Jun 5, 2018

Untested code for correcting cmac(), so mifare_derive_key can pass buffer manually padded to correct length for key but the crypto commands can still use default padding.

void
cmac(const MifareDESFireKey key, uint8_t *ivect, const uint8_t *data, size_t len, bool padded, uint8_t *cmac)
{
    int kbs = key_block_size(key);
    int size = padded_data_length(len, kbs)
    uint8_t *buffer = malloc(size);

    if (!buffer)
	abort();

    memset(buffer, 0, size);
    memcpy(buffer, data, len);

    if ((!len) || (len % kbs)) {
	buffer[len] = 0x80;
        len = size;
        padded = true;
    } 
    if (padded) { 
        // caller provided padded buffer, or cmac() determined buffer needed to be padded
	xor(key->cmac_sk2, buffer + len - kbs, kbs);
    } else {
	xor(key->cmac_sk1, buffer + len - kbs, kbs);
    }

    mifare_cypher_blocks_chained(NULL, key, ivect, buffer, len, MCD_SEND, MCO_ENCYPHER);

    memcpy(cmac, ivect, kbs);

    free(buffer);
}

@smortex
Copy link
Contributor

smortex commented Jun 7, 2018

@dodgambit, thank you for this details report!

I guess the best response to be done is to fix this problem is:

  1. Fix the problem into master (pull-requests are welcome 😉)
  2. Tag a new major version of libfreefare (i.e. 1.0.0, would help Version not changing #81)
  3. Start doing semver (see Provide versioning information nfc-tools.github.io#3)

@smortex smortex added the bug label Jun 7, 2018
@darconeous
Copy link
Member

Well crap. I implemented all of the relevant test vectors from AN10922, and they all passed. It seems that AN10922 didn't include any test vectors with less than one block. The absence of test vectors for 8 bytes or less of diversification data is going to make it difficult to positively confirm that a new version of the diversification is, in fact, correct.

CMAC, when used with AES-128, is secure with only a single message block. I have no idea why they expand it to two for AN10922. CMAC comes with a robust security proof, AN10922 doesn't, so if anything I'd call what was implemented more secure, not less secure. That being said, I don't see any reason why the AN10922 version would actually be less secure, but not using cryptographic primitives exactly as specified is never a good idea.

In other words, I don't think you should worry that what you are using is less secure. It is as secure as CMAC, which has a security proof. Doing an extra round of crypto on padding doesn't increase the security.

I designed the API so that it would be possible to have different constructors for different diversification algorithms. We could simply have a mifare_key_deriver_new_an10922_real() if we want to maintain backward compatibility... Or we could change the behavior of the existing constructor and simply add a mifare_key_deriver_new_an10922_bogus() to ensure that any deployed apps can continue to derive their keys consistently.

What a shame. This could have all been avoided with better test vectors in AN10922.

@darconeous
Copy link
Member

My initial plan is to deprecate mifare_key_deriver_new_an10922() and introduce mifare_key_deriver_new_an10922_real() and mifare_key_deriver_new_cmac(). This will ensure that code that was building against the previous version will fail to compile and force whoever is compiling to investigate why. If they were using less than 8 bytes of diversification data, they could switch to mifare_key_deriver_new_cmac(). Otherwise, they would switch to mifare_key_deriver_new_an10922_real(). Any new code would simply use mifare_key_deriver_new_an10922_real().

But there is a bigger problem, and I've already mentioned it:

The absence of test vectors for 8 bytes or less [in AN10922] of diversification data is going to make it difficult to positively confirm that a new version of the diversification is, in fact, correct.

This seems like a big deal. I could add support for this and just go with the numbers I end up getting and hope I'm correct, but I think @smortex would frown on that.

I suppose I could just try to be super careful. It's a shame that AN10922 didn't include a larger test vector corpus.

@dodgambit, if you don't mind me asking, how did you come to determine this deficiency? Was it a code review, or did you actually encounter a case where the keys didn't match up?

@darconeous
Copy link
Member

Actually, thinking more about it, adding some sort of flags parameter to mifare_key_deriver_new_an10922() might be a better option.

@darconeous
Copy link
Member

It looks like there might be a suitable "official" test vector for the short case in AN10957, pages 13-14:

  • Master Key : 0xf3f9377698707b688eaf84abe39e3791
  • UID : 0x04deadbeeffeed
  • Diversified Key: 0x0bb408baff98b6ee9f2e1585777f6a51

This gives at least allows us to know if the fix is correct.

darconeous added a commit to darconeous/libfreefare that referenced this issue Oct 29, 2019
This commit fixes issue nfc-tools#91.

[AN10922][] specifies the key diversification algorithms used by the
MIFARE SAM AV3. Support for these algorithms was added to
`libfreefare` via pull-request nfc-tools#79.

However, while every attempt was made to write a faithful
implementation, the implemented code did not properly handle cases
where the diversification data was less than or equal to 2x the block
size of the cipher: 16 bytes for AES, and 8 bytes for DES. This
bug was identified in issue nfc-tools#91.

This commit addresses this problem while providing a way to revert to
the previous behavior in cases where it is necessary to maintain
previous deployments. This was accomplished by introducing a new
`flags` parameter to the `mifare_key_deriver_new_an10922` method.

Normally, `flags` should simply be set to `AN10922_FLAG_DEFAULT`.
However, if the previous behavior is required, it should be set to
`AN10922_FLAG_EMULATE_ISSUE_91`.

[AN10922][] does not include any test vectors that might have helped to
identify this problem earlier. However, [AN10957][] (pages 13-14) was
found to have a suitable example usage of [AN10922][] with an
appropriately short value for *M* that we are using as a test vector
to verify correct behavior.

Note that the issue being addressed here is not a security issue:
using the `AN10922_FLAG_EMULATE_ISSUE_91` should not be any less
secure than using `AN10922_FLAG_DEFAULT`.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
[AN10957]: https://www.nxp.com/docs/en/application-note/AN10957.pdf
darconeous added a commit to darconeous/libfreefare that referenced this issue Oct 29, 2019
This commit fixes issue nfc-tools#91.

[AN10922][] specifies the key diversification algorithms used by the
MIFARE SAM AV3. Support for these algorithms was added to
`libfreefare` via pull-request nfc-tools#79.

However, while every attempt was made to write a faithful
implementation, the implemented code did not properly handle cases
where the diversification data was less than or equal to the block
size of the cipher: 16 bytes for AES, and 8 bytes for DES. This
bug was identified in issue nfc-tools#91.

This commit addresses this problem while providing a way to revert to
the previous behavior in cases where it is necessary to maintain
previous deployments. This was accomplished by introducing a new
`flags` parameter to the `mifare_key_deriver_new_an10922` method.

Normally, `flags` should simply be set to `AN10922_FLAG_DEFAULT`.
However, if the previous behavior is required, it should be set to
`AN10922_FLAG_EMULATE_ISSUE_91`.

[AN10922][] does not include any test vectors that might have helped to
identify this problem earlier. However, [AN10957][] (pages 13-14) was
found to have a suitable example usage of [AN10922][] with an
appropriately short value for *M* that we are using as a test vector
to verify correct behavior.

Note that the issue being addressed here is not a security issue:
using the `AN10922_FLAG_EMULATE_ISSUE_91` should not be any less
secure than using `AN10922_FLAG_DEFAULT`.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
[AN10957]: https://www.nxp.com/docs/en/application-note/AN10957.pdf
darconeous added a commit to darconeous/libfreefare that referenced this issue Oct 29, 2019
This commit fixes issue nfc-tools#91.

[AN10922][] specifies the key diversification algorithms used by the
MIFARE SAM AV3. Support for these algorithms was added to
`libfreefare` via pull-request nfc-tools#79.

However, while every attempt was made to write a faithful
implementation, the implemented code did not properly handle cases
where the diversification data was less than or equal to the block
size of the cipher: 16 bytes for AES, and 8 bytes for DES. This
bug was identified in issue nfc-tools#91.

This commit addresses this problem while providing a way to revert to
the previous behavior in cases where it is necessary to maintain
previous deployments. This was accomplished by introducing a new
`flags` parameter to the `mifare_key_deriver_new_an10922` method.

Normally, `flags` should simply be set to `AN10922_FLAG_DEFAULT`.
However, if the previous behavior is required, it should be set to
`AN10922_FLAG_EMULATE_ISSUE_91`.

[AN10922][] does not include any test vectors that might have helped to
identify this problem earlier. However, [AN10957][] (pages 13-14) was
found to have a suitable example usage of [AN10922][] with an
appropriately short value for *M* that we are using as a test vector
to verify correct behavior.

Note that the issue being addressed here is not a security issue:
using the `AN10922_FLAG_EMULATE_ISSUE_91` should not be any less
secure than using `AN10922_FLAG_DEFAULT`.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
[AN10957]: https://www.nxp.com/docs/en/application-note/AN10957.pdf
darconeous added a commit to darconeous/libfreefare that referenced this issue Oct 29, 2019
This commit fixes issue nfc-tools#91.

[AN10922][] specifies the key diversification algorithms used by the
MIFARE SAM AV3. Support for these algorithms was added to
`libfreefare` via pull-request nfc-tools#79.

However, while every attempt was made to write a faithful
implementation, the implemented code did not properly handle cases
where the diversification data was less than or equal to the block
size of the cipher: 16 bytes for AES, and 8 bytes for DES. This
bug was identified in issue nfc-tools#91.

This commit addresses this problem while providing a way to revert to
the previous behavior in cases where it is necessary to maintain
previous deployments. This was accomplished by introducing a new
`flags` parameter to the `mifare_key_deriver_new_an10922` method.

Normally, `flags` should simply be set to `AN10922_FLAG_DEFAULT`.
However, if the previous behavior is required, it should be set to
`AN10922_FLAG_EMULATE_ISSUE_91`.

[AN10922][] does not include any test vectors that might have helped to
identify this problem earlier. However, [AN10957][] (pages 13-14) was
found to have a suitable example usage of [AN10922][] with an
appropriately short value for *M* that we are using as a test vector
to verify correct behavior.

Note that the issue being addressed here is not a security issue:
using the `AN10922_FLAG_EMULATE_ISSUE_91` should not be any less
secure than using `AN10922_FLAG_DEFAULT`.

[AN10922]: https://www.nxp.com/docs/en/application-note/AN10922.pdf
[AN10957]: https://www.nxp.com/docs/en/application-note/AN10957.pdf
@darconeous
Copy link
Member

Fixed by #118.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants