diff --git a/client/src/mifare/desfiretest.c b/client/src/mifare/desfiretest.c index 174d8bd71..8a2c54fc7 100644 --- a/client/src/mifare/desfiretest.c +++ b/client/src/mifare/desfiretest.c @@ -16,6 +16,7 @@ #include "crypto/libpcrypto.h" #include "mifare/desfirecrypto.h" +#include "mifare/lrpcrypto.h" static uint8_t CMACData[] = {0x6B, 0xC1, 0xBE, 0xE2, 0x2E, 0x40, 0x9F, 0x96, 0xE9, 0x3D, 0x7E, 0x11, 0x73, 0x93, 0x17, 0x2A, @@ -474,6 +475,47 @@ static bool TestTransSessionKeys(void) { return res; } +// https://www.nxp.com/docs/en/application-note/AN12304.pdf +// page 10 +static bool TestLRPPlaintexts(void) { + bool res = true; + + uint8_t key[] = {0x56, 0x78, 0x26, 0xB8, 0xDA, 0x8E, 0x76, 0x84, 0x32, 0xA9, 0x54, 0x8D, 0xBE, 0x4A, 0xA3, 0xA0}; + LRPContext ctx = {0}; + LRPSetKey(&ctx, key, 0, false); + + uint8_t key0[] = {0xAC, 0x20, 0xD3, 0x9F, 0x53, 0x41, 0xFE, 0x98, 0xDF, 0xCA, 0x21, 0xDA, 0x86, 0xBA, 0x79, 0x14}; + res = res && (memcmp(ctx.plaintexts[0], key0, sizeof(key0)) == 0); + + uint8_t key1[] = {0x90, 0x7D, 0xA0, 0x3D, 0x67, 0x24, 0x49, 0x16, 0x69, 0x15, 0xE4, 0x56, 0x3E, 0x08, 0x9D, 0x6D}; + res = res && (memcmp(ctx.plaintexts[1], key1, sizeof(key1)) == 0); + + uint8_t key14[] = {0x37, 0xD7, 0x34, 0xA5, 0x1C, 0x07, 0x6E, 0xB8, 0x03, 0xBD, 0x53, 0x0E, 0x17, 0xEB, 0x87, 0xDC}; + res = res && (memcmp(ctx.plaintexts[14], key14, sizeof(key14)) == 0); + + uint8_t key15[] = {0x71, 0xB4, 0x44, 0xAF, 0x25, 0x7A, 0x93, 0x21, 0x53, 0x11, 0xD7, 0x58, 0xDD, 0x33, 0x32, 0x47}; + res = res && (memcmp(ctx.plaintexts[15], key15, sizeof(key15)) == 0); + + if (res) + PrintAndLogEx(INFO, "LRP plaintexts.... " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "LRP plaintexts.... " _RED_("fail")); + + return res; +} + +static bool TestLRPUpdatedKeys(void) { + bool res = true; + + + if (res) + PrintAndLogEx(INFO, "LRP updated keys.. " _GREEN_("passed")); + else + PrintAndLogEx(ERR, "LRP updated keys.. " _RED_("fail")); + + return res; +} + bool DesfireTest(bool verbose) { bool res = true; @@ -492,6 +534,8 @@ bool DesfireTest(bool verbose) { res = res && TestEV2IVEncode(); res = res && TestEV2MAC(); res = res && TestTransSessionKeys(); + res = res && TestLRPPlaintexts(); + res = res && TestLRPUpdatedKeys(); PrintAndLogEx(INFO, "---------------------------"); if (res) diff --git a/client/src/mifare/lrpcrypto.c b/client/src/mifare/lrpcrypto.c index 3f656d396..94f2b36c1 100644 --- a/client/src/mifare/lrpcrypto.c +++ b/client/src/mifare/lrpcrypto.c @@ -26,3 +26,62 @@ #include "aes.h" #include "commonutil.h" +static uint8_t constAA[] = {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa}; +static uint8_t const55[] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}; + +void LRPClearContext(LRPContext *ctx) { + memset(ctx->key, 0, CRYPTO_AES128_KEY_SIZE); + + ctx->useBitPadding = false; + ctx->plaintextsCount = 0; + memset(ctx->plaintexts, 0, LRP_MAX_PLAINTEXTS_SIZE * CRYPTO_AES128_KEY_SIZE); + ctx->updatedKeysCount = 0; + memset(ctx->updatedKeys, 0, LRP_MAX_UPDATED_KEYS_SIZE * CRYPTO_AES128_KEY_SIZE); + ctx->useUpdatedKeyNum = 0; +} + +void LRPSetKey(LRPContext *ctx, uint8_t *key, size_t updatedKeyNum, bool useBitPadding) { + LRPClearContext(ctx); + + memcpy(ctx->key, key, CRYPTO_AES128_KEY_SIZE); + + LRPGeneratePlaintexts(ctx, 16); + LRPGenerateUpdatedKeys(ctx, 4); + + ctx->useUpdatedKeyNum = updatedKeyNum; + ctx->useBitPadding = useBitPadding; +} + +// https://www.nxp.com/docs/en/application-note/AN12304.pdf +// Algorithm 1 +void LRPGeneratePlaintexts(LRPContext *ctx, size_t plaintextsCount) { + if (plaintextsCount > LRP_MAX_PLAINTEXTS_SIZE) + return; + + uint8_t h[CRYPTO_AES128_KEY_SIZE] = {0}; + memcpy(h, ctx->key, CRYPTO_AES128_KEY_SIZE); + + for (int i = 0; i < plaintextsCount; i++) { + aes_encode(NULL, h, const55, h, CRYPTO_AES128_KEY_SIZE); + aes_encode(NULL, h, constAA, ctx->plaintexts[i], CRYPTO_AES128_KEY_SIZE); + } + + ctx->plaintextsCount = plaintextsCount; +} + +// https://www.nxp.com/docs/en/application-note/AN12304.pdf +// Algorithm 2 +void LRPGenerateUpdatedKeys(LRPContext *ctx, size_t updatedKeysCount) { + if (updatedKeysCount > LRP_MAX_UPDATED_KEYS_SIZE) + return; + + uint8_t h[CRYPTO_AES128_KEY_SIZE] = {0}; + aes_encode(NULL, ctx->key, const55, h, CRYPTO_AES128_KEY_SIZE); + + for (int i = 0; i < updatedKeysCount; i++) { + aes_encode(NULL, h, constAA, ctx->plaintexts[i], CRYPTO_AES128_KEY_SIZE); + aes_encode(NULL, h, const55, h, CRYPTO_AES128_KEY_SIZE); + } + + ctx->updatedKeysCount = updatedKeysCount; +} diff --git a/client/src/mifare/lrpcrypto.h b/client/src/mifare/lrpcrypto.h index 25369b36d..12e6a40e7 100644 --- a/client/src/mifare/lrpcrypto.h +++ b/client/src/mifare/lrpcrypto.h @@ -23,11 +23,24 @@ #include "common.h" #include "crypto/libpcrypto.h" +#define LRP_MAX_PLAINTEXTS_SIZE 16 +#define LRP_MAX_UPDATED_KEYS_SIZE 4 + typedef struct { uint8_t key[CRYPTO_AES128_KEY_SIZE]; + + bool useBitPadding; + size_t plaintextsCount; + uint8_t plaintexts[LRP_MAX_PLAINTEXTS_SIZE][CRYPTO_AES128_KEY_SIZE]; + size_t updatedKeysCount; + uint8_t updatedKeys[LRP_MAX_UPDATED_KEYS_SIZE][CRYPTO_AES128_KEY_SIZE]; + size_t useUpdatedKeyNum; } LRPContext; -void LRPSetKey(LRPContext *ctx); +void LRPClearContext(LRPContext *ctx); +void LRPSetKey(LRPContext *ctx, uint8_t *key, size_t updatedKeyNum, bool useBitPadding); +void LRPGeneratePlaintexts(LRPContext *ctx, size_t plaintextsCount); +void LRPGenerateUpdatedKeys(LRPContext *ctx, size_t updatedKeysCount); #endif // __LRPCRYPTO_H