From 499ba26d7bc9b0ec1f9e5b72c8a0c6919f651b18 Mon Sep 17 00:00:00 2001 From: nganhkhoa Date: Mon, 6 Mar 2023 10:03:46 +0700 Subject: [PATCH] basic check of data group hashes --- app/src/main/cpp/api.cpp | 137 ++++++++++++++++++++++++++++++++++++--- app/src/main/cpp/api.h | 3 + app/src/main/cpp/utils.h | 2 +- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/app/src/main/cpp/api.cpp b/app/src/main/cpp/api.cpp index c6c3052..8472282 100644 --- a/app/src/main/cpp/api.cpp +++ b/app/src/main/cpp/api.cpp @@ -9,11 +9,17 @@ #include #include +#include +#include +#include +#include +#include +#include + #include "api.h" #include "consts.h" #include "utils.h" #include "des.h" -#include "sha1.h" bytes Connector::finalizeAPDU(int cla, int ins, int p1, int p2, int ne, bytes& data) { int s = data.size(); @@ -211,7 +217,6 @@ Connector::Response Connector::readBinaryExt(size_t offset, size_t ne) { return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::READ_BINARY_EXT, 0, 0, ne, data); } Connector::Response Connector::readBinaryNormal(size_t offset, size_t toRead) { - LOGI("Read normal offset=%d len=%d"); bytes t; return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::READ_BINARY, offset >> 8, offset & 0xff, toRead, t); } @@ -234,10 +239,10 @@ void Connector::initBAC() { // mrz.padRight(9, '<') + mrz_check_digit // + birth yymmdd + birth_check_digit // + expiry yymmdd + expiry_check_digit - const char dbaKeys[] = "098002079798112232311229"; + const unsigned char dbaKeys[] = "098002079798112232311229"; const int dbaKeysSize = 9 + 1 + 6 + 1 + 6 + 1; bytes dbaKeysSeed(20); - SHA1((char*)dbaKeysSeed.data(), dbaKeys, dbaKeysSize); + SHA1(dbaKeys, dbaKeysSize, dbaKeysSeed.data()); dbaKeysSeed.resize(16); const bytes Kenc = deriveKeyDesEDE(dbaKeysSeed); @@ -378,31 +383,143 @@ void Connector::readEFCOM() { } void Connector::readEFDG1() { auto content = readFileBySFI(EFDG1_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE DG1 size %d", content.size()); - logBytes("FILE DG1 %s", content); + logBytes("FILE DG1 sha256 %s", digest); + calculatedDigests[1] = digest; } void Connector::readEFDG2() { auto content = readFileBySFI(EFDG2_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE DG2 size %d", content.size()); - logBytes("FILE DG2 %s", content); + logBytes("FILE DG2 sha256 %s", digest); + calculatedDigests[2] = digest; } void Connector::readEFDG13() { auto content = readFileBySFI(EFDG13_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE DG13 size %d", content.size()); - logBytes("FILE DG13 %s", content); + logBytes("FILE DG13 sha256 %s", digest); + calculatedDigests[13] = digest; } void Connector::readEFDG14() { auto content = readFileBySFI(EFDG14_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE DG14 size %d", content.size()); - logBytes("FILE DG14 %s", content); + logBytes("FILE DG14 sha256 %s", digest); + calculatedDigests[14] = digest; } void Connector::readEFDG15() { auto content = readFileBySFI(EFDG15_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE DG15 size %d", content.size()); - logBytes("FILE DG15 %s", content); + logBytes("FILE DG15 sha256 %s", digest); + calculatedDigests[15] = digest; } + +typedef struct DigestItem { + ASN1_INTEGER* dg; + ASN1_OCTET_STRING* digest; +} DigestItem; +DECLARE_ASN1_FUNCTIONS(DigestItem) + +ASN1_SEQUENCE(DigestItem) = { + ASN1_SIMPLE(DigestItem, dg, ASN1_INTEGER), + ASN1_SIMPLE(DigestItem, digest, ASN1_OCTET_STRING), +} ASN1_SEQUENCE_END(DigestItem) + +IMPLEMENT_ASN1_FUNCTIONS(DigestItem) + +typedef struct EncapsulatedContent { + ASN1_INTEGER* v; // some dummy value i don't know + X509_ALGOR* algo; + STACK_OF(DigestItem)* digests; +} EncapsulatedContent; +DECLARE_ASN1_FUNCTIONS(EncapsulatedContent) +ASN1_SEQUENCE(EncapsulatedContent) = { + ASN1_SIMPLE(EncapsulatedContent, v, ASN1_INTEGER), + ASN1_SIMPLE(EncapsulatedContent, algo, X509_ALGOR), + ASN1_SEQUENCE_OF(EncapsulatedContent, digests, DigestItem), +} ASN1_SEQUENCE_END(EncapsulatedContent) +IMPLEMENT_ASN1_FUNCTIONS(EncapsulatedContent) + void Connector::readEFSOD() { auto content = readFileBySFI(EFSOD_SFI); + bytes digest(256 / 8); + SHA256(content.data(), content.size(), digest.data()); LOGI("FILE SOD size %d", content.size()); - logBytes("FILE SOD %s", content); + logBytes("FILE SOD sha256 %s", digest); + + // strip first 4 bytes, tag, extended-length-bit, length 2 bytes + content.erase(content.begin(), content.begin() + 4); + + auto bio = BIO_new_mem_buf(content.data(), content.size()); + auto pkcs7 = d2i_PKCS7_bio(bio, nullptr); + + bytes encapsulatedBytes; + { + auto d = (pkcs7->d.sign->contents)->d.other->value.octet_string; + encapsulatedBytes.insert(encapsulatedBytes.end(), d->data, d->data + d->length); + } + logBytes("FILE SOD encapsulated content %s", encapsulatedBytes); + + { // check digest hash for each data group + auto p = encapsulatedBytes.data(); + auto content = d2i_EncapsulatedContent(nullptr, (const unsigned char**)&p, encapsulatedBytes.size()); + if (content == nullptr) { + LOGI("encapsulated content format is wrong"); + return; + } + + auto digests = (OPENSSL_STACK*)content->digests; + for (size_t i = 0; i < sk_num(digests); i++) { + auto item = (DigestItem*)sk_value(digests, i); + auto dg = ASN1_INTEGER_get(item->dg); + if (dg == 3) continue; + auto digest = bytes(item->digest->data, item->digest->data + item->digest->length); + + // will throw if not found in calculatedDigests + if (calculatedDigests[dg] != digest) { + throw "Digest for data group is wrong"; + } + LOGI("dg%d:", dg); + logBytes("%s", digest); + } + } + + bytes encapsulatedDigest(256 / 8); + SHA256(encapsulatedBytes.data(), encapsulatedBytes.size(), encapsulatedDigest.data()); + logBytes("FILE SOD encapsulated digest %s", encapsulatedDigest); + + auto signer = (PKCS7_SIGNER_INFO*)sk_value((OPENSSL_STACK*)pkcs7->d.sign->signer_info, 0); + + auto attrs = (OPENSSL_STACK*)(signer->auth_attr); + for (size_t i = 0; i < sk_num(attrs); i++) { + auto attr = (X509_ATTRIBUTE*)sk_value(attrs, i); + auto x = X509_ATTRIBUTE_get0_type(attr, 0); + +// LOGI("%d", ASN1_TYPE_get(x)); + if (ASN1_TYPE_get(x) == 4) { + auto p = x->value.octet_string; + auto pp = bytes(p->data, p->data + p->length); + logBytes("%s", pp); + + if (pp != encapsulatedDigest) { + throw "Encapsulated digest check with attribute data wrong"; + } + } + } + + { // now use openssl to verify with no CA + + } + + { // extract certificate and check with CA pubkey + + } } \ No newline at end of file diff --git a/app/src/main/cpp/api.h b/app/src/main/cpp/api.h index a59744a..b3bd3a3 100644 --- a/app/src/main/cpp/api.h +++ b/app/src/main/cpp/api.h @@ -7,6 +7,7 @@ #include #include +#include #include "utils.h" #include "des.h" @@ -228,6 +229,8 @@ private: bytes readFileBySFI(int sfi); bytes readBinary(size_t offset, size_t length); + std::unordered_map calculatedDigests; + public: transceive_type transceive; SecureMessaging* sm = nullptr; diff --git a/app/src/main/cpp/utils.h b/app/src/main/cpp/utils.h index 71e1fdb..46f66ec 100644 --- a/app/src/main/cpp/utils.h +++ b/app/src/main/cpp/utils.h @@ -34,7 +34,7 @@ inline bytes randomBytes(size_t length) { return data; } -inline void logBytes(char* msg, const bytes& data) { +inline void logBytes(const char* msg, const bytes& data) { unsigned char charmap[] = { '0', '1', '2', '3', '4', '5', '6', '7',