add active authentication (challenge/response)
This commit is contained in:
parent
3b93c9ea30
commit
490ce44ccb
@ -198,6 +198,13 @@ Connector::Response Connector::externalAuthenticate(bytes& eifd, uint64_t mifd)
|
||||
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::EXTERNAL_AUTHENTICATE, 0x00, 0x00, data.size(), data);
|
||||
}
|
||||
|
||||
Connector::Response Connector::internalAuthenticate(bytes data, size_t sigLength) {
|
||||
if (data.size() != 8) {
|
||||
throw "Internal Authentication accepts challenge with size 8 bytes only";
|
||||
}
|
||||
return sendICC(ISO7816_CLA::NO_SM, ISO7816_INS::INTERNAL_AUTHENTICATE, 0x00, 0x00, sigLength, data);
|
||||
}
|
||||
|
||||
Connector::Response Connector::readBinaryBySFI(int sfi, int offset) {
|
||||
bytes t;
|
||||
sfi |= 0x80;
|
||||
@ -549,11 +556,11 @@ void Connector::readEFSOD() {
|
||||
X509_get0_signature(&psig, &palg, cert);
|
||||
|
||||
bytes signature(psig->data, psig->data + psig->length);
|
||||
logBytes("signature raw %s", signature);
|
||||
logBytes("certificate signature raw %s", signature);
|
||||
|
||||
const unsigned char* sig = signature.data();
|
||||
ECDSA_SIG* cert_sig;
|
||||
d2i_ECDSA_SIG(&cert_sig, &sig, signature.size());
|
||||
cert_sig = d2i_ECDSA_SIG(nullptr, &sig, signature.size());
|
||||
|
||||
if (!cert_sig) {
|
||||
throw "cannot get certificate signature";
|
||||
@ -595,4 +602,91 @@ void Connector::readEFSOD() {
|
||||
throw "verification with CA returns false";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connector::activeAuthentication() {
|
||||
const unsigned char* dg15ptr = dg15.data();
|
||||
auto pubkey = d2i_RSA_PUBKEY(nullptr, &dg15ptr, dg15.size());
|
||||
|
||||
if (!pubkey) {
|
||||
throw "Cannot read pubkey";
|
||||
}
|
||||
|
||||
bytes m2 = randomBytes(8);
|
||||
|
||||
// 256 is hardcoded
|
||||
// MRTD returns signature of size [sigLength] or of arbitrarily size if [sigLength] is 256.
|
||||
auto response = internalAuthenticate(m2, 256);
|
||||
auto message = response.data;
|
||||
|
||||
BIGNUM* msg_number = BN_bin2bn(message.data(), message.size(), nullptr);
|
||||
const BIGNUM* n = RSA_get0_n(pubkey);
|
||||
const BIGNUM* e = RSA_get0_e(pubkey);
|
||||
|
||||
auto ctx = BN_CTX_new();
|
||||
BIGNUM* raw_msg_number = BN_new();
|
||||
BN_mod_exp(raw_msg_number, msg_number, e, n, ctx);
|
||||
BN_CTX_free(ctx);
|
||||
|
||||
LOGI("pubkey n %s", BN_bn2hex(n));
|
||||
LOGI("extracted raw message %s", BN_bn2hex(raw_msg_number));
|
||||
|
||||
bytes raw_msg(BN_num_bytes(raw_msg_number));
|
||||
BN_bn2bin(raw_msg_number, raw_msg.data());
|
||||
logBytes("active signature %s", raw_msg);
|
||||
|
||||
const uint8_t t = (raw_msg[raw_msg.size() - 1] == 0xbc) ? 1 : 2;
|
||||
int hashlen;
|
||||
if (t == 1) {
|
||||
hashlen = 160;
|
||||
} else {
|
||||
switch (raw_msg[raw_msg.size() - 2]) {
|
||||
case 0x34:
|
||||
hashlen = 256;
|
||||
break;
|
||||
case 0x35:
|
||||
hashlen = 512;
|
||||
break;
|
||||
case 0x36:
|
||||
hashlen = 384;
|
||||
break;
|
||||
case 0x38:
|
||||
hashlen = 224;
|
||||
break;
|
||||
default:
|
||||
hashlen = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const int k = BN_num_bits(n);
|
||||
const int m1_len = ((k - hashlen - 8 * t - 4) - 4);
|
||||
|
||||
// bits 01 default bit
|
||||
// bit 1 partial recovery
|
||||
// bit 1 end of padding
|
||||
// k - hash_len - m1_len - 8t - 4 bits padding
|
||||
const int pad = (2 + 1 + 1 + k - hashlen - m1_len - 8 * t - 4);
|
||||
|
||||
const int m1_len_bytes = m1_len / 8;
|
||||
const int pad_bytes = pad / 8;
|
||||
const int hash_len_bytes = hashlen / 8;
|
||||
|
||||
const int message_end = pad_bytes + m1_len_bytes;
|
||||
const int hash_end = message_end + hash_len_bytes;
|
||||
|
||||
const auto m1 = bytes(raw_msg.begin() + pad_bytes, raw_msg.begin() + message_end);
|
||||
const auto hash = bytes(raw_msg.begin() + message_end, raw_msg.begin() + hash_end);
|
||||
|
||||
bytes m1_m2;
|
||||
m1_m2.insert(m1_m2.end(), m1.begin(), m1.end());
|
||||
m1_m2.insert(m1_m2.end(), m2.begin(), m2.end());
|
||||
|
||||
bytes check_hash(160 / 8);
|
||||
SHA1(m1_m2.data(), m1_m2.size(), check_hash.data());
|
||||
bool verified = std::equal(hash.begin(), hash.end(), check_hash.begin());
|
||||
|
||||
logBytes("hash %s", hash);
|
||||
logBytes("check hash %s", check_hash);
|
||||
LOGI("active authentication verified %d", verified);
|
||||
}
|
@ -221,6 +221,7 @@ private:
|
||||
Connector::Response getChallenge(int challengeLen);
|
||||
|
||||
Connector::Response externalAuthenticate(bytes& eifd, uint64_t mifd);
|
||||
Connector::Response internalAuthenticate(bytes data, size_t sigLength);
|
||||
|
||||
Connector::Response readBinaryBySFI(int sfi, int offset);
|
||||
Connector::Response readBinaryExt(size_t offset, size_t ne);
|
||||
@ -229,8 +230,11 @@ private:
|
||||
bytes readFileBySFI(int sfi);
|
||||
bytes readBinary(size_t offset, size_t length);
|
||||
|
||||
|
||||
std::unordered_map<int, bytes> calculatedDigests;
|
||||
|
||||
bytes dg15;
|
||||
|
||||
public:
|
||||
transceive_type transceive;
|
||||
SecureMessaging* sm = nullptr;
|
||||
@ -249,6 +253,7 @@ public:
|
||||
void readEFDG15();
|
||||
void readEFSOD();
|
||||
|
||||
void activeAuthentication();
|
||||
};
|
||||
|
||||
#endif //CCCC_API_H
|
||||
|
@ -29,7 +29,10 @@ Java_com_bshield_cccc_MainActivity_initNFCScan(
|
||||
connector->readEFDG13();
|
||||
connector->readEFDG14();
|
||||
connector->readEFDG15();
|
||||
|
||||
connector->readEFSOD();
|
||||
connector->activeAuthentication();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user