From 44f2e253d8e11349c3d8679d38b4f78296d1a06d Mon Sep 17 00:00:00 2001 From: Grayson Martin Date: Thu, 6 Jul 2023 18:03:58 -0500 Subject: [PATCH] import mbedtls ecc point (de)compression extension and use --- client/deps/mbedtls.cmake | 1 + client/src/cmdvas.c | 79 ++++++++++++++- common/mbedtls/ecc_point_compression.c | 130 +++++++++++++++++++++++++ common/mbedtls/ecc_point_compression.h | 32 ++++++ 4 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 common/mbedtls/ecc_point_compression.c create mode 100644 common/mbedtls/ecc_point_compression.h diff --git a/client/deps/mbedtls.cmake b/client/deps/mbedtls.cmake index 784f0f94d..82a97e8a0 100644 --- a/client/deps/mbedtls.cmake +++ b/client/deps/mbedtls.cmake @@ -10,6 +10,7 @@ add_library(pm3rrg_rdv4_mbedtls STATIC ../../common/mbedtls/error.c ../../common/mbedtls/ecp.c ../../common/mbedtls/ecdh.c + ../../common/mbedtls/ecc_point_compression.c ../../common/mbedtls/ecp_curves.c ../../common/mbedtls/certs.c ../../common/mbedtls/camellia.c diff --git a/client/src/cmdvas.c b/client/src/cmdvas.c index a0e0b0522..00c5a83f4 100644 --- a/client/src/cmdvas.c +++ b/client/src/cmdvas.c @@ -38,6 +38,7 @@ #include "mbedtls/ecp.h" #include "mbedtls/bignum.h" #include "mbedtls/ecdh.h" +#include "mbedtls/ecc_point_compression.h" uint8_t ecpData[] = { 0x6a, 0x01, 0x00, 0x00, 0x04 }; uint8_t aid[] = { 0x4f, 0x53, 0x45, 0x2e, 0x56, 0x41, 0x53, 0x2e, 0x30, 0x31 }; @@ -187,9 +188,7 @@ static int LoadReaderPrivateKey(uint8_t *buf, size_t bufLen, mbedtls_ecp_keypair tlvdb_free(derRoot); - if (mbedtls_mpi_read_binary(&privKey->Q.X, pubkeyCoordsTlv->value + 2, 32) - || mbedtls_mpi_read_binary(&privKey->Q.Y, pubkeyCoordsTlv->value + 34, 32) - || mbedtls_mpi_lset(&privKey->Q.Z, 1)) { + if (mbedtls_ecp_point_read_binary(&privKey->grp, &privKey->Q, pubkeyCoordsTlv->value + 1, 65)) { PrintAndLogEx(FAILED, "Failed to read in public key coordinates"); return PM3_EINVARG; } @@ -216,9 +215,20 @@ static int GetPrivateKeyHint(mbedtls_ecp_keypair *privKey, uint8_t *keyHint) { } static int LoadMobileEphemeralKey(uint8_t *xcoordBuf, mbedtls_ecp_keypair *pubKey) { - if (mbedtls_mpi_read_binary(&pubKey->Q.X, xcoordBuf, 32)) { + uint8_t compressedEcKey[33] = {0}; + compressedEcKey[0] = 0x02; + memcpy(compressedEcKey + 1, xcoordBuf, 32); + + uint8_t decompressedEcKey[65] = {0}; + size_t decompressedEcKeyLen = 0; + if (mbedtls_ecp_decompress(&pubKey->grp, compressedEcKey, sizeof(compressedEcKey), decompressedEcKey, &decompressedEcKeyLen, sizeof(decompressedEcKey))) { return PM3_EINVARG; } + + if (mbedtls_ecp_point_read_binary(&pubKey->grp, &pubKey->Q, decompressedEcKey, decompressedEcKeyLen)) { + return PM3_EINVARG; + } + return PM3_SUCCESS; } @@ -400,6 +410,65 @@ static int CmdVASReader(const char *Cmd) { } static int CmdVASDecrypt(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "nfc vas decrypt", + "Decrypt a previously captured cryptogram", + "nfc vas reader -p pass.com.example.ticket -k ./priv.key -> select pass and decrypt with priv.key\nnfc vas reader --url https://example.com -> URL Only mode"); + void *argtable[] = { + arg_param_begin, + arg_str0("p", NULL, "", "pass type id"), + arg_str0("k", NULL, "", "path to terminal private key"), + arg_str0(NULL, NULL, "", "cryptogram to decrypt"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + struct arg_str *passTypeIdArg = arg_get_str(ctx, 1); + int passTypeIdLen = arg_get_str_len(ctx, 1); + uint8_t pidHash[32] = {0}; + sha256hash((uint8_t *) passTypeIdArg->sval[0], passTypeIdLen, pidHash); + + uint8_t cryptogram[120] = {0}; + int cryptogramLen = 0; + CLIGetHexWithReturn(ctx, 3, cryptogram, &cryptogramLen); + + struct arg_str *keyPathArg = arg_get_str(ctx, 2); + int keyPathLen = arg_get_str_len(ctx, 2); + + if (keyPathLen == 0 && passTypeIdLen > 0) { + PrintAndLogEx(FAILED, "Must provide path to terminal private key if a pass type id is provided"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + + uint8_t *keyData = NULL; + size_t keyDataLen = 0; + if (loadFile_safe(keyPathArg->sval[0], "", (void **)&keyData, &keyDataLen) != PM3_SUCCESS) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + mbedtls_ecp_keypair privKey; + mbedtls_ecp_keypair_init(&privKey); + + if (LoadReaderPrivateKey(keyData, keyDataLen, &privKey) != PM3_SUCCESS) { + CLIParserFree(ctx); + mbedtls_ecp_keypair_free(&privKey); + return PM3_EINVARG; + } + + uint8_t message[64] = {0}; + size_t messageLen = 0; + uint8_t timestamp[4] = {0}; + + if (DecryptVASCryptogram(cryptogram, cryptogramLen, &privKey, message, &messageLen, timestamp) != PM3_SUCCESS) { + CLIParserFree(ctx); + mbedtls_ecp_keypair_free(&privKey); + return PM3_EINVARG; + } + + CLIParserFree(ctx); + mbedtls_ecp_keypair_free(&privKey); return PM3_SUCCESS; } @@ -412,7 +481,7 @@ static int CmdHelp(const char *Cmd); static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "----------- " _CYAN_("Value Added Service") " -----------"}, {"reader", CmdVASReader, IfPm3Iso14443a, "Read and decrypt VAS message"}, - {"decrypt", CmdVASDecrypt, AlwaysAvailable, "Decrypt a VAS cryptogram"}, + {"decrypt", CmdVASDecrypt, AlwaysAvailable, "Decrypt a previously captured VAS cryptogram"}, {"sim", CmdVASSim, IfPm3Iso14443a, "Simulate a VAS mobile credential"}, {"--------", CmdHelp, AlwaysAvailable, "----------------- " _CYAN_("General") " -----------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, diff --git a/common/mbedtls/ecc_point_compression.c b/common/mbedtls/ecc_point_compression.c new file mode 100644 index 000000000..387255507 --- /dev/null +++ b/common/mbedtls/ecc_point_compression.c @@ -0,0 +1,130 @@ +/* +* Not original to the mbedtls library. Taken from +* https://github.com/mwarning/mbedtls_ecp_compression +* to solve mbedtls' lack of support for elliptic point +* compression and decompression +* +* Released under CC0 1.0 Universal License +*/ + +/* +* This is all about mbedtls_ecp_decompress() and mbedtls_ecp_compress() +* +* Perform X25519 / Curve25519 point compression and decompression for mbedtls. +* As of mbedtls 2.5.1, mbedtls does not support decompression yet. +* +*/ + +#define MBEDTLS_ALLOW_PRIVATE_ACCESS + +#include "ecc_point_compression.h" + +int mbedtls_ecp_decompress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +) { + int ret; + size_t plen; + mbedtls_mpi r; + mbedtls_mpi x; + mbedtls_mpi n; + + plen = mbedtls_mpi_size(&grp->P); + + *olen = 2 * plen + 1; + + if (osize < *olen) + return(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + + if (ilen != plen + 1) + return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + if (input[0] != 0x02 && input[0] != 0x03) + return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + // output will consist of 0x04|X|Y + memcpy(output, input, ilen); + output[0] = 0x04; + + mbedtls_mpi_init(&r); + mbedtls_mpi_init(&x); + mbedtls_mpi_init(&n); + + // x <= input + MBEDTLS_MPI_CHK(mbedtls_mpi_read_binary(&x, input + 1, plen)); + + // r = x^2 + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &x, &x)); + + // r = x^2 + a + if (grp->A.p == NULL) { + // Special case where a is -3 + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_int(&r, &r, 3)); + } else { + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->A)); + } + + // r = x^3 + ax + MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&r, &r, &x)); + + // r = x^3 + ax + b + MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&r, &r, &grp->B)); + + // Calculate square root of r over finite field P: + // r = sqrt(x^3 + ax + b) = (x^3 + ax + b) ^ ((P + 1) / 4) (mod P) + + // n = P + 1 + MBEDTLS_MPI_CHK(mbedtls_mpi_add_int(&n, &grp->P, 1)); + + // n = (P + 1) / 4 + MBEDTLS_MPI_CHK(mbedtls_mpi_shift_r(&n, 2)); + + // r ^ ((P + 1) / 4) (mod p) + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&r, &r, &n, &grp->P, NULL)); + + // Select solution that has the correct "sign" (equals odd/even solution in finite group) + if ((input[0] == 0x03) != mbedtls_mpi_get_bit(&r, 0)) { + // r = p - r + MBEDTLS_MPI_CHK(mbedtls_mpi_sub_mpi(&r, &grp->P, &r)); + } + + // y => output + ret = mbedtls_mpi_write_binary(&r, output + 1 + plen, plen); + +cleanup: + mbedtls_mpi_free(&r); + mbedtls_mpi_free(&x); + mbedtls_mpi_free(&n); + + return(ret); +} + +int mbedtls_ecp_compress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +) { + size_t plen; + + plen = mbedtls_mpi_size(&grp->P); + + *olen = plen + 1; + + if (osize < *olen) + return(MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL); + + if (ilen != 2 * plen + 1) + return (MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + if (input[0] != 0x04) + return(MBEDTLS_ERR_ECP_BAD_INPUT_DATA); + + // output will consist of 0x0?|X + memcpy(output, input, *olen); + + // Encode even/odd of Y into first byte (either 0x02 or 0x03) + output[0] = 0x02 + (input[2 * plen] & 1); + + return(0); +} diff --git a/common/mbedtls/ecc_point_compression.h b/common/mbedtls/ecc_point_compression.h new file mode 100644 index 000000000..d75fea39b --- /dev/null +++ b/common/mbedtls/ecc_point_compression.h @@ -0,0 +1,32 @@ +/* +* Not original to the mbedtls library. Taken from +* https://github.com/mwarning/mbedtls_ecp_compression +* to solve mbedtls' lack of support for elliptic point +* compression and decompression +* +* Released under CC0 1.0 Universal License +*/ + +/* +* This is all about mbedtls_ecp_decompress() and mbedtls_ecp_compress() +* +* Perform X25519 / Curve25519 point compression and decompression for mbedtls. +* As of mbedtls 2.5.1, mbedtls does not support decompression yet. +* +*/ + +#include + +#include "mbedtls/ecp.h" + +int mbedtls_ecp_decompress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +); + +int mbedtls_ecp_compress( + const mbedtls_ecp_group *grp, + const unsigned char *input, size_t ilen, + unsigned char *output, size_t *olen, size_t osize +);