From 689f19b33ef5e33697692c3b4c9dbf127727df32 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Sat, 5 Feb 2022 16:49:25 +0200 Subject: [PATCH 01/10] commands --- client/src/cipurse/cipursecore.c | 8 ++++++++ client/src/cipurse/cipursecore.h | 3 +++ 2 files changed, 11 insertions(+) diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 855e1e56d..cfeddf2af 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -212,6 +212,14 @@ int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_ return CIPURSEExchange((sAPDU_t) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, result, max_result_len, result_len, sw); } +int CIPURSEUpdateKeyAttrib(uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x4e, 0, key_attrib, 0, NULL}, false, 0, result, max_result_len, result_len, sw); +} + +int CIPURSEUpdateKey(uint8_t encryt_key_num, uint8_t udate_key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0xd6, encryt_key_num, udate_key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw); +} + int CIPURSECommitTransaction(uint16_t *sw) { uint8_t result[APDU_RES_LEN] = {0}; size_t result_len = 0; diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h index 89af5e5bf..ee6231c93 100644 --- a/client/src/cipurse/cipursecore.h +++ b/client/src/cipurse/cipursecore.h @@ -55,6 +55,9 @@ int CIPURSEUpdateFileAttributes(uint8_t *data, uint16_t datalen, uint8_t *result int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateKeyAttrib(uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateKey(uint8_t encryt_key_num, uint8_t udate_key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); + int CIPURSECommitTransaction(uint16_t *sw); int CIPURSECancelTransaction(uint16_t *sw); From 90d20fd80edec93c64a961930990375837bb6ac2 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Sat, 5 Feb 2022 17:03:08 +0200 Subject: [PATCH 02/10] command sketches and low level fix --- client/src/cipurse/cipursecore.c | 8 ++++---- client/src/cipurse/cipursecore.h | 4 ++-- client/src/cmdhfcipurse.c | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index cfeddf2af..5fbf5bdd6 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -212,12 +212,12 @@ int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_ return CIPURSEExchange((sAPDU_t) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, result, max_result_len, result_len, sw); } -int CIPURSEUpdateKeyAttrib(uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x4e, 0, key_attrib, 0, NULL}, false, 0, result, max_result_len, result_len, sw); +int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x4e, 0x00, key_num, 1, &key_attrib}, false, 0, result, max_result_len, result_len, sw); } -int CIPURSEUpdateKey(uint8_t encryt_key_num, uint8_t udate_key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0xd6, encryt_key_num, udate_key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw); +int CIPURSEUpdateKey(uint8_t encrypt_key_num, uint8_t key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { + return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0xd6, encrypt_key_num, key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw); } int CIPURSECommitTransaction(uint16_t *sw) { diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h index ee6231c93..b6f8f9bcb 100644 --- a/client/src/cipurse/cipursecore.h +++ b/client/src/cipurse/cipursecore.h @@ -55,8 +55,8 @@ int CIPURSEUpdateFileAttributes(uint8_t *data, uint16_t datalen, uint8_t *result int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSEUpdateKeyAttrib(uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSEUpdateKey(uint8_t encryt_key_num, uint8_t udate_key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateKey(uint8_t encrypt_key_num, uint8_t key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSECommitTransaction(uint16_t *sw); int CIPURSECancelTransaction(uint16_t *sw); diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 44475bf55..1cd756d7a 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -1402,6 +1402,20 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { return PM3_SUCCESS; } +// {"updkey", CmdHFCipurseUpdateKey, IfPm3Iso14443a, "Update key"}, +static int CmdHFCipurseUpdateKey(const char *Cmd) { + + DropField(); + return PM3_SUCCESS; +} + +// {"updakey", CmdHFCipurseUpdateKeyAttr, IfPm3Iso14443a, "Update key attributes"}, +static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { + + DropField(); + return PM3_SUCCESS; +} + bool CheckCardCipurse(void) { uint8_t buf[APDU_RES_LEN] = {0}; size_t len = 0; @@ -1491,6 +1505,8 @@ static command_t CommandTable[] = { {"formatall", CmdHFCipurseFormatAll, IfPm3Iso14443a, "Erase all the data from chip"}, {"create", CmdHFCipurseCreateDGI, IfPm3Iso14443a, "Create file, application, key via DGI record"}, {"delete", CmdHFCipurseDeleteFile, IfPm3Iso14443a, "Delete file"}, + {"updkey", CmdHFCipurseUpdateKey, IfPm3Iso14443a, "Update key"}, + {"updakey", CmdHFCipurseUpdateKeyAttr, IfPm3Iso14443a, "Update key attributes"}, {"default", CmdHFCipurseDefault, IfPm3Iso14443a, "Set default key and file id for all the other commands"}, {"test", CmdHFCipurseTest, AlwaysAvailable, "Tests"}, {NULL, NULL, 0, NULL} From 1482a9b7fca8fe02f45a25664d7903509b2a3813 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Sat, 5 Feb 2022 23:49:44 +0200 Subject: [PATCH 03/10] make global print sec attr --- client/src/cipurse/cipursecore.c | 2 +- client/src/cipurse/cipursecore.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 5fbf5bdd6..fa0580381 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -425,7 +425,7 @@ void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen) { } } -static void CIPURSEPrintKeySecurityAttributes(uint8_t attr) { +void CIPURSEPrintKeySecurityAttributes(uint8_t attr) { PrintAndLogEx(INFO, " Update right: %s", (attr & 0x01) ? "self" : "any"); PrintAndLogEx(INFO, " Change key and rights: %s", (attr & 0x02) ? "ok" : "frozen"); PrintAndLogEx(INFO, " Use as key encryption key: %s", (attr & 0x04) ? "blocked" : "ok"); diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h index b6f8f9bcb..584fc8ea6 100644 --- a/client/src/cipurse/cipursecore.h +++ b/client/src/cipurse/cipursecore.h @@ -55,8 +55,8 @@ int CIPURSEUpdateFileAttributes(uint8_t *data, uint16_t datalen, uint8_t *result int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); -int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSEUpdateKey(uint8_t encrypt_key_num, uint8_t key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); +int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw); int CIPURSECommitTransaction(uint16_t *sw); int CIPURSECancelTransaction(uint16_t *sw); @@ -74,5 +74,6 @@ void CIPURSEPrintFileUpdateAttr(uint8_t *attr, size_t len); void CIPURSEPrintFileDescriptor(uint8_t desc); void CIPURSEPrintDGIArray(uint8_t *dgi, size_t dgilen); void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen); +void CIPURSEPrintKeySecurityAttributes(uint8_t attr); #endif /* __CIPURSECORE_H__ */ From e8bf48c54e9824532a5e549f7845a698519d55db Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Sun, 6 Feb 2022 01:05:39 +0200 Subject: [PATCH 04/10] set key attr ok --- client/src/cmdhfcipurse.c | 146 +++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 1cd756d7a..9241a084e 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -73,6 +73,13 @@ static const APDUSpcCodeDescription_t DeleteAPDUCodeDescriptions[] = { {0x6A82, "File not found" } }; +static const APDUSpcCodeDescription_t UAPDpdateKeyAttrCodeDescriptions[] = { + {0x6581, "Transaction mechanism capabilities exceeded" }, + {0x6982, "Key is frozen or only the key itself has the rights to update" }, + {0x6985, "Deactivated file" }, + {0x6A88, "Invalid key number (outside the range supported by the current DF)" } +}; + static uint8_t defaultKeyId = 1; static uint8_t defaultKey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY; #define CIPURSE_MAX_AID_LENGTH 16 @@ -1409,9 +1416,144 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { return PM3_SUCCESS; } -// {"updakey", CmdHFCipurseUpdateKeyAttr, IfPm3Iso14443a, "Update key attributes"}, static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { - + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cipurse updakey", + "Update key attributes. Factory default - 0x02.\n" + "b0 - Update right - 1 self\n" + "b1 - Change key and rights - 0 frozen\n" + "b2 - Use as key encryption key - 1 blocked\n" + "b8 - Key validity - 0 valid", + "hf cipurse updakey --trgkey 2 --attr 80 -> block key 2 for lifetime (WARNING!)\n" + "hf cipurse updakey --trgkey 1 --attr 02 --commit -> for key 1"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", NULL, "", "key ID for authentication"), + arg_str0("k", "key", "", "Auth key"), + + arg_str0(NULL, "aid", "", "application ID (AID)"), + arg_str0(NULL, "fid", "", "file ID (FID)"), + arg_lit0(NULL, "mfd", "select masterfile by empty id"), + + arg_int0(NULL, "trgkey", "", "target key ID"), + arg_str0(NULL, "attr", "", "key attributes 1 byte"), + arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), + arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_lit0(NULL, "commit", "commit "), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); + + CipurseChannelSecurityLevel sreq = CPSMACed; + CipurseChannelSecurityLevel sresp = CPSMACed; + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; + + uint8_t aid[16] = {0}; + size_t aidLen = 0; + bool useAID = false; + uint16_t fileId = defaultFileId; + bool useFID = false; + int res = CLIParseCommandParameters(ctx, 4, 5, 6, 10, 11, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool selmfd = arg_get_lit(ctx, 7); + + uint8_t trgKeyId = arg_get_int_def(ctx, 8, defaultKeyId); + + uint8_t hdata[250] = {0}; + int hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 9, hdata, &hdatalen); + if (hdatalen != 1) { + PrintAndLogEx(ERR, _RED_("ERROR:") " key attributes must be 1 bytes only and must be specified."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool noauth = arg_get_lit(ctx, 12); + bool needCommit = arg_get_lit(ctx, 13); + + CLIParserFree(ctx); + SetAPDULogging(APDULogging); + + if (verbose && hdatalen == 1) { + PrintAndLogEx(INFO, "Decoded attributes:"); + CIPURSEPrintKeySecurityAttributes(hdata[0]); + PrintAndLogEx(NORMAL, ""); + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + + if (useAID || useFID || selmfd) { + res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); + DropField(); + return PM3_ESOFT; + } + } else { + res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); + DropField(); + return PM3_ESOFT; + } + if (verbose) + PrintAndLogEx(INFO, "Cipurse masterfile " _GREEN_("selected")); + } + + if (verbose) { + if (!noauth) + PrintAndLogEx(INFO, "key id " _YELLOW_("%d") " key " _YELLOW_("%s") + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); + } + + if (!noauth) { + bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); + if (bres == false) { + if (verbose) + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); + DropField(); + return PM3_ESOFT; + } + + // set channel security levels + CIPURSECSetActChannelSecurityLevels(sreq, sresp); + } + + res = CIPURSEUpdateKeyAttrib(trgKeyId, hdata[0], buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Update key attributes command " _RED_("ERROR") ". Card returns:\n 0x%04x - %s", sw, + GetSpecificAPDUCodeDesc(UAPDpdateKeyAttrCodeDescriptions, ARRAYLEN(UAPDpdateKeyAttrCodeDescriptions), sw)); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Key attributes updated " _GREEN_("succesfully")); + + if (needCommit) { + sw = 0; + res = CIPURSECommitTransaction(&sw); + if (res != 0 || sw != 0x9000) + PrintAndLogEx(WARNING, "Commit " _YELLOW_("ERROR") ". Card returns 0x%04x", sw); + + if (verbose) + PrintAndLogEx(INFO, "Commit " _GREEN_("OK")); + } + DropField(); return PM3_SUCCESS; } From bb1257919b90277ad357e0182acd2419411f9b22 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Sun, 6 Feb 2022 01:05:51 +0200 Subject: [PATCH 05/10] doc --- doc/cipurse.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/cipurse.md b/doc/cipurse.md index cf5a5cafd..2573a6dc5 100644 --- a/doc/cipurse.md +++ b/doc/cipurse.md @@ -260,7 +260,13 @@ or if file with transaction mechanism **8. Set the keys and needed key attributes** -TBW +keys for application usually filled at create time with DGI `A00F` + +keys for masterfile needs to be updated manually with command: +```hf cipurse updakey``` + +update key attributes with default attributes +```hf cipurse updakey --aid 4144204631 --trgkey 1 --attr 02 -v --commit``` **8. Set the file attributes** From d34d28367332ce6d8907f1323c5633c123e8bdad Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Mon, 7 Feb 2022 23:52:20 +0200 Subject: [PATCH 06/10] fix doc --- doc/cipurse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cipurse.md b/doc/cipurse.md index 2573a6dc5..6fa57b403 100644 --- a/doc/cipurse.md +++ b/doc/cipurse.md @@ -266,7 +266,7 @@ keys for masterfile needs to be updated manually with command: ```hf cipurse updakey``` update key attributes with default attributes -```hf cipurse updakey --aid 4144204631 --trgkey 1 --attr 02 -v --commit``` +```hf cipurse updakey --aid 4144204631 --trgkeyn 1 --attr 02 -v --commit``` **8. Set the file attributes** From bc88040967b9e061203ef572a61892886d3aeef9 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 8 Feb 2022 00:25:52 +0200 Subject: [PATCH 07/10] plain update key works --- client/src/cipurse/cipursecore.c | 2 +- client/src/cmdhfcipurse.c | 184 ++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 6 deletions(-) diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index fa0580381..41bce0eb5 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -217,7 +217,7 @@ int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, } int CIPURSEUpdateKey(uint8_t encrypt_key_num, uint8_t key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) { - return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0xd6, encrypt_key_num, key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw); + return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x52, encrypt_key_num, key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw); } int CIPURSECommitTransaction(uint16_t *sw) { diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 9f462e40b..33bd40c6b 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -80,6 +80,14 @@ static const APDUSpcCodeDescription_t UAPDpdateKeyAttrCodeDescriptions[] = { {0x6A88, "Invalid key number (outside the range supported by the current DF)" } }; +static const APDUSpcCodeDescription_t UAPDpdateKeyCodeDescriptions[] = { + {0x6982, "Key is frozen or only the key itself has the rights to update" }, + {0x6984, "key enc key is blocked or invalid" }, + {0x6985, "Deactivated file" }, + {0x6A80, "invalid algo, key length or kvv" }, + {0x6A88, "Invalid key number (outside the range supported by the current DF)" } +}; + static uint8_t defaultKeyId = 1; static uint8_t defaultKey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY; #define CIPURSE_MAX_AID_LENGTH 16 @@ -1436,8 +1444,169 @@ static int CmdHFCipurseDeleteFile(const char *Cmd) { return PM3_SUCCESS; } -// {"updkey", CmdHFCipurseUpdateKey, IfPm3Iso14443a, "Update key"}, static int CmdHFCipurseUpdateKey(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf cipurse updakey", + "Update key. ", + "hf cipurse updkey --aid 4144204631 --newkeyn 2 --newkeya 00 --newkey 73737373737373737373737373737373 -> update default application key 2 with default value 73..73\n" + "hf cipurse updkey --newkeyn 1 --newkeya 00 --newkey 0102030405060708090a0b0c0d0e0f10 --commit -> for key 1"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("a", "apdu", "show APDU requests and responses"), + arg_lit0("v", "verbose", "show technical data"), + arg_int0("n", NULL, "", "key ID for authentication"), + arg_str0("k", "key", "", "Auth key"), + + arg_str0(NULL, "aid", "", "application ID (AID)"), + arg_str0(NULL, "fid", "", "file ID (FID)"), + arg_lit0(NULL, "mfd", "select masterfile by empty id"), + + arg_int0(NULL, "newkeyn", "", "target key ID"), + arg_str0(NULL, "newkey", "", "new key"), + arg_str0(NULL, "newkeya", "", "new key additional info. 0x00 by default"), + + arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), + arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), + arg_lit0(NULL, "no-auth", "execute without authentication"), + arg_lit0(NULL, "commit", "commit "), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + bool APDULogging = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); + uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId); + + CipurseChannelSecurityLevel sreq = CPSMACed; + CipurseChannelSecurityLevel sresp = CPSMACed; + uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0}; + + uint8_t aid[16] = {0}; + size_t aidLen = 0; + bool useAID = false; + uint16_t fileId = defaultFileId; + bool useFID = false; + int res = CLIParseCommandParameters(ctx, 4, 5, 6, 11, 12, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool selmfd = arg_get_lit(ctx, 7); + + uint8_t newKeyId = arg_get_int_def(ctx, 8, 0); + if (newKeyId == 0) { + PrintAndLogEx(ERR, _RED_("ERROR:") " new key id must be specified."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t hdata[250] = {0}; + int hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 9, hdata, &hdatalen); + if (hdatalen != 16) { + PrintAndLogEx(ERR, _RED_("ERROR:") " new key must be 16 bytes only and must be specified."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t newKey[16] = {0}; + memcpy(newKey, hdata, 16); + + hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen); + if (hdatalen && hdatalen != 1) { + PrintAndLogEx(ERR, _RED_("ERROR:") " new key additional info must be 1 byte only."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t newKeyAInfo = (hdatalen) ? hdata[0] : 0x00; + + bool noauth = arg_get_lit(ctx, 13); + bool needCommit = arg_get_lit(ctx, 14); + + CLIParserFree(ctx); + SetAPDULogging(APDULogging); + + uint8_t kvv[CIPURSE_KVV_LENGTH] = {0}; + CipurseCGetKVV(newKey, kvv); + + uint8_t keydata[3 + 16 + 3] = {newKeyAInfo, 0x10, 0x09, 0x00}; + memcpy(&keydata[3], newKey, 16); + memcpy(&keydata[3 + 16], kvv, 3); + + if (verbose) { + PrintAndLogEx(INFO, "New key number: %d", newKeyId); + PrintAndLogEx(INFO, "New key additional info: 0x%02x", newKeyAInfo); + PrintAndLogEx(INFO, "New key: %s", sprint_hex_inrow(key, 16)); + PrintAndLogEx(INFO, "New key kvv: %s", sprint_hex_inrow(kvv, 3)); + PrintAndLogEx(INFO, "New key data: %s", sprint_hex_inrow(keydata, sizeof(keydata))); + PrintAndLogEx(NORMAL, ""); + } + + uint8_t buf[APDU_RES_LEN] = {0}; + size_t len = 0; + uint16_t sw = 0; + + if (useAID || useFID || selmfd) { + res = SelectCommand(selmfd, useAID, aid, aidLen, useFID, fileId, verbose, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Select command ( " _RED_("error") " )"); + DropField(); + return PM3_ESOFT; + } + } else { + res = CIPURSESelectMFEx(true, true, buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Cipurse masterfile select " _RED_("error") ". Card returns 0x%04x", sw); + DropField(); + return PM3_ESOFT; + } + if (verbose) + PrintAndLogEx(INFO, "Cipurse masterfile " _GREEN_("selected")); + } + + if (verbose) { + if (!noauth) + PrintAndLogEx(INFO, "key id " _YELLOW_("%d") " key " _YELLOW_("%s") + , keyId + , sprint_hex(key, CIPURSE_AES_KEY_LENGTH) + ); + } + + if (!noauth) { + bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose); + if (bres == false) { + if (verbose) + PrintAndLogEx(ERR, "Authentication ( " _RED_("fail") " )"); + DropField(); + return PM3_ESOFT; + } + + // set channel security levels + CIPURSECSetActChannelSecurityLevels(sreq, sresp); + } + + res = CIPURSEUpdateKey(0, newKeyId, keydata, sizeof(keydata), buf, sizeof(buf), &len, &sw); + if (res != 0 || sw != 0x9000) { + PrintAndLogEx(ERR, "Update key command " _RED_("ERROR") ". Card returns:\n 0x%04x - %s", sw, + GetSpecificAPDUCodeDesc(UAPDpdateKeyCodeDescriptions, ARRAYLEN(UAPDpdateKeyCodeDescriptions), sw)); + DropField(); + return PM3_ESOFT; + } + PrintAndLogEx(INFO, "Key updated " _GREEN_("succesfully")); + + if (needCommit) { + sw = 0; + res = CIPURSECommitTransaction(&sw); + if (res != 0 || sw != 0x9000) + PrintAndLogEx(WARNING, "Commit " _YELLOW_("ERROR") ". Card returns 0x%04x", sw); + + if (verbose) + PrintAndLogEx(INFO, "Commit " _GREEN_("OK")); + } DropField(); return PM3_SUCCESS; @@ -1451,8 +1620,8 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { "b1 - Change key and rights - 0 frozen\n" "b2 - Use as key encryption key - 1 blocked\n" "b8 - Key validity - 0 valid", - "hf cipurse updakey --trgkey 2 --attr 80 -> block key 2 for lifetime (WARNING!)\n" - "hf cipurse updakey --trgkey 1 --attr 02 --commit -> for key 1"); + "hf cipurse updakey --trgkeyn 2 --attr 80 -> block key 2 for lifetime (WARNING!)\n" + "hf cipurse updakey --trgkeyn 1 --attr 02 --commit -> for key 1"); void *argtable[] = { arg_param_begin, @@ -1465,7 +1634,7 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { arg_str0(NULL, "fid", "", "file ID (FID)"), arg_lit0(NULL, "mfd", "select masterfile by empty id"), - arg_int0(NULL, "trgkey", "", "target key ID"), + arg_int0(NULL, "trgkeyn", "", "target key ID"), arg_str0(NULL, "attr", "", "key attributes 1 byte"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), @@ -1496,7 +1665,12 @@ static int CmdHFCipurseUpdateKeyAttr(const char *Cmd) { bool selmfd = arg_get_lit(ctx, 7); - uint8_t trgKeyId = arg_get_int_def(ctx, 8, defaultKeyId); + uint8_t trgKeyId = arg_get_int_def(ctx, 8, 0); + if (trgKeyId == 0) { + PrintAndLogEx(ERR, _RED_("ERROR:") " target key id must be specified."); + CLIParserFree(ctx); + return PM3_EINVARG; + } uint8_t hdata[250] = {0}; int hdatalen = sizeof(hdata); From 0e5382857df86d493b7e145dac515b33a83088ff Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 8 Feb 2022 01:07:03 +0200 Subject: [PATCH 08/10] added encode key --- client/src/cmdhfcipurse.c | 40 ++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 33bd40c6b..3a8e49244 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -39,6 +39,7 @@ #include "cmdtrace.h" #include "util.h" #include "fileutils.h" // laodFileJSONroot +#include "crypto/libpcrypto.h" const uint8_t PxSE_AID[] = {0xA0, 0x00, 0x00, 0x05, 0x07, 0x01, 0x00}; #define PxSE_AID_LENGTH 7 @@ -1466,6 +1467,9 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { arg_str0(NULL, "newkey", "", "new key"), arg_str0(NULL, "newkeya", "", "new key additional info. 0x00 by default"), + arg_int0(NULL, "enckeyn", "", "encrypt key ID (must be equal to the key on the card)"), + arg_str0(NULL, "enckey", "", "encrypt key (must be equal to the key on the card)"), + arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), arg_lit0(NULL, "no-auth", "execute without authentication"), @@ -1487,7 +1491,7 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { bool useAID = false; uint16_t fileId = defaultFileId; bool useFID = false; - int res = CLIParseCommandParameters(ctx, 4, 5, 6, 11, 12, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp); + int res = CLIParseCommandParameters(ctx, 4, 5, 6, 13, 14, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp); if (res) { CLIParserFree(ctx); return PM3_EINVARG; @@ -1511,8 +1515,8 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { return PM3_EINVARG; } - uint8_t newKey[16] = {0}; - memcpy(newKey, hdata, 16); + uint8_t newKey[CIPURSE_AES_KEY_LENGTH] = {0}; + memcpy(newKey, hdata, CIPURSE_AES_KEY_LENGTH); hdatalen = sizeof(hdata); CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen); @@ -1524,8 +1528,22 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { uint8_t newKeyAInfo = (hdatalen) ? hdata[0] : 0x00; - bool noauth = arg_get_lit(ctx, 13); - bool needCommit = arg_get_lit(ctx, 14); + uint8_t encKeyId = arg_get_int_def(ctx, 11, 0); + + hdatalen = sizeof(hdata); + CLIGetHexWithReturn(ctx, 12, hdata, &hdatalen); + if (hdatalen && hdatalen != 16) { + PrintAndLogEx(ERR, _RED_("ERROR:") " encode key must be 16 bytes only and must be specified."); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t encKey[CIPURSE_AES_KEY_LENGTH] = CIPURSE_DEFAULT_KEY; + if (hdatalen) + memcpy(encKey, hdata, CIPURSE_AES_KEY_LENGTH); + + bool noauth = arg_get_lit(ctx, 15); + bool needCommit = arg_get_lit(ctx, 16); CLIParserFree(ctx); SetAPDULogging(APDULogging); @@ -1540,9 +1558,17 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { if (verbose) { PrintAndLogEx(INFO, "New key number: %d", newKeyId); PrintAndLogEx(INFO, "New key additional info: 0x%02x", newKeyAInfo); - PrintAndLogEx(INFO, "New key: %s", sprint_hex_inrow(key, 16)); + PrintAndLogEx(INFO, "New key: %s", sprint_hex_inrow(newKey, 16)); PrintAndLogEx(INFO, "New key kvv: %s", sprint_hex_inrow(kvv, 3)); PrintAndLogEx(INFO, "New key data: %s", sprint_hex_inrow(keydata, sizeof(keydata))); + if (encKeyId) { + PrintAndLogEx(INFO, "Encode key number: %d", encKeyId); + PrintAndLogEx(INFO, "Encode key: %s", sprint_hex_inrow(encKey, 16)); + + aes_encode(NULL, encKey, newKey, &keydata[3], CIPURSE_AES_KEY_LENGTH); + + PrintAndLogEx(INFO, "Encoded new key data: %s", sprint_hex_inrow(keydata, sizeof(keydata))); + } PrintAndLogEx(NORMAL, ""); } @@ -1589,7 +1615,7 @@ static int CmdHFCipurseUpdateKey(const char *Cmd) { CIPURSECSetActChannelSecurityLevels(sreq, sresp); } - res = CIPURSEUpdateKey(0, newKeyId, keydata, sizeof(keydata), buf, sizeof(buf), &len, &sw); + res = CIPURSEUpdateKey(encKeyId, newKeyId, keydata, sizeof(keydata), buf, sizeof(buf), &len, &sw); if (res != 0 || sw != 0x9000) { PrintAndLogEx(ERR, "Update key command " _RED_("ERROR") ". Card returns:\n 0x%04x - %s", sw, GetSpecificAPDUCodeDesc(UAPDpdateKeyCodeDescriptions, ARRAYLEN(UAPDpdateKeyCodeDescriptions), sw)); From 931cadfcf6272e8ef973ec34480ed253e4384bc7 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 8 Feb 2022 01:07:17 +0200 Subject: [PATCH 09/10] fix doc --- doc/cipurse.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/cipurse.md b/doc/cipurse.md index 6fa57b403..58c7d4a40 100644 --- a/doc/cipurse.md +++ b/doc/cipurse.md @@ -266,7 +266,7 @@ keys for masterfile needs to be updated manually with command: ```hf cipurse updakey``` update key attributes with default attributes -```hf cipurse updakey --aid 4144204631 --trgkeyn 1 --attr 02 -v --commit``` +```hf cipurse updakey --aid 4144204631 --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` **8. Set the file attributes** From 495f0e9f42907920ca80404e7448b1fd913c54c4 Mon Sep 17 00:00:00 2001 From: merlokk <807634+merlokk@users.noreply.github.com> Date: Tue, 8 Feb 2022 01:26:06 +0200 Subject: [PATCH 10/10] doc --- doc/cipurse.md | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/doc/cipurse.md b/doc/cipurse.md index 58c7d4a40..765869ab9 100644 --- a/doc/cipurse.md +++ b/doc/cipurse.md @@ -11,6 +11,13 @@ - [Card architecture](#card-architecture) - [Card structure](#card-structure) - [How to](#how-to) + - [How to select application or file](#how-to-select-application-or-file) + - [How to delete application or file](#how-to-delete-application-or-file) + - [How to read file](#how-to-read-file) + - [How to write file](#how-to-write-file) + - [How to read file attributes](#how-to-read-file-attributes) + - [How to set file attributes](#how-to-set-file-attributes) + - [How to update key](#how-to-update-key) - [How to personalize card](#how-to-personalize-card) @@ -120,7 +127,7 @@ select it with display output in raw and tlv views options ```hf cipurse delete --aid 4144204631 --chfid 0102``` -### How read file +### How to read file ^[Top](#top) with default key and aid @@ -133,7 +140,7 @@ with default key and aid without authentication ```hf cipurse read --fid 0102 --no-auth``` -### How write file +### How to write file ^[Top](#top) with default key and aid @@ -149,7 +156,7 @@ with default key and aid, perform commit (works for files with transactions mech ```hf cipurse read --fid 0102 -d abbbccdd --commit``` -### How read file attributes +### How to read file attributes ^[Top](#top) read master file attributes @@ -173,7 +180,7 @@ or with default application ```hf cipurse aread --aid 4144204632 --chfid 0102``` -### How set file attributes +### How to set file attributes ^[Top](#top) set elementary file attributes (EF) @@ -191,6 +198,16 @@ set master file (MF) file attributes ```hf cipurse awrite --mfd -d 080000FFFFFFFFFFFFFFFFFF86023232 --commit``` (full access with/wo keys and tag 86 is set by `22`) +### How to update key +^[Top](#top) + +update key for master application +```hf cipurse updakey --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` + +update key for application +```hf cipurse updakey --aid 4144204631 --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` + + ### How to personalize card ^[Top](#top) @@ -263,10 +280,13 @@ or if file with transaction mechanism keys for application usually filled at create time with DGI `A00F` keys for masterfile needs to be updated manually with command: -```hf cipurse updakey``` +```hf cipurse updakey --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` + +update key for application (if needs) +```hf cipurse updakey --aid 4144204631 --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` update key attributes with default attributes -```hf cipurse updakey --aid 4144204631 --newkeyn 1 --newkeya 00 --newkey 73737373737373737373737373737373 --commit``` +```hf cipurse updakey --aid 4144204631 --trgkeyn 1 --attr 02 -v --commit``` **8. Set the file attributes**