add working api code

This commit is contained in:
nganhkhoa 2023-03-01 10:52:54 +07:00
parent 5fb8af9d4f
commit 941a40a38c
2 changed files with 511 additions and 0 deletions

282
app/src/main/cpp/api.cpp Normal file
View File

@ -0,0 +1,282 @@
//
// Created by ACER on 2023/02/24.
//
#include <vector>
#include <stdint.h>
#include <functional>
#include <android/log.h>
#include <jni.h>
#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();
bool extended = s > 255 || ne > 256;
bytes r = {uint8_t(cla), uint8_t(ins), uint8_t(p1), uint8_t(p2)};
if (extended) {
r.push_back(0);
r.push_back(s >> 8);
}
if (!data.empty()) {
r.push_back(s);
r.insert(r.end(), data.begin(), data.end());
}
if (ne == 0)
return r;
ne = (ne == 256 || ne == 65536) ? 0 : ne;
if (extended) {
if (data.empty()) {
r.push_back(0);
}
r.push_back((ne >> 8));
}
r.push_back(ne);
return r;
}
Connector::Response Connector::sendICC(int cla, int ins, int p1, int p2, int ne, bytes& data) {
LOGI("send %02x%02x%02x%02x", cla, ins, p1, p2);
logBytes(" send data %s", data);
if (sm) {
// wrap
cla |= ISO7816_CLA::SM_HEADER_AUTHN;
bytes dataDO;
if (!data.empty()) {
iso9797_pad(data);
sm->encrypt(data);
if (ins == ISO7816_INS::READ_BINARY_EXT) {
dataDO = sm->do85(data);
} else {
dataDO = sm->do87(data, true);
}
}
bytes do97 = sm->do97(ne);
bytes M = {uint8_t(cla), uint8_t(ins), uint8_t(p1), uint8_t(p2)};
iso9797_pad(M);
M.insert(M.end(), dataDO.begin(), dataDO.end());
M.insert(M.end(), do97.begin(), do97.end());
bytes N = sm->next_ssc();
N.insert(N.end(), M.begin(), M.end());
iso9797_pad(N);
bytes CC = sm->mac(N);
bytes do8e = sm->do8e(CC);
data.clear();
data.insert(data.end(), dataDO.begin(), dataDO.end());
data.insert(data.end(), do97.begin(), do97.end());
data.insert(data.end(), do8e.begin(), do8e.end());
ne = 256;
}
bytes command = finalizeAPDU(cla, ins, p1, p2, ne, data);
bytes raw_response = transceive(command);
logBytes("raw response %s", raw_response);
auto response = decodeResponse(raw_response);
logBytes(" raw decoded %s", response.data);
LOGI(" status %02x%02x", response.code >> 8, response.code & 0x0f);
logBytes(" raw %s", raw_response);
if (sm) {
// unwrap
// data missing || data invalid
if (response.code >> 8 == 0x69 && ((response.code & 0x0f == 0x87) || (response.code & 0x0f == 0x88))) {
return response;
}
if (response.data.empty()) return response;
auto do = sm->parseDO(response.data);
auto do99 = sm->parseDO99(response.data, do.size);
auto do8e = sm->parseDO8E(response.data, do99.size);
bytes K = sm->generateK(response.data, do8e);
uint64_t CC = bytes2num(sm->mac(K));
if (CC != bytes2num(do8e.value)) {
throw "Decoding message failure";
}
response.data = sm->decrypt(do.value);
response.success = true;
response.code = do99;
}
auto response = decodeResponse(raw_response);
logBytes(" decoded %s", response.data);
LOGI(" status %02x%02x", response.code >> 8, response.code & 0x0f);
logBytes(" raw %s", raw_response);
return response;
}
Connector::Response Connector::decodeResponse(bytes& response) {
int s = response.size();
Response r;
if (s < 2) {
r.success = false;
return r;
}
r.success = response[s - 2] == APDUsuccess;
r.code = response[s - 2] << 8 | response[s - 1];
r.data.assign(response.begin(), response.end() - 2);
return r;
}
Connector::Response Connector::selectFile(int p1, int p2, bytes& data) {
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::SELECT_FILE, p1, p2, 0, data);
}
Connector::Response Connector::selectFileByDFName(bytes&& fileId) {
return selectFile(ISO97816_SelectFileP1::byDFName, defaultSelectP2, fileId);
}
Connector::Response Connector::getChallenge(int challengeLen) {
bytes t;
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::GET_CHALLENGE, 0x00, 0x00, challengeLen, t);
}
Connector::Response Connector::externalAuthenticate(bytes& eifd, uint64_t mifd) {
bytes data;
data.insert(data.end(), eifd.begin(), eifd.end());
for (size_t i = 0; i < 8; i++) {
data.push_back(((uint8_t*)&mifd)[7 - i]);
}
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::EXTERNAL_AUTHENTICATE, 0x00, 0x00, data.size(), data);
}
Connector::Response Connector::readBinaryBySFI(int sfi, int offset) {
bytes t;
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::READ_BINARY, sfi, offset, readAheadLength, t);
}
Connector::Response Connector::readBinaryExt(int offset, int ne) {
// make data with TLV 0x54 for offset
bytes data;
int truncated_offset;
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::READ_BINARY_EXT, 0, 0, truncated_offset, data);
}
bool Connector::selectDf1() {
auto response = selectFileByDFName(bytes(Df1Name, Df1Name + sizeof(Df1Name)));
return response.success;
}
void Connector::initBAC() {
// in bytes
int nonceLen = 8;
int kLen = 16;
int sLen = 2 * nonceLen + kLen;
int rLen = sLen;
int eLen = sLen;
int macLen = 8;
// mrz.padRight(9, '<') + mrz_check_digit
// + birth yymmdd + birth_check_digit
// + expiry yymmdd + expiry_check_digit
const char dbaKeys[] = "098002079798112232311229";
const int dbaKeysSize = 9 + 1 + 6 + 1 + 6 + 1;
bytes dbaKeysSeed(20);
SHA1((char*)dbaKeysSeed.data(), dbaKeys, dbaKeysSize);
dbaKeysSeed.resize(16);
const bytes Kenc = deriveKeyDesEDE(dbaKeysSeed);
const bytes Kmac = deriveKeyISO9797(dbaKeysSeed);
const uint64_t Kenc1 = bytes2num(bytes(Kenc.begin(), Kenc.begin() + 8));
const uint64_t Kenc2 = bytes2num(bytes(Kenc.begin() + 8, Kenc.end()));
const uint64_t Kmac1 = bytes2num(bytes(Kmac.begin(), Kmac.begin() + 8));
const uint64_t Kmac2 = bytes2num(bytes(Kmac.begin() + 8, Kmac.end()));
logBytes("Kenc: %s", Kenc);
logBytes("Kmac: %s", Kmac);
// assert success
const bytes RNDicc = getChallenge(nonceLen).data;
const bytes RNDifd = randomBytes(nonceLen);
const bytes Kifd = randomBytes(kLen);
bytes S;
S.insert(S.end(), RNDifd.begin(), RNDifd.end());
S.insert(S.end(), RNDicc.begin(), RNDicc.end());
S.insert(S.end(), Kifd.begin(), Kifd.end());
logBytes("RNDicc %s", RNDicc);
logBytes("RNDifd %s", RNDifd);
logBytes("Kifd %s", Kifd);
logBytes("S %s", S);
bytes Eifd = tripledes_cbc_encrypt(S, Kenc1, Kenc2, Kenc1);
uint64_t Mifd = iso9797_mac(Eifd, Kmac1, Kmac2, Kmac1);
logBytes("Eifd %s", Eifd);
LOGI("Mifd: %llx", Mifd);
const auto pairEiccMicc = externalAuthenticate(Eifd, Mifd);
if (!pairEiccMicc.success) {
throw "External Authentication fail";
}
const bytes Eicc = bytes(pairEiccMicc.data.begin(), pairEiccMicc.data.begin() + eLen);
const uint64_t Micc = bytes2num(bytes(pairEiccMicc.data.begin() + eLen, pairEiccMicc.data.end()));
logBytes("Eicc %s", Eifd);
LOGI("Micc: %llx", Micc);
uint64_t Micc_verify = iso9797_mac(Eicc, 0x58effeadb594fe7c, 0x8a43e9f8c2d0a408, 0x58effeadb594fe7c);
LOGI("Verify Mac: %llx", Micc_verify);
if (Micc != Micc_verify) {
throw "Authentication fail";
}
const bytes R = tripledes_cbc_decrypt(Eicc, 0xfe43f1ab686eb334, 0xe68fea8cea31dfc7, 0xfe43f1ab686eb334);
const bytes eRNDifd = bytes(R.begin() + nonceLen, R.begin() + nonceLen * 2);
const bytes Kicc = bytes(R.begin() + nonceLen * 2, R.end());
logBytes("R: %s", R);
logBytes("eRNDifd: %s", eRNDifd);
logBytes("Kicc: %s", Kicc);
if (bytes2num(eRNDifd) != bytes2num(RNDifd)) {
throw "Authentication fail";
}
// session keys
bytes keyseed;
for (size_t i = 0; i < Kifd.size(); i++) {
keyseed.push_back(Kifd[i] ^ Kicc[i]);
}
bytes encryptKey = deriveKeyDesEDE(keyseed);
bytes macKey = deriveKeyISO9797(keyseed);
size_t suffix = nonceLen / 2;
bytes ssc;
ssc.insert(ssc.end(), RNDicc.begin() + suffix, RNDicc.end());
ssc.insert(ssc.end(), RNDifd.begin() + suffix, RNDifd.end());
uint64_t ssc_value = bytes2num(ssc);
logBytes("encrypt key: %s", encryptKey);
logBytes("mac key: %s", macKey);
logBytes("ssc: %s", ssc);
LOGI("ssc %llx", ssc_value);
sm = new SecureMessaging(encryptKey, macKey, ssc_value);
return;
}
void Connector::readEFCOM() {
}

229
app/src/main/cpp/api.h Normal file
View File

@ -0,0 +1,229 @@
//
// Created by ACER on 2023/02/24.
//
#ifndef CCCC_API_H
#define CCCC_API_H
#include <vector>
#include <stdint.h>
#include "utils.h"
#include "des.h"
class TLV {
public:
int tag;
int length;
bytes value;
int size; // full encoded length
TLV(int tag, bytes data) : tag(tag), length(data.size()), value(data) {
auto tag_nbyte = bytecount(tag);
auto len_nbyte = bytecount(length);
// assume tag is 1 byte for now
size = tag_nbyte + len_nbyte + value.size() + (len_nbyte > 1 ? 1 : 0);
}
// decode to TLV
TLV(bytes data) {
size_t offset = 0;
tag = data[offset++];
if (tag == 0x1f) {
// multi byte tag
throw "Cannot decode multi-byte tag yet";
}
// decode length
length = data[offset++] & 0xff;
if ((length & 0x80) == 0x80) {
// multi byte length
size_t length_nbyte = length & 0x7f;
if (length_nbyte > 3) {
throw "length too big to decode";
}
for (size_t i = 0; i < length_nbyte; i++) {
length <<= 8;
length |= data[offset++];
}
}
value.insert(value.end(), data.begin() + offset, data.begin() + offset + length);
size = offset + length;
}
// encode to TLV
bytes encode() {
auto tag_nbyte = bytecount(tag);
auto tag_encoded = num2bytes(tag);
tag_encoded.erase(tag_encoded.begin(), tag_encoded.end() - tag_nbyte);
auto len_nbyte = bytecount(length);
bytes len_encoded;
if (length < 0x80) {
len_encoded.push_back(length);
} else {
len_encoded.push_back(len_nbyte | 0x80);
auto x = num2bytes(length);
len_encoded.insert(len_encoded.end(), x.begin() + 8 - len_nbyte, x.end());
}
bytes encoded;
encoded.insert(encoded.end(), tag_encoded.begin(), tag_encoded.end());
encoded.insert(encoded.end(), len_encoded.begin(), len_encoded.end());
encoded.insert(value.end(), value.begin(), value.end());
}
};
class SecureMessaging {
bytes encKey;
bytes macKey;
uint64_t ssc;
// TLV encoding, simplified code
bytes buildDO(int tag, bytes data) {
// return TLV(tag, data).encode();
if (tag > 255) {
// in this context only
throw "Tag value is overflow";
}
if (data.empty()) return data;
auto tag_nbyte = bytecount(tag);
auto tag_encoded = num2bytes(tag);
tag_encoded.erase(tag_encoded.begin(), tag_encoded.end() - tag_nbyte);
size_t len = data.size();
if (len > 0xffffff) {
throw "Length too big";
}
auto len_nbyte = bytecount(len);
bytes len_encoded;
if (len < 0x80) {
len_encoded.push_back(len);
} else {
len_encoded.push_back(len_nbyte | 0x80);
auto x = num2bytes(len);
len_encoded.insert(len_encoded.end(), x.begin() + 8 - len_nbyte, x.end());
}
bytes encoded;
encoded.insert(encoded.end(), tag_encoded.begin(), tag_encoded.end());
encoded.insert(encoded.end(), len_encoded.begin(), len_encoded.end());
encoded.insert(encoded.end(), data.begin(), data.end());
return encoded;
}
public:
SecureMessaging(bytes encKey, bytes macKey, uint64_t ssc) : encKey(encKey), macKey(macKey), ssc(ssc) {}
bytes do97(int ne) {
if (ne == 256 || ne == 65536) {
return buildDO(0x97, bytes(ne == 256 ? 1 : 2));
}
// intToBin(ne, minLen=0)
return buildDO(0x97, bytes());
}
bytes do85(bytes data) {
return buildDO(0x85, data);
}
bytes do87(bytes data, bool padded) {
if (data.empty()) return bytes();
bytes d;
d.push_back(padded ? 0x1 : 0x2);
d.insert(d.end(), data.begin(), data.end());
return buildDO(0x87, d);
}
bytes do8e(bytes data) {
return buildDO(0x8e, data);
}
TLV* parseDO(bytes data) {
if (data.empty() || data[0] != 0x85 || data[0] != 0x87) {
return nullptr;
}
auto t = new TLV(data);
return t;
}
TLV parseDO99(bytes data, size_t offset) {
if (data.empty()) {
return nullptr;
}
}
TLV parseDO8E(bytes data, size_t offset) {
}
bytes generateK(bytes data, ) {}
bytes mac(bytes data) {
const uint64_t Kmac1 = bytes2num(bytes(macKey.begin(), macKey.begin() + 8));
const uint64_t Kmac2 = bytes2num(bytes(macKey.begin() + 8, macKey.end()));
uint64_t m = iso9797_mac(data, Kmac1, Kmac2, Kmac1);
return num2bytes(m);
}
bytes encrypt(bytes& data) {
const uint64_t Kenc1 = bytes2num(bytes(encKey.begin(), encKey.begin() + 8));
const uint64_t Kenc2 = bytes2num(bytes(encKey.begin() + 8, encKey.end()));
bytes d = tripledes_cbc_encrypt(data, Kenc1, Kenc2, Kenc1);
data = d;
}
bytes decrypt(bytes& data) {
const uint64_t Kenc1 = bytes2num(bytes(encKey.begin(), encKey.begin() + 8));
const uint64_t Kenc2 = bytes2num(bytes(encKey.begin() + 8, encKey.end()));
bytes d = tripledes_cbc_decrypt(data, Kenc1, Kenc2, Kenc1);
data = d;
}
bytes next_ssc() {
ssc++;
return num2bytes(ssc);
}
};
class Connector {
typedef std::function<bytes(bytes)> transceive_type;
struct Response {
bytes data;
bool success;
int code;
};
private:
bytes finalizeAPDU(int cla, int ins, int p1, int p2, int ne, bytes& data);
Connector::Response sendICC(int cla, int ins, int p1, int p2, int ne, bytes& data);
Connector::Response decodeResponse(bytes& response);
Connector::Response selectFile(int p1, int p2, bytes& data);
Connector::Response selectFileByDFName(bytes&& fileId);
Connector::Response getChallenge(int challengeLen);
Connector::Response externalAuthenticate(bytes& eifd, uint64_t mifd);
Connector::Response readBinaryBySFI(int sfi, int offset);
Connector::Response readBinaryExt(int offset, int ne);
// setupSecureMessaging();
public:
transceive_type transceive;
SecureMessaging* sm;
int readAheadLength = 8;
Connector(transceive_type transceive) : transceive(transceive) {}
bool selectDf1();
void initBAC();
void readEFCOM();
};
#endif //CCCC_API_H