Key diversification guidance
darconeous opened this issue · 8 comments
I was considering implementing and filing a pull request to add support for AN10922-style key diversification. Before I start, I have two questions:
- Is support for this even desirable?
- Other than what is in HACKING.md, is there any specific details about how you would want this implemented?
I was thinking it would look something like this:
struct mifare_an10922 {
uint8_t m[31];
uint8_t len;
};
typedef struct mifare_an10922 *MifareAN10922;
MifareAN10922 mifare_an10922_new();
void mifare_an10922_free(MifareAN10922 m);
/* these funcs return false if input doesn't fit, true otherwise */
bool mifare_an10922_append_data(MifareAN10922 m, const uint8_t *data, size_t len);
bool mifare_an10922_append_uid(MifareAN10922 m, FreefareTag tag);
bool mifare_an10922_append_desfire_aid(MifareAN10922 m, MifareDESFireAID aid);
bool mifare_an10922_append_cstr(MifareAN10922 m, const char *cstr);
/* Returns NULL if m is too big */
MifareDESFireKey mifare_an10922_aes128_key_derive(MifareAN10922 m, MifareDESFireKey master_key);
MifareDESFireKey mifare_an10922_2tdea_key_derive(MifareAN10922 m, MifareDESFireKey master_key);
MifareDESFireKey mifare_an10922_3tdea_key_derive(MifareAN10922 m, MifareDESFireKey master_key);
The DESFire EV1 AES-128 card-specific key derivation might look like this:
FreefareTag tag = ...;
MifareDESFireAID aid = ...;
MifareDESFireKey master_key = ...;
MifareAN10922 m = mifare_an10922_new();
/* Build the diversification input */
mifare_an10922_append_uid(m, tag);
mifare_an10922_append_aid(m, aid);
mifare_an10922_append_cstr(m, "sysid");
/* Derive the key */
MifareDESFireKey derived_key = mifare_an10922_aes128_key_derive(m, master_key);
/* Release the diversification input */
mifare_an10922_free(m);
if (mifare_desfire_authenticate_aes(tag, 0, derived_key) < 0) {
goto fail;
}
The methods would use the test vectors from AN10922 for unit tests.
Thoughts?
Any guidance or thoughts?
Hi!
I don't have strong opinions reading this: it's something I have never used. My guess is that if this key diversification mechanism only makes sense in a libfreefare context, then it could be integrated in the libfreefare, but if use cases in totally independent context are possible, maybe a separate library may be a better choice.
If we are in the first case (add this to libfreefare), if this key diversification is always performed in the same way, maybe only providing a new mifare_*derived*_key_new()
function that does everything can make a simpler API, but if some decisions have to be taken by the end-user, a call sequence as you describe looks good to me.
Having unit tests is certainly a best practice. Using the documentation's examples is a perfect minimal unit-tests set. Feel free to add more tests if it can help you 😉.
My guess is that if this key diversification mechanism only makes sense in a libfreefare context, then it could be integrated in the libfreefare, but if use cases in totally independent context are possible, maybe a separate library may be a better choice.
I can't myself answer the question of scope/appropriateness, but I can provide my own reasoning for it being included in libfreefare. AN10922 describes key the derivation mechanisms that are available in the MIFARE SAM AV2. Since the MIFARE SAM AV2 is designed to enable practical and secure deployments of MIFARE cards (and is itself sold under the MIFARE brand) the thinking was that software implementations of the algorithms it supports (if not direct support for the SAM itself) should be within the scope of libfreefare.
The software-based AN10922 key-derivation mechanism I'm proposing allows developers to more easily use key diversification that is compatible with the MIFARE SAM AV2. This makes it easier to migrate to a system that uses a MIFARE SAM later on in development (or to test in software without a SAM), should they decide to do that. It is not intended to be used for general purpose key derivation outside of the context of MIFARE cards.
...if this key diversification is always performed in the same way...
While AN10922 does provide some basic recommendations for what to use for the content of m
(the "message" of the CMAC operation), AN10922 (and, presumably, the MIFARE SAM AV2) allows the application to define what information is present in m
and in what way it is formatted. This is why the proposed API was written the way it was.
I'll give it some more thought on how to possibly make it less clunky, but what I have above might be as good as it gets without significantly limiting what options are available to the developer.
Will likely do something like this instead, so that the master key is associated with the object instead of m
:
typedef struct mifare_an10922 *MifareAN10922;
MifareAN10922 mifare_an10922_new(MifareDESFireKey master_key);
void mifare_an10922_free(MifareAN10922 m);
void mifare_an10922_begin_aes128(MifareAN10922 state);
void mifare_an10922_begin_3des(MifareAN10922 state);
void mifare_an10922_begin_3k3des(MifareAN10922 state);
bool mifare_an10922_update_data(MifareAN10922 m, const uint8_t *data, size_t len);
bool mifare_an10922_update_uid(MifareAN10922 m, FreefareTag tag);
bool mifare_an10922_update_aid(MifareAN10922 m, MifareDESFireAID aid);
bool mifare_an10922_update_cstr(MifareAN10922 m, const char *cstr);
MifareDESFireKey mifare_an10922_end(MifareAN10922 state);
The DESFire EV1 AES-128 card-specific key derivation might look like this:
MifareDESFireKey master_key = ...;
MifareAN10922 derivator = mifare_an10922_new(master_key);
/* ... begin inner loop ... */
FreefareTag tag = ...;
MifareDESFireAID aid = ...;
MifareDESFireKey derived_key = NULL;
int auth_result = -1;
/* Mark start of key derivation. */
mifare_an10922_begin_aes128(derivator);
/* Build the diversification input */
mifare_an10922_update_uid(derivator, tag);
mifare_an10922_update_aid(derivator, aid);
mifare_an10922_update_cstr(derivator, "sysid");
/* Derive the key */
derived_key = mifare_an10922_end(derivator);
/* Attempt authentication with derived key */
auth_result = mifare_desfire_authenticate_aes(tag, 0, derived_key);
/* Free the derived key */
mifare_desfire_key_free(derived_key);
/* Check our authentication */
if (auth_result < 0) {
goto fail;
}
/* ... do stuff ... */
/* ... end inner loop ... */
/* Cleanup derivator */
mifare_an10922_free(derivator);
This structure allows us to allocate a single MifareAN10922
object and re-use it. It also more easily allows for eventually adding hardware support, if that is something that someone eventually wants to implement (would have a different constructor but otherwise have the same API).
Thank you for the details. For me, everything is fine and makes sense with the reasoning. This look like a good addition 👍
Feel free to follow-up if you are unsure about how to do something, but it looks like you proposed API is consistent with the rest of the library's API (opaque types and so on).
Excellent! I'll hopefully have a pull request ready sometime next week.
Note that pull request #79 is the implementation of this feature
#79 has been merged. Closing.