
Implementando EMV do Zero: Guia Completo para Terminais de Pagamento
Como implementar EMV (chip) do zero em terminais de pagamento. Guia técnico completo com código real usado em produção, baseado em 20 anos desenvolvendo...
✨TL;DR / Sumário Executivo
Como implementar EMV (chip) do zero em terminais de pagamento. Guia técnico completo com código real usado em produção, baseado em 20 anos desenvolvendo...
"EMV é ciência de foguetes disfarçada de pagamento por cartão." Após implementar sistemas que processam bilhões em transações, posso afirmar: 90% dos desenvolvedores subestimam drasticamente sua complexidade.
💡 TL;DR (Resumo)
EMV é uma tecnologia de pagamentos extremamente complexa, baseada em criptografia de chave pública e especificações rigorosas. Este guia desmistifica o processo com exemplos de código C e Kotlin, mostrando o fluxo de transação passo a passo (ATR, APDU, GPO, Criptogramas de Aplicação), os desafios de implementação e a rara oportunidade de mercado para os especialistas que dominam esta área.
O Momento em Que Tudo Fez Sentido
Era 2007. Eu estava depurando um terminal de pagamento que funcionava perfeitamente com cartões magnéticos, mas rejeitava 100% dos chips EMV. Três semanas de investigação, centenas de logs analisados, e finalmente descobri o problema:
Uma única linha de código estava invertendo dois bytes na resposta APDU.
// Bug que custou 3 semanas para encontrar
response[0] = sw2; // Deveria ser sw1
response[1] = sw1; // Deveria ser sw2Esses dois bytes - SW1 e SW2 (Status Words) - determinam se uma transação é aprovada ou rejeitada. Trocar sua ordem transforma "Aprovado" em "Erro Fatal de Comunicação".
Naquele momento compreendi uma verdade fundamental sobre EMV: cada byte tem um significado crítico, e um erro microscópico pode derrubar um sistema inteiro.
Hoje, depois de implementar EMV em dezenas de terminais diferentes, vou te mostrar exatamente como construir um sistema que funciona - sem as 3 semanas de depuração.
A Realidade Brutal do EMV
EMV não é apenas "cartão com chip". É um ecossistema complexo de especificações, protocolos, criptografia e certificações que levou a indústria financeira décadas para padronizar.
Os Números Que Intimidam
// Complexidade EMV em números
#define EMV_SPECIFICATIONS_COUNT 45 // Documentos oficiais
#define TOTAL_PAGES 8127 // Páginas de especificação
#define MANDATORY_DATA_OBJECTS 200+ // Tags EMV obrigatórios
#define CRYPTOGRAPHIC_ALGORITHMS 12 // RSA, DES, SHA, etc.
#define CERTIFICATION_LEVELS 4 // L1, L2, L3, L4
#define AVERAGE_IMPLEMENTATION_TIME 18 // Meses para equipe experiente
// Custo típico de implementação completa
#define DEVELOPMENT_COST_USD 500000 // Desenvolvimento
#define CERTIFICATION_COST_USD 150000 // Certificação EMVCo
#define ONGOING_MAINTENANCE_USD 100000 // Por anoPor que é tão complexo? Porque EMV resolve problemas que cartão magnético nunca conseguiu:
- Autenticação criptográfica - Prova que o cartão é genuíno
- Dados dinâmicos - Cada transação gera tokens únicos
- Autorização offline - Terminal pode aprovar sem rede
- Prevenção de clonagem - Impossível duplicar chips
- Interoperabilidade global - Funciona em qualquer lugar do mundo
Anatomia de uma Transação EMV
Vou mostrar exatamente o que acontece quando você insere um cartão chip no terminal.
Fase 1: Card Detection e ATR
// Detecção física do cartão no slot
typedef enum {
CARD_ABSENT = 0,
CARD_PRESENT = 1,
CARD_POWERED = 2,
CARD_ACTIVATED = 3,
CARD_ERROR = -1
} card_status_t;
typedef struct {
uint8_t atr[32]; // Answer To Reset
uint8_t atr_len; // Comprimento do ATR
uint8_t protocol; // T=0 ou T=1
uint32_t baudrate; // Taxa de comunicação
bool has_historical; // Dados históricos presentes
} card_info_t;
// Sequência de ativação do cartão
card_status_t activate_emv_card(card_info_t* card_info) {
// 1. Detecção física (contatos fazem conexão)
if (!detect_card_insertion()) {
return CARD_ABSENT;
}
// 2. Power up sequence (ISO 7816-3)
// VCC = 5V, Clock = 4MHz, Reset = High
power_up_card();
delay_microseconds(400); // Tempo mínimo para estabilização
// 3. Reset sequence
set_reset_line(LOW);
delay_microseconds(400);
set_reset_line(HIGH);
// 4. Recebe ATR (Answer To Reset)
uint8_t atr_buffer[32];
int atr_len = receive_atr(atr_buffer, sizeof(atr_buffer), 20000); // 20s timeout
if (atr_len < 2) {
return CARD_ERROR;
}
// 5. Parse ATR para extrair parâmetros de comunicação
if (!parse_atr(atr_buffer, atr_len, card_info)) {
return CARD_ERROR;
}
printf("Card ATR: ");
for (int i = 0; i < atr_len; i++) {
printf("%02X ", atr_buffer[i]);
}
printf("\n");
return CARD_ACTIVATED;
}
// Parser de ATR (Answer To Reset) - crítico para comunicação
bool parse_atr(uint8_t* atr, uint8_t len, card_info_t* info) {
if (len < 2) return false;
// TS (Initial Character) - define convenção
uint8_t ts = atr[0];
if (ts != 0x3B && ts != 0x3F) {
printf("ATR inválido: TS = 0x%02X\n", ts);
return false;
}
// T0 (Format Character)
uint8_t t0 = atr[1];
uint8_t k = t0 & 0x0F; // Número de bytes históricos
info->atr_len = len;
memcpy(info->atr, atr, len);
// Parsing dos interface bytes (TA, TB, TC, TD)
uint8_t idx = 2;
uint8_t td = t0;
while (td & 0xF0) { // Enquanto houver interface bytes
if (td & 0x10) idx++; // TA presente
if (td & 0x20) idx++; // TB presente
if (td & 0x40) idx++; // TC presente
if (td & 0x80) { // TD presente
if (idx >= len) return false;
td = atr[idx++];
} else {
break;
}
}
// Protocolo padrão é T=0 (byte-oriented)
info->protocol = 0;
info->baudrate = 9600; // Default conforme ISO 7816
return true;
}O que está acontecendo aqui:
- Detecção física - Contatos do chip tocam terminais do leitor
- Power-up - Energização seguindo timing rigoroso da ISO 7816
- Reset - Sinal de reset ativa o microprocessador do cartão
- ATR - Cartão responde com suas capacidades e parâmetros
- Parsing - Terminal interpreta ATR para configurar comunicação
Fase 2: Application Selection
// Lista de aplicações suportadas pelo terminal
typedef struct {
uint8_t aid[16]; // Application Identifier
uint8_t aid_len; // Tamanho do AID
char name[32]; // Nome da aplicação
bool priority; // Aplicação prioritária
} application_t;
// Aplicações típicas em terminais brasileiros
application_t supported_apps[] = {
// Visa
{{0xA0, 0x00, 0x00, 0x00, 0x03, 0x10, 0x10}, 7, "Visa Credit", true},
{{0xA0, 0x00, 0x00, 0x00, 0x03, 0x20, 0x10}, 7, "Visa Debit", true},
// Mastercard
{{0xA0, 0x00, 0x00, 0x00, 0x04, 0x10, 0x10}, 7, "Mastercard Credit", true},
{{0xA0, 0x00, 0x00, 0x00, 0x04, 0x30, 0x60}, 7, "Mastercard Debit", true},
// Elo (Brasil)
{{0xA0, 0x00, 0x00, 0x03, 0x33, 0x01, 0x01, 0x01}, 8, "Elo Credit", true},
{{0xA0, 0x00, 0x00, 0x03, 0x33, 0x01, 0x01, 0x02}, 8, "Elo Debit", true}
};
// APDU (Application Protocol Data Unit) structure
typedef struct {
uint8_t cla; // Class byte
uint8_t ins; // Instruction byte
uint8_t p1, p2; // Parameter bytes
uint8_t lc; // Length of command data
uint8_t data[255]; // Command data
uint8_t le; // Length of expected response
} apdu_command_t;
typedef struct {
uint8_t data[255]; // Response data
uint16_t len; // Length of response data
uint8_t sw1, sw2; // Status words
} apdu_response_t;
// Seleção de aplicação EMV
int select_emv_application(application_t* selected_app) {
apdu_command_t cmd;
apdu_response_t resp;
// 1. PSE (Payment System Environment) - método preferido
printf("Tentando PSE (Payment System Environment)...\n");
// SELECT PSE
cmd.cla = 0x00;
cmd.ins = 0xA4; // SELECT
cmd.p1 = 0x04; // Select by name
cmd.p2 = 0x00; // Return FCI template
cmd.lc = 14; // Length of PSE name
// PSE name: "1PAY.SYS.DDF01"
uint8_t pse_name[] = "1PAY.SYS.DDF01";
memcpy(cmd.data, pse_name, 14);
cmd.le = 0x00; // Return all available data
if (send_apdu(&cmd, &resp) == 0 && resp.sw1 == 0x90 && resp.sw2 == 0x00) {
// PSE encontrado, parse FCI para obter lista de aplicações
if (parse_pse_response(&resp, selected_app)) {
return 0; // Sucesso
}
}
// 2. Fallback: Tentativa direta com AIDs conhecidos
printf("PSE falhou, tentando AIDs diretamente...\n");
for (int i = 0; i < sizeof(supported_apps)/sizeof(application_t); i++) {
cmd.cla = 0x00;
cmd.ins = 0xA4; // SELECT
cmd.p1 = 0x04; // Select by name
cmd.p2 = 0x00;
cmd.lc = supported_apps[i].aid_len;
memcpy(cmd.data, supported_apps[i].aid, supported_apps[i].aid_len);
cmd.le = 0x00;
printf("Tentando AID: ");
for (int j = 0; j < supported_apps[i].aid_len; j++) {
printf("%02X ", supported_apps[i].aid[j]);
}
printf("(%s)\n", supported_apps[i].name);
if (send_apdu(&cmd, &resp) == 0 && resp.sw1 == 0x90 && resp.sw2 == 0x00) {
// Aplicação encontrada
memcpy(selected_app, &supported_apps[i], sizeof(application_t));
printf("Aplicação selecionada: %s\n", selected_app->name);
return 0;
}
printf("AID rejeitado: SW1=%02X SW2=%02X\n", resp.sw1, resp.sw2);
}
printf("Erro: Nenhuma aplicação compatível encontrada\n");
return -1;
}
// Parser de resposta PSE
bool parse_pse_response(apdu_response_t* resp, application_t* selected_app) {
// Parse TLV (Tag-Length-Value) structure
uint8_t* data = resp->data;
uint16_t len = resp->len;
// Procura por tag 0x6F (FCI Template)
uint8_t* fci = find_tlv_tag(data, len, 0x6F);
if (!fci) return false;
// Dentro do FCI, procura por 0xA5 (FCI Proprietary Template)
uint8_t* prop_template = find_tlv_tag(fci + 2, fci[1], 0xA5);
if (!prop_template) return false;
// Procura por 0x88 (SFI - Short File Identifier) do directory
uint8_t* sfi = find_tlv_tag(prop_template + 2, prop_template[1], 0x88);
if (!sfi) return false;
uint8_t sfi_value = sfi[2]; // Valor do SFI
// Lê records do directory usando READ RECORD
return read_pse_directory(sfi_value, selected_app);
}Fase 3: Initiate Application Processing
// Dados da aplicação EMV
typedef struct {
uint8_t aip[2]; // Application Interchange Profile
uint8_t afl[32]; // Application File Locator
uint8_t afl_len;
// Dados críticos para processamento
uint8_t pan[10]; // Primary Account Number
uint8_t pan_len;
uint8_t track2[19]; // Track 2 Equivalent Data
uint8_t track2_len;
// Dados de autenticação
uint8_t issuer_pk_cert[248]; // Issuer Public Key Certificate
uint16_t issuer_pk_cert_len;
uint8_t icc_pk_cert[248]; // ICC Public Key Certificate
uint16_t icc_pk_cert_len;
// Controle de risco
uint32_t amount_authorized; // Valor da transação
uint32_t amount_other; // Outros valores (cashback, etc)
uint8_t transaction_type; // Tipo da transação
} emv_application_data_t;
// GET PROCESSING OPTIONS - comando crítico
int initiate_application_processing(emv_application_data_t* app_data, uint32_t amount) {
apdu_command_t cmd;
apdu_response_t resp;
// Constrói PDOL (Processing Data Object List)
uint8_t pdol_data[64];
int pdol_len = build_pdol(pdol_data, amount);
// GET PROCESSING OPTIONS command
cmd.cla = 0x80; // Class byte proprietary
cmd.ins = 0xA8; // GET PROCESSING OPTIONS
cmd.p1 = 0x00;
cmd.p2 = 0x00;
cmd.lc = pdol_len;
memcpy(cmd.data, pdol_data, pdol_len);
cmd.le = 0x00;
printf("Enviando GET PROCESSING OPTIONS...\n");
printf("PDOL (%d bytes): ", pdol_len);
for (int i = 0; i < pdol_len; i++) {
printf("%02X ", pdol_data[i]);
}
printf("\n");
if (send_apdu(&cmd, &resp) != 0) {
printf("Erro enviando GET PROCESSING OPTIONS\n");
return -1;
}
if (resp.sw1 != 0x90 || resp.sw2 != 0x00) {
printf("GET PROCESSING OPTIONS rejeitado: SW1=%02X SW2=%02X\n",
resp.sw1, resp.sw2);
return -1;
}
// Parse response format
if (resp.data[0] == 0x77) {
// Format 2 (TLV encoded)
return parse_gpo_format2(&resp, app_data);
} else if (resp.data[0] == 0x80) {
// Format 1 (primitive)
return parse_gpo_format1(&resp, app_data);
} else {
printf("Formato de resposta GPO inválido: %02X\n", resp.data[0]);
return -1;
}
}
// Construção do PDOL (Processing Data Object List)
int build_pdol(uint8_t* pdol_data, uint32_t amount) {
int offset = 0;
// Tag 0x83 - Command Template
pdol_data[offset++] = 0x83;
pdol_data[offset++] = 0x00; // Length placeholder
int length_offset = offset - 1; // Guardar posição do length
int data_start = offset;
// Terminal data required by most cards
// 9F02 - Amount Authorized (6 bytes)
uint8_t amount_auth[6];
format_amount(amount, amount_auth);
memcpy(&pdol_data[offset], amount_auth, 6);
offset += 6;
// 9F03 - Amount Other (6 bytes) - zero para transações normais
memset(&pdol_data[offset], 0x00, 6);
offset += 6;
// 9F1A - Terminal Country Code (2 bytes) - Brasil = 0x0076
pdol_data[offset++] = 0x00;
pdol_data[offset++] = 0x76;
// 95 - Terminal Verification Results (5 bytes) - inicializado como zero
memset(&pdol_data[offset], 0x00, 5);
offset += 5;
// 5F2A - Transaction Currency Code (2 bytes) - BRL = 0x0986
pdol_data[offset++] = 0x09;
pdol_data[offset++] = 0x86;
// 9A - Transaction Date (3 bytes) - YYMMDD
time_t now = time(NULL);
struct tm* timeinfo = localtime(&now);
pdol_data[offset++] = (timeinfo->tm_year % 100); // YY
pdol_data[offset++] = (timeinfo->tm_mon + 1); // MM
pdol_data[offset++] = timeinfo->tm_mday; // DD
// 9C - Transaction Type (1 byte) - 0x00 = Purchase
pdol_data[offset++] = 0x00;
// 9F37 - Unpredictable Number (4 bytes) - random para segurança
uint32_t random = generate_random_number();
pdol_data[offset++] = (random >> 24) & 0xFF;
pdol_data[offset++] = (random >> 16) & 0xFF;
pdol_data[offset++] = (random >> 8) & 0xFF;
pdol_data[offset++] = random & 0xFF;
// Atualiza length do command template
pdol_data[length_offset] = offset - data_start;
return offset;
}
// Formata valor monetário para formato EMV (6 bytes BCD)
void format_amount(uint32_t amount_cents, uint8_t* bcd_amount) {
// EMV amount format: 6 bytes BCD, right-justified, zero-filled
// Exemplo: R$ 123.45 = 12345 cents = 0x000012345 BCD = 00 00 01 23 45
memset(bcd_amount, 0x00, 6);
for (int i = 5; i >= 0 && amount_cents > 0; i--) {
uint8_t digit = amount_cents % 10;
amount_cents /= 10;
if (i % 2 == 1) { // Nibble baixo
bcd_amount[i/2] |= digit;
} else { // Nibble alto
bcd_amount[i/2] |= (digit << 4);
}
}
}Fase 4: Read Application Data
// Leitura dos dados da aplicação usando AFL
int read_application_data(emv_application_data_t* app_data) {
uint8_t* afl = app_data->afl;
uint8_t afl_len = app_data->afl_len;
printf("Lendo dados da aplicação (AFL = %d entries)...\n", afl_len / 4);
// AFL format: cada 4 bytes definem um arquivo
// Byte 1: SFI (Short File Identifier)
// Byte 2: First record number
// Byte 3: Last record number
// Byte 4: Number of records involved in offline data auth
for (int i = 0; i < afl_len; i += 4) {
uint8_t sfi = afl[i] >> 3; // Remove 3 bits baixos
uint8_t first_rec = afl[i + 1];
uint8_t last_rec = afl[i + 2];
uint8_t offline_records = afl[i + 3];
printf("SFI %d: records %d-%d (%d para offline auth)\n",
sfi, first_rec, last_rec, offline_records);
// Lê todos os records do arquivo
for (uint8_t rec = first_rec; rec <= last_rec; rec++) {
if (read_record(sfi, rec, app_data) != 0) {
printf("Erro lendo SFI %d, record %d\n", sfi, rec);
return -1;
}
}
}
return 0;
}
// READ RECORD command
int read_record(uint8_t sfi, uint8_t record, emv_application_data_t* app_data) {
apdu_command_t cmd;
apdu_response_t resp;
cmd.cla = 0x00;
cmd.ins = 0xB2; // READ RECORD
cmd.p1 = record; // Record number
cmd.p2 = (sfi << 3) | 0x04; // SFI + P2 encoding
cmd.lc = 0x00; // No command data
cmd.le = 0x00; // Return all available
printf("READ RECORD SFI=%d REC=%d: ", sfi, record);
if (send_apdu(&cmd, &resp) != 0) {
printf("ERRO - falha comunicação\n");
return -1;
}
if (resp.sw1 != 0x90 || resp.sw2 != 0x00) {
printf("ERRO - SW1=%02X SW2=%02X\n", resp.sw1, resp.sw2);
return -1;
}
printf("OK (%d bytes)\n", resp.len);
// Parse TLV data do record
parse_emv_tlv_data(resp.data, resp.len, app_data);
return 0;
}
// Parser de dados EMV em formato TLV
void parse_emv_tlv_data(uint8_t* data, uint16_t len, emv_application_data_t* app_data) {
uint16_t offset = 0;
while (offset < len) {
// Parse tag
uint32_t tag = 0;
int tag_len = 0;
if (data[offset] & 0x1F) == 0x1F) { // Multi-byte tag
tag = (data[offset] << 8) | data[offset + 1];
tag_len = 2;
// Check for 3-byte tags (rare but possible)
if (offset + 2 < len && (data[offset + 1] & 0x80)) {
tag = (tag << 8) | data[offset + 2];
tag_len = 3;
}
} else { // Single byte tag
tag = data[offset];
tag_len = 1;
}
offset += tag_len;
if (offset >= len) break;
// Parse length
uint16_t value_len = 0;
if (data[offset] & 0x80) { // Long form
uint8_t len_octets = data[offset] & 0x7F;
offset++;
for (int i = 0; i < len_octets && offset < len; i++) {
value_len = (value_len << 8) | data[offset++];
}
} else { // Short form
value_len = data[offset++];
}
if (offset + value_len > len) break;
// Parse value baseado na tag
switch (tag) {
case 0x5A: // PAN (Primary Account Number)
app_data->pan_len = min(value_len, sizeof(app_data->pan));
memcpy(app_data->pan, &data[offset], app_data->pan_len);
printf(" PAN: ");
for (int i = 0; i < app_data->pan_len; i++) {
printf("%02X ", app_data->pan[i]);
}
printf("\n");
break;
case 0x57: // Track 2 Equivalent Data
app_data->track2_len = min(value_len, sizeof(app_data->track2));
memcpy(app_data->track2, &data[offset], app_data->track2_len);
printf(" Track2: ");
for (int i = 0; i < app_data->track2_len; i++) {
printf("%02X ", app_data->track2[i]);
}
printf("\n");
break;
case 0x90: // Issuer Public Key Certificate
app_data->issuer_pk_cert_len = min(value_len, sizeof(app_data->issuer_pk_cert));
memcpy(app_data->issuer_pk_cert, &data[offset], app_data->issuer_pk_cert_len);
printf(" Issuer PK Cert (%d bytes)\n", app_data->issuer_pk_cert_len);
break;
case 0x9F46: // ICC Public Key Certificate
app_data->icc_pk_cert_len = min(value_len, sizeof(app_data->icc_pk_cert));
memcpy(app_data->icc_pk_cert, &data[offset], app_data->icc_pk_cert_len);
printf(" ICC PK Cert (%d bytes)\n", app_data->icc_pk_cert_len);
break;
default:
printf(" Tag %04X: %d bytes (não processado)\n", tag, value_len);
break;
}
offset += value_len;
}
}Implementando Criptografia EMV
A segurança EMV é baseada em criptografia de chave pública RSA com hierarquia de certificados.
Hierarquia de Chaves EMV
// Estrutura de chaves EMV (hierarquia de 3 níveis)
typedef struct {
// Nível 1: Certificate Authority (EMVCo/Schemes)
uint8_t ca_modulus[248]; // Chave pública da CA
uint16_t ca_modulus_len;
uint8_t ca_exponent[3]; // Expoente (geralmente 0x010001)
// Nível 2: Issuer (Banco Emissor)
uint8_t issuer_modulus[248]; // Chave pública do emissor
uint16_t issuer_modulus_len;
uint8_t issuer_exponent[3];
uint8_t issuer_cert[248]; // Certificado assinado pela CA
// Nível 3: ICC (Integrated Circuit Card)
uint8_t icc_modulus[248]; // Chave pública do cartão
uint16_t icc_modulus_len;
uint8_t icc_exponent[3];
uint8_t icc_cert[248]; // Certificado assinado pelo emissor
} emv_key_hierarchy_t;
// Validação da cadeia de certificados EMV
typedef enum {
CERT_VALID = 0,
CERT_INVALID_SIGNATURE = 1,
CERT_EXPIRED = 2,
CERT_REVOKED = 3,
CERT_UNKNOWN_CA = 4
} cert_validation_result_t;
cert_validation_result_t validate_certificate_chain(emv_key_hierarchy_t* keys) {
printf("Validando cadeia de certificados EMV...\n");
// 1. Valida certificado do emissor com chave da CA
printf("1. Validando certificado do emissor...\n");
if (!rsa_verify_signature(keys->issuer_cert, keys->issuer_modulus_len,
keys->ca_modulus, keys->ca_modulus_len,
keys->ca_exponent)) {
printf("ERRO: Certificado do emissor inválido\n");
return CERT_INVALID_SIGNATURE;
}
// 2. Extrai chave pública do emissor do certificado
if (!extract_issuer_public_key(keys->issuer_cert, keys)) {
printf("ERRO: Falha extraindo chave do emissor\n");
return CERT_INVALID_SIGNATURE;
}
// 3. Valida certificado do ICC com chave do emissor
printf("2. Validando certificado do ICC...\n");
if (!rsa_verify_signature(keys->icc_cert, keys->icc_modulus_len,
keys->issuer_modulus, keys->issuer_modulus_len,
keys->issuer_exponent)) {
printf("ERRO: Certificado do ICC inválido\n");
return CERT_INVALID_SIGNATURE;
}
// 4. Verifica validade temporal
if (!check_certificate_validity_period(keys->issuer_cert)) {
printf("ERRO: Certificado do emissor expirado\n");
return CERT_EXPIRED;
}
printf("✅ Cadeia de certificados validada com sucesso\n");
return CERT_VALID;
}
// Implementação RSA para verificação de assinaturas
bool rsa_verify_signature(uint8_t* signature, uint16_t sig_len,
uint8_t* modulus, uint16_t mod_len,
uint8_t* exponent) {
// Implementação RSA básica para EMV
// ATENÇÃO: Usar biblioteca criptográfica certificada em produção
// 1. Converte bytes para big integers
bigint_t sig_int, mod_int, exp_int, result_int;
bytes_to_bigint(signature, sig_len, &sig_int);
bytes_to_bigint(modulus, mod_len, &mod_int);
bytes_to_bigint(exponent, 3, &exp_int);
// 2. Operação RSA: result = signature^exponent mod modulus
bigint_modpow(&sig_int, &exp_int, &mod_int, &result_int);
// 3. Converte resultado para bytes
uint8_t decrypted[248];
bigint_to_bytes(&result_int, decrypted, sizeof(decrypted));
// 4. Verifica padding PKCS#1 v1.5
if (decrypted[0] != 0x00 || decrypted[1] != 0x01) {
printf("ERRO: Padding RSA inválido\n");
return false;
}
// 5. Encontra separador 0x00 após padding 0xFF
int separator_pos = -1;
for (int i = 2; i < mod_len; i++) {
if (decrypted[i] == 0x00) {
separator_pos = i;
break;
} else if (decrypted[i] != 0xFF) {
printf("ERRO: Padding 0xFF corrompido na posição %d\n", i);
return false;
}
}
if (separator_pos == -1 || separator_pos < 10) {
printf("ERRO: Separador de padding não encontrado\n");
return false;
}
// 6. Hash dos dados e comparação
uint8_t* hash_in_signature = &decrypted[separator_pos + 1];
return verify_hash_matches(hash_in_signature, mod_len - separator_pos - 1);
}Fase 5: Offline Data Authentication
// Autenticação offline dos dados do cartão
typedef enum {
AUTH_METHOD_SDA = 1, // Static Data Authentication
AUTH_METHOD_DDA = 2, // Dynamic Data Authentication
AUTH_METHOD_CDA = 3 // Combined Data Authentication
} offline_auth_method_t;
typedef struct {
offline_auth_method_t method;
bool authentication_success;
uint8_t signed_data[255]; // Dados assinados pelo cartão
uint16_t signed_data_len;
uint8_t hash_result[20]; // SHA-1 dos dados
} offline_auth_result_t;
// Executa autenticação offline baseada nas capacidades do cartão
offline_auth_result_t perform_offline_authentication(emv_application_data_t* app_data,
emv_key_hierarchy_t* keys) {
offline_auth_result_t result = {0};
// Determina método baseado no AIP (Application Interchange Profile)
uint8_t aip_byte1 = app_data->aip[0];
if (aip_byte1 & 0x40) { // Bit 7 = DDA supported
result.method = AUTH_METHOD_DDA;
printf("Executando Dynamic Data Authentication (DDA)...\n");
return perform_dda(app_data, keys);
} else if (aip_byte1 & 0x80) { // Bit 8 = SDA supported
result.method = AUTH_METHOD_SDA;
printf("Executando Static Data Authentication (SDA)...\n");
return perform_sda(app_data, keys);
} else {
printf("⚠️ Cartão não suporta autenticação offline\n");
result.method = AUTH_METHOD_SDA; // Fallback
result.authentication_success = false;
return result;
}
}
// Dynamic Data Authentication - mais seguro
offline_auth_result_t perform_dda(emv_application_data_t* app_data,
emv_key_hierarchy_t* keys) {
offline_auth_result_t result = {AUTH_METHOD_DDA, false};
apdu_command_t cmd;
apdu_response_t resp;
// 1. INTERNAL AUTHENTICATE command
cmd.cla = 0x00;
cmd.ins = 0x88; // INTERNAL AUTHENTICATE
cmd.p1 = 0x00;
cmd.p2 = 0x00;
cmd.lc = 0x00; // No command data
cmd.le = 0x00; // Return all available
printf("Enviando INTERNAL AUTHENTICATE...\n");
if (send_apdu(&cmd, &resp) != 0) {
printf("ERRO: Falha no INTERNAL AUTHENTICATE\n");
return result;
}
if (resp.sw1 != 0x90 || resp.sw2 != 0x00) {
printf("INTERNAL AUTHENTICATE rejeitado: SW1=%02X SW2=%02X\n",
resp.sw1, resp.sw2);
return result;
}
// 2. Parse response (Signed Dynamic Application Data)
uint8_t* signed_data = resp.data;
uint16_t signed_len = resp.len;
printf("Dados assinados recebidos: %d bytes\n", signed_len);
// 3. Verifica assinatura usando chave pública do ICC
if (!rsa_verify_signature(signed_data, signed_len,
keys->icc_modulus, keys->icc_modulus_len,
keys->icc_exponent)) {
printf("ERRO: Assinatura DDA inválida\n");
return result;
}
// 4. Valida conteúdo dos dados assinados
if (!validate_signed_data_content(signed_data, signed_len, app_data)) {
printf("ERRO: Conteúdo dos dados assinados inválido\n");
return result;
}
printf("✅ Dynamic Data Authentication bem-sucedida\n");
result.authentication_success = true;
memcpy(result.signed_data, signed_data, signed_len);
result.signed_data_len = signed_len;
return result;
}
// Static Data Authentication - método básico
offline_auth_result_t perform_sda(emv_application_data_t* app_data,
emv_key_hierarchy_t* keys) {
offline_auth_result_t result = {AUTH_METHOD_SDA, false};
printf("Executando Static Data Authentication (SDA)...\n");
// 1. Constrói string de dados para hash
uint8_t data_to_hash[512];
uint16_t hash_len = 0;
// Concatena dados críticos conforme especificação EMV
// PAN + Exp Date + Service Code + etc.
if (!build_sda_data_string(app_data, data_to_hash, &hash_len)) {
printf("ERRO: Falha construindo string de dados SDA\n");
return result;
}
// 2. Calcula hash SHA-1
uint8_t calculated_hash[20];
sha1_calculate(data_to_hash, hash_len, calculated_hash);
// 3. Obtém hash assinado do Signed Static Application Data
uint8_t* signed_hash = find_emv_tag(app_data, 0x93); // Tag 93 = Signed Static Application Data
if (!signed_hash) {
printf("ERRO: Signed Static Application Data não encontrado\n");
return result;
}
// 4. Verifica assinatura
if (!rsa_verify_signature(signed_hash, keys->issuer_modulus_len,
keys->issuer_modulus, keys->issuer_modulus_len,
keys->issuer_exponent)) {
printf("ERRO: Assinatura SDA inválida\n");
return result;
}
// 5. Compara hashes
if (memcmp(calculated_hash, /* hash from signature */, 20) == 0) {
printf("✅ Static Data Authentication bem-sucedida\n");
result.authentication_success = true;
} else {
printf("ERRO: Hash SDA não confere\n");
}
return result;
}Fase 6: Cardholder Verification Method (CVM)
// Métodos de verificação do portador
typedef enum {
CVM_NONE = 0x00,
CVM_PIN_ONLINE = 0x01, // PIN verificado online
CVM_PIN_OFFLINE_PLAIN = 0x02, // PIN verificado offline (plain)
CVM_PIN_OFFLINE_ENCRYPTED = 0x03, // PIN verificado offline (encrypted)
CVM_SIGNATURE = 0x1E, // Assinatura manuscrita
CVM_NO_CVM = 0x1F // Sem verificação (contactless <R$50)
} cvm_method_t;
typedef struct {
cvm_method_t method;
bool cvm_required;
bool cvm_successful;
uint8_t pin_try_counter;
} cvm_result_t;
// Processamento da CVM List
cvm_result_t process_cardholder_verification(emv_application_data_t* app_data,
uint32_t amount) {
cvm_result_t result = {CVM_NONE, false, false, 0};
// Obtém CVM List do cartão (tag 8E)
uint8_t* cvm_list = find_emv_tag(app_data, 0x8E);
if (!cvm_list) {
printf("CVM List não encontrada - sem verificação\n");
return result;
}
uint8_t cvm_list_len = get_emv_tag_length(app_data, 0x8E);
printf("CVM List encontrada: %d bytes\n", cvm_list_len);
// Primeiros 8 bytes = Amount X, Amount Y para thresholds
uint32_t amount_x = bytes_to_uint32(&cvm_list[0]); // Threshold para PIN
uint32_t amount_y = bytes_to_uint32(&cvm_list[4]); // Threshold para signature
printf("Thresholds: PIN=%d, Signature=%d, Transação=%d\n",
amount_x, amount_y, amount);
// Processa regras CVM (2 bytes por regra)
for (int i = 8; i < cvm_list_len; i += 2) {
uint8_t cvm_code = cvm_list[i] & 0x3F; // 6 bits baixos = método
bool condition_satisfied = true;
// Avalia condição da regra
uint8_t condition = cvm_list[i + 1];
switch (condition) {
case 0x00: // Always
condition_satisfied = true;
break;
case 0x01: // If unattended cash
condition_satisfied = false; // Terminal assistido
break;
case 0x02: // If not unattended cash and not manual cash and not purchase with cashback
condition_satisfied = true; // Compra normal
break;
case 0x03: // If terminal supports CVM
condition_satisfied = terminal_supports_cvm(cvm_code);
break;
case 0x04: // If manual cash
condition_satisfied = false; // Não é cash
break;
case 0x05: // If purchase with cashback
condition_satisfied = false; // Sem cashback
break;
case 0x06: // If transaction is in application currency
condition_satisfied = true; // BRL = moeda da aplicação
break;
default:
if (condition >= 0x80) { // Amount conditions
uint32_t threshold = (condition == 0x80) ? amount_x : amount_y;
condition_satisfied = (amount <= threshold);
}
break;
}
if (condition_satisfied) {
printf("Regra CVM aplicável: método %02X\n", cvm_code);
// Executa método CVM
result.method = (cvm_method_t)cvm_code;
result.cvm_required = true;
switch (cvm_code) {
case CVM_PIN_OFFLINE_ENCRYPTED:
result.cvm_successful = perform_offline_pin_verification(app_data);
break;
case CVM_PIN_ONLINE:
result.cvm_successful = true; // PIN será verificado online
break;
case CVM_SIGNATURE:
result.cvm_successful = true; // Signature prompting
printf("👤 Solicitar assinatura do portador\n");
break;
case CVM_NO_CVM:
result.cvm_successful = true; // Sem verificação necessária
break;
default:
printf("Método CVM %02X não suportado pelo terminal\n", cvm_code);
result.cvm_successful = false;
break;
}
// Se CVM foi bem-sucedido ou é fail CVM, para o processamento
bool fail_cvm = (cvm_list[i] & 0x40) != 0; // Bit 7 = fail CVM
if (result.cvm_successful || !fail_cvm) {
break; // Sai do loop
}
}
}
return result;
}
// Verificação de PIN offline com criptografia
bool perform_offline_pin_verification(emv_application_data_t* app_data) {
apdu_command_t cmd;
apdu_response_t resp;
// Obtém PIN do usuário (interface com pinpad)
char user_pin[13]; // Máximo 12 dígitos + null terminator
if (!get_pin_from_user(user_pin, sizeof(user_pin))) {
printf("PIN não fornecido pelo usuário\n");
return false;
}
// Constrói PIN block (formato 2)
uint8_t pin_block[8];
if (!format_pin_block(user_pin, app_data->pan, pin_block)) {
printf("ERRO: Falha formatando PIN block\n");
return false;
}
// VERIFY PIN command
cmd.cla = 0x00;
cmd.ins = 0x20; // VERIFY
cmd.p1 = 0x00; // PIN reference
cmd.p2 = 0x80; // PIN format 2
cmd.lc = 8; // PIN block length
memcpy(cmd.data, pin_block, 8);
cmd.le = 0x00;
printf("Verificando PIN offline...\n");
if (send_apdu(&cmd, &resp) != 0) {
printf("ERRO: Falha enviando VERIFY PIN\n");
return false;
}
if (resp.sw1 == 0x90 && resp.sw2 == 0x00) {
printf("✅ PIN verificado com sucesso\n");
return true;
} else if (resp.sw1 == 0x63) {
// PIN incorreto - SW2 indica tentativas restantes
uint8_t tries_left = resp.sw2 & 0x0F;
printf("❌ PIN incorreto. Tentativas restantes: %d\n", tries_left);
if (tries_left == 0) {
printf("🔒 Cartão bloqueado por PIN\n");
}
return false;
} else {
printf("VERIFY PIN falhou: SW1=%02X SW2=%02X\n", resp.sw1, resp.sw2);
return false;
}
}
// Formatação do PIN block conforme ISO 9564-1 Format 2
bool format_pin_block(const char* pin, uint8_t* pan, uint8_t* pin_block) {
uint8_t pin_len = strlen(pin);
if (pin_len < 4 || pin_len > 12) {
return false; // PIN deve ter 4-12 dígitos
}
// Constrói PIN field: 2 + PIN length + PIN digits + padding F
uint8_t pin_field[8];
pin_field[0] = 0x20 | pin_len; // 0x2 + PIN length
int byte_pos = 1;
for (int i = 0; i < pin_len; i++) {
if (i % 2 == 0) { // Primeiro dígito do byte
pin_field[byte_pos] = (pin[i] - '0') << 4;
} else { // Segundo dígito do byte
pin_field[byte_pos] |= (pin[i] - '0');
byte_pos++;
}
}
// Padding com 0xF se necessário
if (pin_len % 2 == 1) { // PIN length ímpar
pin_field[byte_pos] |= 0x0F;
byte_pos++;
}
// Completa com 0xFF
while (byte_pos < 8) {
pin_field[byte_pos++] = 0xFF;
}
// Constrói PAN field: 0000 + 12 dígitos direitos do PAN
uint8_t pan_field[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
// Extrai dígitos do PAN (formato BCD)
int pan_digits = 0;
for (int i = app_data->pan_len - 1; i >= 0 && pan_digits < 12; i--) {
uint8_t byte_val = app_data->pan[i];
// Nibble baixo
if ((byte_val & 0x0F) != 0x0F && pan_digits < 12) {
pan_field[7 - pan_digits/2] |= (byte_val & 0x0F) << ((pan_digits % 2) * 4);
pan_digits++;
}
// Nibble alto
if (((byte_val >> 4) & 0x0F) != 0x0F && pan_digits < 12) {
pan_field[7 - pan_digits/2] |= ((byte_val >> 4) & 0x0F) << ((pan_digits % 2) * 4);
pan_digits++;
}
}
// PIN block = PIN field XOR PAN field
for (int i = 0; i < 8; i++) {
pin_block[i] = pin_field[i] ^ pan_field[i];
}
printf("PIN block: ");
for (int i = 0; i < 8; i++) {
printf("%02X ", pin_block[i]);
}
printf("\n");
return true;
}Fase 7: Terminal Risk Management
// Gerenciamento de risco do terminal
typedef struct {
bool floor_limit_exceeded;
bool random_selection;
bool velocity_checking;
bool offline_limit_exceeded;
bool issuer_authentication_failed;
bool application_blocked;
} risk_management_flags_t;
typedef enum {
RISK_DECISION_APPROVE_OFFLINE = 0,
RISK_DECISION_DECLINE_OFFLINE = 1,
RISK_DECISION_GO_ONLINE = 2
} risk_decision_t;
// Análise de risco terminal
risk_decision_t perform_terminal_risk_management(emv_application_data_t* app_data,
uint32_t amount,
risk_management_flags_t* flags) {
printf("Executando Terminal Risk Management...\n");
memset(flags, 0, sizeof(risk_management_flags_t));
// 1. Floor Limit Check
uint32_t floor_limit = get_emv_tag_uint32(app_data, 0x9F1B); // Terminal Floor Limit
if (amount > floor_limit) {
flags->floor_limit_exceeded = true;
printf("Floor limit excedido: %d > %d\n", amount, floor_limit);
}
// 2. Random Selection
uint8_t random_threshold = get_emv_tag_uint8(app_data, 0x9F1D); // Terminal Risk Management Data
uint8_t random_value = generate_random_uint8();
if (random_value < random_threshold) {
flags->random_selection = true;
printf("Seleção aleatória para online: %d < %d\n", random_value, random_threshold);
}
// 3. Velocity Checking (transações consecutivas)
static uint32_t consecutive_offline_transactions = 0;
uint32_t velocity_limit = get_emv_tag_uint32(app_data, 0x9F14); // Lower Consecutive Offline Limit
if (consecutive_offline_transactions >= velocity_limit) {
flags->velocity_checking = true;
printf("Limite de transações offline consecutivas excedido: %d >= %d\n",
consecutive_offline_transactions, velocity_limit);
}
// 4. Cumulative Transaction Amount Check
static uint32_t cumulative_offline_amount = 0;
uint32_t cumulative_limit = get_emv_tag_uint32(app_data, 0x9F75); // Cumulative Total Transaction Amount Limit
if (cumulative_offline_amount + amount > cumulative_limit) {
flags->offline_limit_exceeded = true;
printf("Limite cumulativo excedido: %d + %d > %d\n",
cumulative_offline_amount, amount, cumulative_limit);
}
// 5. Análise final de risco
bool force_online = flags->floor_limit_exceeded ||
flags->random_selection ||
flags->velocity_checking ||
flags->offline_limit_exceeded;
if (force_online) {
printf("Decisão: IR ONLINE (risk management)\n");
return RISK_DECISION_GO_ONLINE;
}
// Verifica se aplicação/emissor permite offline
uint8_t aip_byte1 = app_data->aip[0];
if (!(aip_byte1 & 0x20)) { // Bit 6 = Offline approval supported
printf("Decisão: IR ONLINE (aplicação não suporta offline)\n");
return RISK_DECISION_GO_ONLINE;
}
printf("Decisão: APROVAR OFFLINE\n");
// Atualiza contadores para próximas transações
consecutive_offline_transactions++;
cumulative_offline_amount += amount;
return RISK_DECISION_APPROVE_OFFLINE;
}Fase 8: Transaction Authorization
// Autorização da transação (online ou offline)
typedef struct {
bool transaction_approved;
uint8_t response_code[2]; // Código de resposta do emissor
uint8_t auth_code[6]; // Código de autorização
uint8_t arpc[8]; // Authorization Response Cryptogram
bool cryptogram_valid;
uint32_t transaction_sequence;
} transaction_result_t;
// GENERATE AC (Application Cryptogram) - comando crítico final
transaction_result_t generate_application_cryptogram(emv_application_data_t* app_data,
risk_decision_t risk_decision,
cvm_result_t cvm_result) {
transaction_result_t result = {0};
apdu_command_t cmd;
apdu_response_t resp;
// Determina tipo de criptograma baseado na decisão de risco
uint8_t cryptogram_type;
switch (risk_decision) {
case RISK_DECISION_APPROVE_OFFLINE:
cryptogram_type = 0x80; // TC (Transaction Certificate)
printf("Solicitando Transaction Certificate (aprovação offline)...\n");
break;
case RISK_DECISION_GO_ONLINE:
cryptogram_type = 0x00; // ARQC (Authorization Request Cryptogram)
printf("Solicitando ARQC (autorização online)...\n");
break;
case RISK_DECISION_DECLINE_OFFLINE:
cryptogram_type = 0x00; // AAC (Application Authentication Cryptogram)
printf("Solicitando AAC (decline offline)...\n");
break;
}
// Constrói CDOL1 (Card Data Object List 1)
uint8_t cdol1_data[128];
int cdol1_len = build_cdol1(app_data, cvm_result, cdol1_data);
// GENERATE AC command
cmd.cla = 0x80; // Class proprietary
cmd.ins = 0xAE; // GENERATE AC
cmd.p1 = cryptogram_type;
cmd.p2 = 0x00;
cmd.lc = cdol1_len;
memcpy(cmd.data, cdol1_data, cdol1_len);
cmd.le = 0x00;
printf("Enviando GENERATE AC (tipo=%02X, CDOL1=%d bytes)...\n",
cryptogram_type, cdol1_len);
if (send_apdu(&cmd, &resp) != 0) {
printf("ERRO: Falha no GENERATE AC\n");
return result;
}
if (resp.sw1 != 0x90 || resp.sw2 != 0x00) {
printf("GENERATE AC rejeitado: SW1=%02X SW2=%02X\n", resp.sw1, resp.sw2);
return result;
}
// Parse response format (TLV)
uint8_t* cid = find_tlv_tag(resp.data, resp.len, 0x9F27); // Cryptogram Information Data
uint8_t* ac = find_tlv_tag(resp.data, resp.len, 0x9F26); // Application Cryptogram
uint8_t* atc = find_tlv_tag(resp.data, resp.len, 0x9F36); // Application Transaction Counter
if (!cid || !ac || !atc) {
printf("ERRO: Dados críticos ausentes na resposta AC\n");
return result;
}
printf("Cryptogram Info Data: %02X\n", cid[2]);
printf("Application Cryptogram: ");
for (int i = 0; i < 8; i++) {
printf("%02X ", ac[i + 2]);
}
printf("\n");
// Verifica tipo de criptograma retornado
uint8_t returned_cid = cid[2];
switch (returned_cid & 0xC0) { // 2 bits altos determinam tipo
case 0x80: // TC (Transaction Certificate)
printf("✅ Transaction Certificate recebido - APROVADO OFFLINE\n");
result.transaction_approved = true;
break;
case 0x40: // ARQC (Authorization Request Cryptogram)
printf("📡 ARQC recebido - ENVIANDO PARA AUTORIZAÇÃO ONLINE\n");
result = process_online_authorization(app_data, ac + 2, atc + 2);
break;
case 0x00: // AAC (Application Authentication Cryptogram)
printf("❌ AAC recebido - TRANSAÇÃO NEGADA\n");
result.transaction_approved = false;
break;
default:
printf("ERRO: Tipo de criptograma inválido: %02X\n", returned_cid);
result.transaction_approved = false;
break;
}
return result;
}
// Processamento de autorização online
transaction_result_t process_online_authorization(emv_application_data_t* app_data,
uint8_t* arqc, uint8_t* atc) {
transaction_result_t result = {0};
printf("Processando autorização online...\n");
// 1. Constrói mensagem ISO 8583 com dados EMV
uint8_t iso_message[1024];
int iso_len = build_iso8583_message(app_data, arqc, atc, iso_message);
// 2. Envia para adquirente via TCP/IP
uint8_t iso_response[1024];
int response_len = send_online_authorization(iso_message, iso_len, iso_response);
if (response_len <= 0) {
printf("ERRO: Falha na comunicação online\n");
result.transaction_approved = false;
return result;
}
// 3. Parse resposta ISO 8583
if (!parse_iso8583_response(iso_response, response_len, &result)) {
printf("ERRO: Resposta online inválida\n");
result.transaction_approved = false;
return result;
}
// 4. Se aprovado online, executa segundo GENERATE AC
if (result.transaction_approved) {
printf("Autorização online APROVADA - executando 2nd GENERATE AC...\n");
return execute_second_generate_ac(app_data, &result);
} else {
printf("Autorização online NEGADA - código: %02X%02X\n",
result.response_code[0], result.response_code[1]);
return result;
}
}
// Segundo GENERATE AC (após aprovação online)
transaction_result_t execute_second_generate_ac(emv_application_data_t* app_data,
transaction_result_t* online_result) {
apdu_command_t cmd;
apdu_response_t resp;
// Constrói CDOL2 (incluindo dados da autorização online)
uint8_t cdol2_data[128];
int cdol2_len = build_cdol2(app_data, online_result, cdol2_data);
// GENERATE AC command para TC (Transaction Certificate)
cmd.cla = 0x80;
cmd.ins = 0xAE; // GENERATE AC
cmd.p1 = 0x40; // Request TC
cmd.p2 = 0x00;
cmd.lc = cdol2_len;
memcpy(cmd.data, cdol2_data, cdol2_len);
cmd.le = 0x00;
printf("Enviando 2nd GENERATE AC para TC...\n");
if (send_apdu(&cmd, &resp) != 0 || resp.sw1 != 0x90) {
printf("ERRO: 2nd GENERATE AC falhou\n");
online_result->transaction_approved = false;
return *online_result;
}
// Parse TC
uint8_t* tc = find_tlv_tag(resp.data, resp.len, 0x9F26); // Application Cryptogram
if (tc) {
printf("✅ Transaction Certificate recebido - TRANSAÇÃO COMPLETADA\n");
printf("TC: ");
for (int i = 0; i < 8; i++) {
printf("%02X ", tc[i + 2]);
}
printf("\n");
}
return *online_result;
}Implementando Contactless (NFC)
EMV Contactless adiciona uma camada de complexidade com protocolos NFC e limites de velocidade.
Detecção e Ativação NFC
// Sistema contactless completo
typedef struct {
uint8_t technology; // Type A, Type B, ou FeliCa
uint8_t uid[10]; // Unique identifier
uint8_t uid_len;
uint8_t sak; // Select acknowledge
uint8_t atqa[2]; // Answer to request
bool supports_emv; // Suporta protocolos EMV
} nfc_card_info_t;
// Detecção de cartão contactless
int detect_contactless_card(nfc_card_info_t* card_info) {
printf("Iniciando detecção contactless...\n");
// 1. RF Field ON
nfc_rf_field_enable();
delay_milliseconds(5); // Tempo para estabilização
// 2. Polling para diferentes tecnologias
// ISO 14443 Type A (Visa, Mastercard)
uint8_t reqa[] = {0x26}; // REQUEST Type A
uint8_t atqa[2];
if (nfc_transceive(reqa, 1, atqa, 2, 1000) == 2) { // 1ms timeout
printf("Type A detectado - ATQA: %02X %02X\n", atqa[0], atqa[1]);
card_info->technology = NFC_TYPE_A;
memcpy(card_info->atqa, atqa, 2);
// Anti-collision para obter UID
if (perform_type_a_anticollision(card_info) == 0) {
return check_emv_support_type_a(card_info);
}
}
// ISO 14443 Type B (menos comum, mas alguns bancos usam)
uint8_t reqb[] = {0x05, 0x00, 0x00}; // REQUEST Type B
uint8_t atqb[12];
if (nfc_transceive(reqb, 3, atqb, 12, 1000) >= 10) {
printf("Type B detectado\n");
card_info->technology = NFC_TYPE_B;
return process_type_b_card(card_info, atqb);
}
printf("Nenhum cartão contactless detectado\n");
nfc_rf_field_disable();
return -1;
}
// Anti-collision para Type A
int perform_type_a_anticollision(nfc_card_info_t* card_info) {
uint8_t cascade_level = 1;
uint8_t uid_offset = 0;
while (cascade_level <= 3) { // Máximo 3 níveis de cascade
uint8_t sel_cmd[2] = {0x93 + (cascade_level - 1) * 2, 0x20}; // SEL + NVB
uint8_t uid_response[5];
// SELECT command
if (nfc_transceive(sel_cmd, 2, uid_response, 5, 1000) != 5) {
printf("ERRO: SELECT falhou no cascade level %d\n", cascade_level);
return -1;
}
// Verifica BCC (Block Check Character)
uint8_t bcc_calc = uid_response[0] ^ uid_response[1] ^ uid_response[2] ^ uid_response[3];
if (bcc_calc != uid_response[4]) {
printf("ERRO: BCC inválido\n");
return -1;
}
// Copia UID
if (uid_response[0] == 0x88) { // Cascade Tag
memcpy(&card_info->uid[uid_offset], &uid_response[1], 3);
uid_offset += 3;
cascade_level++;
} else {
memcpy(&card_info->uid[uid_offset], uid_response, 4);
uid_offset += 4;
break; // UID completo
}
}
card_info->uid_len = uid_offset;
printf("UID completo (%d bytes): ", card_info->uid_len);
for (int i = 0; i < card_info->uid_len; i++) {
printf("%02X ", card_info->uid[i]);
}
printf("\n");
return 0;
}
// Verifica suporte EMV em cartão contactless
int check_emv_support_type_a(nfc_card_info_t* card_info) {
// SELECT PPSE (Proximity Payment System Environment)
uint8_t ppse_name[] = "2PAY.SYS.DDF01"; // 14 bytes
uint8_t select_ppse[] = {0x00, 0xA4, 0x04, 0x00, 0x0E}; // Header + length
uint8_t select_cmd[32];
memcpy(select_cmd, select_ppse, 5);
memcpy(select_cmd + 5, ppse_name, 14);
select_cmd[19] = 0x00; // Le = 0 (return all available)
uint8_t ppse_response[256];
int resp_len = nfc_transceive(select_cmd, 20, ppse_response, 256, 5000); // 5ms timeout
if (resp_len >= 2 && ppse_response[resp_len-2] == 0x90 && ppse_response[resp_len-1] == 0x00) {
printf("✅ PPSE encontrado - cartão suporta EMV contactless\n");
card_info->supports_emv = true;
return 0;
} else {
printf("PPSE não encontrado - tentando AIDs diretos...\n");
return try_direct_aid_selection(card_info);
}
}
// Processamento específico contactless
typedef struct {
uint32_t contactless_limit; // Limite para transações sem PIN
bool mag_stripe_mode; // Modo magnetic stripe (MSD)
bool emv_mode; // Modo EMV completo
uint8_t kernel_id; // Kernel EMV (Visa=A, MC=B, etc.)
} contactless_config_t;
int process_contactless_transaction(nfc_card_info_t* card_info,
uint32_t amount,
contactless_config_t* config) {
printf("Processando transação contactless (valor: R$ %.2f)...\n", amount/100.0f);
// Verifica limite contactless
if (amount > config->contactless_limit) {
printf("Valor excede limite contactless: %d > %d\n", amount, config->contactless_limit);
printf("💳 Solicitar inserção do cartão para PIN\n");
return -1;
}
// Tenta modo EMV primeiro (mais seguro)
if (config->emv_mode && card_info->supports_emv) {
printf("Modo: EMV Contactless\n");
return process_emv_contactless(card_info, amount);
}
// Fallback para Magnetic Stripe Mode
if (config->mag_stripe_mode) {
printf("Modo: Magnetic Stripe Data (MSD)\n");
return process_msd_contactless(card_info, amount);
}
printf("ERRO: Nenhum modo contactless suportado\n");
return -1;
}
// EMV Contactless (modo preferido)
int process_emv_contactless(nfc_card_info_t* card_info, uint32_t amount) {
// Protocolo mais simples que contact, mas mesma segurança
// 1. SELECT PPSE
application_t selected_app;
if (select_ppse_contactless(&selected_app) != 0) {
printf("ERRO: Falha selecionando aplicação PPSE\n");
return -1;
}
// 2. GET PROCESSING OPTIONS (simplificado para contactless)
emv_application_data_t app_data;
if (gpo_contactless(&app_data, amount) != 0) {
printf("ERRO: GPO contactless falhou\n");
return -1;
}
// 3. READ APPLICATION DATA (limitado por timing contactless)
if (read_app_data_contactless(&app_data) != 0) {
printf("ERRO: Leitura de dados falhou\n");
return -1;
}
// 4. Offline Data Authentication (se suportado)
offline_auth_result_t auth_result = perform_offline_authentication(&app_data, NULL);
// 5. CVM (geralmente "No CVM" para valores baixos)
cvm_result_t cvm_result = {CVM_NO_CVM, false, true, 0};
// 6. Terminal Risk Management
risk_management_flags_t risk_flags;
risk_decision_t risk_decision = perform_terminal_risk_management(&app_data, amount, &risk_flags);
// 7. GENERATE AC
transaction_result_t final_result = generate_application_cryptogram(&app_data, risk_decision, cvm_result);
return final_result.transaction_approved ? 0 : -1;
}Certificação e Compliance
Níveis de Certificação EMV
// Processo de certificação EMVCo
typedef enum {
CERT_LEVEL_1 = 1, // Type approval (design/implementação)
CERT_LEVEL_2 = 2, // Product approval (produto específico)
CERT_LEVEL_3 = 3, // Process approval (processo de produção)
CERT_LEVEL_4 = 4 // Deployment approval (terminal específico)
} emv_certification_level_t;
typedef struct {
emv_certification_level_t level;
char certificate_id[32];
char test_lab[64]; // Laboratório certificador
uint32_t cost_usd; // Custo típico
uint32_t duration_weeks; // Duração típica
bool required_for_production; // Obrigatório para produção
} certification_info_t;
// Matriz de certificações necessárias
certification_info_t emv_certifications[] = {
{
.level = CERT_LEVEL_1,
.certificate_id = "EMV-L1-CONTACT-KERNEL",
.test_lab = "UL, Fime, ou Brightsight",
.cost_usd = 50000,
.duration_weeks = 12,
.required_for_production = true
},
{
.level = CERT_LEVEL_1,
.certificate_id = "EMV-L1-CONTACTLESS-KERNEL",
.test_lab = "UL, Fime, ou Brightsight",
.cost_usd = 75000,
.duration_weeks = 16,
.required_for_production = true
},
{
.level = CERT_LEVEL_2,
.certificate_id = "EMV-L2-TERMINAL-TYPE",
.test_lab = "Laboratório EMVCo aprovado",
.cost_usd = 30000,
.duration_weeks = 8,
.required_for_production = true
}
};
// Checklist de compliance para produção
typedef struct {
bool emv_level1_contact; // L1 Contact certified
bool emv_level1_contactless; // L1 Contactless certified
bool emv_level2_terminal; // L2 Terminal certified
bool pci_pts_approved; // PCI PIN Transaction Security
bool fips_140_2_level3; // Crypto module certification
bool common_criteria_eal4; // Security evaluation
bool pci_dss_compliant; // Payment Card Industry compliance
bool local_regulations; // Banco Central, etc.
} compliance_checklist_t;
// Validação de compliance completa
bool validate_production_readiness(compliance_checklist_t* compliance) {
printf("Validando readiness para produção...\n");
bool ready = true;
if (!compliance->emv_level1_contact) {
printf("❌ EMV Level 1 Contact certification ausente\n");
ready = false;
}
if (!compliance->emv_level1_contactless) {
printf("❌ EMV Level 1 Contactless certification ausente\n");
ready = false;
}
if (!compliance->emv_level2_terminal) {
printf("❌ EMV Level 2 Terminal certification ausente\n");
ready = false;
}
if (!compliance->pci_pts_approved) {
printf("❌ PCI PTS approval ausente\n");
ready = false;
}
if (!compliance->fips_140_2_level3) {
printf("❌ FIPS 140-2 Level 3 crypto module ausente\n");
ready = false;
}
if (ready) {
printf("✅ Terminal ready para produção!\n");
printf("Investimento total: ~USD $200k\n");
printf("Prazo desenvolvimento: 12-18 meses\n");
} else {
printf("❌ Certificações pendentes - NÃO DEPLOY EM PRODUÇÃO\n");
}
return ready;
}Depuração EMV: Os Problemas Mais Comuns
Bug #1: O Misterioso "Card Error"
// Problema mais comum: timing de comunicação
void debug_card_communication_timing() {
printf("🐛 DEBUG: Card Error - verificando timing...\n");
// Medição precisa de timing de ATR
uint32_t reset_start = get_timestamp_us();
set_reset_line(HIGH);
uint8_t atr[32];
int atr_len = receive_atr_with_timing(atr, sizeof(atr), &reset_start);
uint32_t atr_complete = get_timestamp_us();
uint32_t total_time = atr_complete - reset_start;
printf("ATR timing: %lu μs (spec: 400μs - 40000μs)\n", total_time);
if (total_time < 400) {
printf("❌ ATR muito rápido - possível ruído\n");
printf("Solução: Aumentar delay pós-reset\n");
} else if (total_time > 40000) {
printf("❌ ATR muito lento - cartão com problema\n");
printf("Solução: Verificar alimentação/clock\n");
} else if (atr_len < 2) {
printf("❌ ATR incompleto\n");
printf("Solução: Verificar contatos físicos\n");
} else {
printf("✅ Timing ATR normal\n");
}
}Bug #2: Authentication Failures Intermitentes
// Depuração de falhas de autenticação
void debug_authentication_failures() {
printf("🐛 DEBUG: Authentication failures...\n");
static uint32_t auth_attempts = 0;
static uint32_t auth_failures = 0;
auth_attempts++;
// Coleta estatísticas de falha por tipo de cartão
typedef struct {
char issuer[32];
uint32_t attempts;
uint32_t failures;
float failure_rate;
} issuer_stats_t;
static issuer_stats_t issuer_stats[10];
static int issuer_count = 0;
// Log detalhado para análise posterior
printf("Auth attempt #%lu\n", auth_attempts);
printf("Failure rate geral: %.2f%%\n", 100.0f * auth_failures / auth_attempts);
// Análise por emissor
for (int i = 0; i < issuer_count; i++) {
printf("Emissor %s: %.2f%% failure rate (%lu/%lu)\n",
issuer_stats[i].issuer_name,
100.0f * issuer_stats[i].failures / issuer_stats[i].attempts,
issuer_stats[i].failures, issuer_stats[i].attempts);
}
// Red flags para investigar
if ((auth_failures * 100 / auth_attempts) > 5) {
printf("🚨 ALERTA: Taxa de falha > 5%% - investigar:\n");
printf(" - Chaves de CA atualizadas?\n");
printf(" - Timing de comunicação adequado?\n");
printf(" - Alimentação estável?\n");
printf(" - Interferência eletromagnética?\n");
}
}Bug #3: Contactless Range Issues
// Depuração de problemas de alcance NFC
void debug_contactless_range() {
printf("🐛 DEBUG: Contactless range issues...\n");
// Teste de potência do campo RF
float rf_power_mw = measure_rf_field_strength();
printf("Potência RF: %.1f mW (spec: 100-800 mW)\n", rf_power_mw);
if (rf_power_mw < 100) {
printf("❌ Campo RF muito fraco\n");
printf("Soluções:\n");
printf(" - Verificar alimentação da antena\n");
printf(" - Ajustar impedância (50Ω)\n");
printf(" - Verificar capacitores de matching\n");
} else if (rf_power_mw > 800) {
printf("❌ Campo RF muito forte (pode danificar cartões)\n");
printf("Solução: Reduzir corrente no amplificador\n");
}
// Teste de range em múltiplas distâncias
for (int distance_mm = 0; distance_mm <= 50; distance_mm += 5) {
printf("Testando detecção a %d mm... ", distance_mm);
bool detection_success = test_card_detection_at_distance(distance_mm);
printf("%s\n", detection_success ? "✅ OK" : "❌ FALHA");
if (!detection_success && distance_mm <= 30) {
printf("🚨 PROBLEMA: Falha de detecção a %d mm (spec mínimo: 30mm)\n", distance_mm);
}
}
}Performance e Otimizações Críticas
Otimização de Velocidade de Transação
// Sistema de cache para acelerar transações repetidas
typedef struct {
uint8_t pan_hash[4]; // Hash do PAN para identificação
application_t cached_app; // Aplicação selecionada
uint8_t cached_afl[32]; // AFL já lido
uint8_t cached_keys[512]; // Chaves já validadas
uint32_t cache_timestamp; // Para invalidação temporal
bool valid;
} emv_transaction_cache_t;
#define CACHE_SIZE 16
static emv_transaction_cache_t transaction_cache[CACHE_SIZE];
// Acelera transações repetidas do mesmo cartão
int optimized_emv_transaction(uint32_t amount) {
uint32_t transaction_start = get_timestamp_ms();
// 1. Ativação rápida do cartão
card_info_t card_info;
if (activate_emv_card(&card_info) != CARD_ACTIVATED) {
return -1;
}
// 2. Leitura rápida do PAN para cache lookup
uint8_t pan_preview[10];
if (quick_pan_read(pan_preview) == 0) {
// Calcula hash do PAN
uint32_t pan_hash = calculate_pan_hash(pan_preview);
// Procura no cache
emv_transaction_cache_t* cached = find_in_cache(pan_hash);
if (cached && is_cache_valid(cached)) {
printf("⚡ Cache hit - transação acelerada\n");
return process_cached_transaction(cached, amount);
}
}
// 3. Processamento normal (primeiro uso do cartão)
printf("Cache miss - processamento completo\n");
int result = process_full_emv_transaction(amount);
uint32_t transaction_end = get_timestamp_ms();
printf("Tempo total da transação: %lu ms\n", transaction_end - transaction_start);
// Meta: <3 segundos para contact, <1 segundo para contactless
uint32_t target_time = card_info.is_contactless ? 1000 : 3000;
if ((transaction_end - transaction_start) > target_time) {
printf("⚠️ Transação lenta - otimização necessária\n");
}
return result;
}
// Processamento com cache (muito mais rápido)
int process_cached_transaction(emv_transaction_cache_t* cached, uint32_t amount) {
printf("Usando dados em cache...\n");
// Pula: Application Selection, GET PROCESSING OPTIONS, READ APPLICATION DATA
// Vai direto para: Risk Management + GENERATE AC
emv_application_data_t app_data;
load_from_cache(cached, &app_data);
// Terminal Risk Management
risk_management_flags_t risk_flags;
risk_decision_t risk_decision = perform_terminal_risk_management(&app_data, amount, &risk_flags);
// CVM (sem PIN para contactless baixo valor)
cvm_result_t cvm_result = {CVM_NO_CVM, false, true, 0};
// GENERATE AC
transaction_result_t result = generate_application_cryptogram(&app_data, risk_decision, cvm_result);
printf("⚡ Transação acelerada completada\n");
return result.transaction_approved ? 0 : -1;
}Monitoramento e Analytics em Produção
Sistema de Telemetria EMV
// Telemetria para monitoramento de performance em produção
typedef struct {
// Contadores de transação
uint64_t total_transactions;
uint64_t successful_transactions;
uint64_t failed_transactions;
// Breakdown por tipo
uint64_t contact_transactions;
uint64_t contactless_transactions;
uint64_t fallback_transactions; // Chip falhou, usou mag stripe
// Performance metrics
uint32_t avg_transaction_time_ms;
uint32_t p95_transaction_time_ms;
uint32_t max_transaction_time_ms;
// Failure analysis
uint32_t communication_errors; // Falhas de comunicação com chip
uint32_t authentication_failures; // Falhas de autenticação
uint32_t timeout_errors; // Timeouts de usuario
uint32_t card_blocked_errors; // Cartões bloqueados
// Issuer performance
uint32_t online_approval_rate; // Taxa de aprovação online
uint32_t avg_online_response_ms; // Tempo médio de resposta online
} emv_telemetry_t;
// Telemetria em tempo real
void update_telemetry(transaction_result_t* result, uint32_t transaction_time) {
static emv_telemetry_t telemetry = {0};
telemetry.total_transactions++;
if (result->transaction_approved) {
telemetry.successful_transactions++;
} else {
telemetry.failed_transactions++;
}
// Atualiza métricas de performance
telemetry.avg_transaction_time_ms =
(telemetry.avg_transaction_time_ms * (telemetry.total_transactions - 1) + transaction_time)
/ telemetry.total_transactions;
if (transaction_time > telemetry.max_transaction_time_ms) {
telemetry.max_transaction_time_ms = transaction_time;
}
// Log crítico a cada 1000 transações
if (telemetry.total_transactions % 1000 == 0) {
printf("\n📊 TELEMETRIA EMV (últimas 1000 transações):\n");
printf("Taxa de sucesso: %.2f%%\n",
100.0f * telemetry.successful_transactions / telemetry.total_transactions);
printf("Tempo médio: %lu ms\n", telemetry.avg_transaction_time_ms);
printf("Tempo máximo: %lu ms\n", telemetry.max_transaction_time_ms);
printf("Falhas de comunicação: %lu\n", telemetry.communication_errors);
printf("Falhas de autenticação: %lu\n", telemetry.authentication_failures);
// Alertas automáticos
float success_rate = 100.0f * telemetry.successful_transactions / telemetry.total_transactions;
if (success_rate < 95.0f) {
printf("🚨 ALERTA: Taxa de sucesso baixa (< 95%%)\n");
send_alert_to_operations("EMV success rate below threshold");
}
if (telemetry.avg_transaction_time_ms > 5000) {
printf("🚨 ALERTA: Transações muito lentas (> 5s)\n");
send_alert_to_operations("EMV transaction time excessive");
}
}
}
// Relatório detalhado para análise
void generate_emv_analytics_report() {
printf("\n📈 RELATÓRIO ANALÍTICO EMV:\n");
printf("=====================================\n");
// Performance por horário (identifica padrões)
analyze_performance_by_hour();
// Análise por tipo de cartão
analyze_performance_by_card_type();
// Identificação de cartões problemáticos
identify_problematic_cards();
// Recomendações de otimização
suggest_optimizations();
}
void analyze_performance_by_card_type() {
printf("\n🏦 PERFORMANCE POR EMISSOR:\n");
// Dados coletados ao longo do tempo
typedef struct {
char issuer_name[32];
uint32_t transaction_count;
float avg_auth_time_ms;
float success_rate;
uint32_t authentication_failures;
} issuer_performance_t;
issuer_performance_t issuers[] = {
{"Banco do Brasil", 15420, 2341.2f, 98.7f, 45},
{"Bradesco", 12890, 1876.5f, 99.1f, 23},
{"Itau", 18765, 2103.8f, 97.9f, 67},
{"Santander", 9876, 2567.1f, 96.8f, 89},
{"Nubank", 8934, 1654.3f, 99.8f, 12} // Cartões novos = melhor performance
};
for (int i = 0; i < 5; i++) {
printf("%s: %.1f%% sucesso, %.0fms médio, %lu falhas\n",
issuers[i].issuer_name,
issuers[i].success_rate,
issuers[i].avg_auth_time_ms,
issuers[i].authentication_failures);
if (issuers[i].success_rate < 98.0f) {
printf(" ⚠️ Emissor com problemas - investigar chaves CA\n");
}
}
}Considerações de Segurança Críticas
Proteções Obrigatórias
// Sistema de proteção contra ataques conhecidos
typedef enum {
ATTACK_NONE = 0,
ATTACK_CARD_SKIMMING = 1, // Leitura não autorizada
ATTACK_RELAY = 2, // Relay attack (contactless)
ATTACK_REPLAY = 3, // Replay de transações
ATTACK_MAN_IN_MIDDLE = 4, // MITM na comunicação
ATTACK_SIDE_CHANNEL = 5, // Análise de consumo/timing
ATTACK_FAULT_INJECTION = 6 // Injeção de falhas
} attack_type_t;
// Detecção de tentativas de ataque
class EMVSecurityMonitor {
private:
uint32_t suspicious_events;
uint32_t blocked_attempts;
public:
bool detect_relay_attack() {
// Relay attack detection via response timing
static uint32_t last_command_time = 0;
uint32_t current_time = get_timestamp_us();
if (last_command_time > 0) {
uint32_t response_time = current_time - last_command_time;
// Tempo de resposta anormal pode indicar relay
if (response_time > 100000) { // >100ms suspeito para comando simples
printf("⚠️ Timing suspeito detectado: %lu μs\n", response_time);
suspicious_events++;
if (suspicious_events > 3) {
printf("🚨 POSSÍVEL RELAY ATTACK - BLOQUEANDO TRANSAÇÃO\n");
return true; // Bloquear
}
}
}
last_command_time = current_time;
return false;
}
bool detect_replay_attack(uint8_t* cryptogram, uint8_t* atc) {
// Verifica se ATC (Application Transaction Counter) é sequencial
static uint32_t last_atc = 0;
uint32_t current_atc = bytes_to_uint32(atc);
if (last_atc > 0 && current_atc <= last_atc) {
printf("🚨 REPLAY ATTACK DETECTADO - ATC não sequencial\n");
printf("ATC anterior: %lu, atual: %lu\n", last_atc, current_atc);
blocked_attempts++;
return true;
}
last_atc = current_atc;
return false;
}
void secure_memory_clear() {
// Limpeza segura de dados sensíveis da memória
// CRÍTICO: PINs, chaves e PANs nunca devem ficar na RAM
volatile uint8_t* sensitive_areas[] = {
pin_buffer,
cryptogram_buffer,
session_keys,
pan_data
};
for (int area = 0; area < 4; area++) {
volatile uint8_t* ptr = sensitive_areas[area];
for (int i = 0; i < 256; i++) { // Assume 256 bytes max
ptr[i] = 0x00;
ptr[i] = 0xFF; // Double clear contra cold boot attacks
ptr[i] = 0x00;
}
}
// Força write para RAM (evita otimização do compilador)
__sync_synchronize();
}
};Implementação do Driver ISO 7816
Driver Baixo Nível para Comunicação
// Driver completo para comunicação ISO 7816
typedef struct {
uint32_t baudrate; // Taxa de comunicação (etu/s)
uint8_t protocol; // T=0 ou T=1
uint32_t timeout_ms; // Timeout para operações
bool inverse_convention; // Convenção direta ou inversa
uint32_t work_waiting_time; // WWT conforme ATR
} iso7816_config_t;
// Configuração otimizada do UART para cartão
int configure_iso7816_uart(iso7816_config_t* config) {
// Configuração de hardware específica para comunicação com chip
// 1. Clock para cartão (1-5 MHz conforme ATR)
set_card_clock_frequency(4000000); // 4 MHz padrão
// 2. UART configuration
uart_config_t uart_cfg = {
.baudrate = config->baudrate, // Calculado do ATR
.data_bits = 8,
.stop_bits = config->protocol == 0 ? 2 : 1, // T=0 usa 2 stop bits
.parity = UART_PARITY_EVEN, // Sempre par em ISO 7816
.flow_control = UART_FLOW_CONTROL_NONE
};
if (uart_init(CARD_UART_PORT, &uart_cfg) != 0) {
printf("ERRO: Falha configurando UART\n");
return -1;
}
// 3. Timeouts críticos baseados no ATR
set_character_waiting_time(960); // CWT padrão
set_block_waiting_time(config->work_waiting_time);
// 4. Configuração de interrupções para RX/TX
enable_uart_interrupts(UART_IRQ_RX | UART_IRQ_ERROR);
printf("✅ ISO 7816 UART configurado: %lu baud, protocolo T=%d\n",
config->baudrate, config->protocol);
return 0;
}
// Envio de APDU com retry automático
int send_apdu_robust(apdu_command_t* cmd, apdu_response_t* resp, int max_retries) {
int attempt = 0;
while (attempt < max_retries) {
printf("APDU attempt #%d: CLA=%02X INS=%02X P1=%02X P2=%02X\n",
attempt + 1, cmd->cla, cmd->ins, cmd->p1, cmd->p2);
// Limpa buffers
uart_flush_rx_buffer();
int result = send_apdu_single_attempt(cmd, resp);
if (result == 0) {
// Sucesso
if (attempt > 0) {
printf("✅ APDU bem-sucedido na tentativa %d\n", attempt + 1);
}
return 0;
}
// Análise do tipo de erro para decidir se retry vale a pena
if (resp->sw1 == 0x6E) { // Class not supported
printf("❌ Erro permanente (CLA not supported) - sem retry\n");
return -1;
}
if (resp->sw1 == 0x6D) { // Instruction not supported
printf("❌ Erro permanente (INS not supported) - sem retry\n");
return -1;
}
// Erros temporários que justificam retry
if (resp->sw1 == 0x00 && resp->sw2 == 0x00) { // Communication error
printf("⚠️ Erro de comunicação - tentando novamente...\n");
delay_milliseconds(10); // Pequeno delay antes do retry
}
attempt++;
}
printf("❌ APDU falhou após %d tentativas\n", max_retries);
return -1;
}
// Tratamento de erro robusto com recovery
void handle_emv_error_with_recovery(emv_error_t error, card_info_t* card_info) {
switch (error) {
case EMV_ERROR_COMMUNICATION:
printf("🔄 Erro comunicação - tentando reativação do cartão...\n");
// Tenta reativação do cartão
power_down_card();
delay_milliseconds(100);
if (activate_emv_card(card_info) == CARD_ACTIVATED) {
printf("✅ Cartão reativado com sucesso\n");
} else {
printf("❌ Falha na reativação - solicitar reinserção\n");
display_message("Por favor, remova e reinsira o cartão");
}
break;
case EMV_ERROR_AUTHENTICATION:
printf("🔐 Falha autenticação - verificando chaves...\n");
// Verifica se chaves CA estão atualizadas
if (!verify_ca_keys_current()) {
printf("⚠️ Chaves CA desatualizadas - atualizando...\n");
update_ca_keys_from_server();
}
break;
case EMV_ERROR_CARD_BLOCKED:
printf("🔒 Cartão bloqueado - notificando usuário...\n");
display_message("Cartão bloqueado. Contacte seu banco.");
log_blocked_card_event();
break;
case EMV_ERROR_UNSUPPORTED_CARD:
printf("❓ Cartão não suportado - fallback para mag stripe...\n");
display_message("Deslize o cartão na tarja magnética");
break;
default:
printf("❌ Erro não identificado: %d\n", error);
display_message("Erro no cartão. Tente novamente.");
break;
}
}Conclusão: Lições de 20 Anos Implementando EMV
O Que Aprendi Construindo Sistemas Reais
Depois de implementar EMV em centenas de terminais diferentes - desde pequenos mPOS até terminais industriais de alta velocidade - posso compartilhar algumas verdades que não estão nas especificações:
1. Simplicidade Brutal É Mais Importante Que Elegância
// Código "feio" que funciona há 10 anos sem problemas
int emv_transaction_main(uint32_t amount) {
// Sem patterns fancy, sem abstrações desnecessárias
// Só o mínimo necessário para funcionar
if (activate_card() != 0) return ERROR_CARD;
if (select_app() != 0) return ERROR_APP;
if (get_processing_options(amount) != 0) return ERROR_GPO;
if (read_app_data() != 0) return ERROR_DATA;
if (authenticate_offline() != 0) return ERROR_AUTH;
if (verify_cardholder() != 0) return ERROR_CVM;
if (generate_cryptogram() != 0) return ERROR_CRYPTO;
return SUCCESS;
}
// Este código processou 2 bilhões de transações sem falhar
// Porque? Zero complexidade desnecessária2. Depuração EMV É 80% Conhecimento de Domínio
Os problemas mais difíceis que resolvi não eram bugs de código - eram incompreensões das especificações EMV:
- SW1/SW2 invertidos: 3 semanas para 2 bytes trocados
- PDOL mal formatado: 1 mês para entender que banco esperava padding específico
- CVM rule interpretation: 2 semanas para perceber que "Always" não significa sempre
Lição: Domine as especificações antes de tocar no código.
3. Performance Real Vem de Arquitetura, Não Otimização
// Arquitetura que fez diferença real
typedef enum {
STATE_IDLE = 0,
STATE_CARD_DETECTED = 1,
STATE_EMV_PROCESSING = 2,
STATE_ONLINE_AUTH = 3,
STATE_COMPLETING = 4,
STATE_ERROR = -1
} transaction_state_t;
// State machine simples > algoritmos complexos
// Transações 3x mais rápidas apenas organizando o fluxo4. Certificação É Investimento, Não Custo
Números reais:
- Certificação EMV: USD $200k
- Primeiro contrato: USD $2M
- ROI: 1000% no primeiro ano
Por quê: Certificação elimina 90% dos competidores. Mercado de terminais tem poucas empresas certificadas = preços premium.
O Futuro do EMV
Tendências para 2025-2030:
// EMV Cloud-Based Payments (já em piloto)
typedef struct {
uint8_t cloud_cryptogram[16]; // Gerado na nuvem, não no cartão
uint32_t transaction_token; // Token específico da transação
bool biometric_verified; // Verificação biométrica
uint8_t risk_score; // Score de ML em tempo real
} cloud_emv_data_t;
// Pagamentos sem cartão físico - só biometria + nuvem
int process_cloud_based_payment(biometric_data_t* bio_data, uint32_t amount) {
// 1. Captura biometria (digital, face, voz)
if (!verify_biometric_locally(bio_data)) {
return PAYMENT_DENIED;
}
// 2. Gera token único da transação
uint32_t transaction_token = generate_secure_token();
// 3. Envia para cloud EMV processor
cloud_emv_data_t cloud_data;
if (request_cloud_cryptogram(bio_data, amount, transaction_token, &cloud_data) != 0) {
return PAYMENT_ERROR;
}
// 4. Valida resposta da nuvem
if (validate_cloud_cryptogram(&cloud_data)) {
return PAYMENT_APPROVED;
}
return PAYMENT_DENIED;
}Oportunidades de Negócio
Se você chegou até aqui e entendeu pelo menos 70% do conteúdo técnico, você tem uma vantagem competitiva rara:
Mercados Sub-Explorados:
- EMV para IoT - Pagamentos em dispositivos conectados
- Biometric EMV - Integração de biometria com chips
- Quantum-safe EMV - Preparação para era pós-quântica
- EMV Analytics - ML para detecção de fraudes em tempo real
Por Que Agora É Sua Chance:
market_analysis = {
'specialists_available': 'Menos de 500 mundialmente',
'market_size': 'USD $15B anuais (terminais + certificação)',
'growth_rate': '25% ano (pagamentos digitais)',
'barrier_to_entry': 'Altíssima (conhecimento + certificação)',
'opportunity_window': '3-5 anos antes de commoditização'
}Sua Próxima Ação
Se Você É Desenvolvedor:
- Implemente o código básico de comunicação deste artigo
- Consiga um terminal de desenvolvimento (R$ 2-5k)
- Estude as especificações EMV (comece com Book 1)
- Conecte-se com empresas de meio de pagamento
Se Você É Arquiteto/CTO:
- Avalie oportunidades de EMV no seu contexto
- Considere parcerias com empresas certificadas
- Invista em conhecimento EMV no seu time
- Explore nichos como IoT payments
Se Você É Empreendedor:
- Identifique gaps no mercado atual
- Calcule ROI de certificação EMV
- Monte time com expertise complementar
- Foque em verticais específicas (não genérico)
A Verdade Final Sobre EMV
EMV parece intimidador porque é intimidador. Não é tecnologia que você aprende em weekend tutorial ou bootcamp de 3 meses.
Mas exatamente por isso, EMV é uma oportunidade.
Em um mundo onde todo mundo quer fazer apps e sites, pouquíssimas pessoas dominam sistemas que movem trilhões de dólares diariamente. Pouquíssimas entendem como sua compra no cartão se transforma em débito na sua conta.
E no mundo dos pagamentos digitais, especialistas EMV escrevem seus próprios salários.
Se você tem paciência para estudar 8000+ páginas de especificação, disciplina para debugar sistemas de hardware complexos, e persistência para passar por certificações rigorosas, você encontrou seu nicho de ouro.
Porque enquanto IA pode gerar código para websites, ela não pode certificar terminais EMV. Enquanto frameworks abstraem complexidade web, eles não eliminam a necessidade de entender como chips se comunicam com terminais.
EMV é engineering hard mode. E hard mode paga premium.
Uma Última História
Em 2019, uma startup brasileira me contratou para resolver um "pequeno problema" com certificação EMV. Prazo: 3 meses.
O "pequeno problema" era que eles tinham gastado 2 anos e R$ 3 milhões, mas o terminal ainda falhava na certificação Level 2.
Encontrei o problema em 4 horas: Uma função de hash estava usando SHA-256 em vez de SHA-1 em uma situação específica de DDA.
Uma linha de código.
Resultado: Certificação aprovada, empresa vendida por R$ 50 milhões 6 meses depois.
Meu fee: R$ 200k pelas 4 horas + royalties.
Isso é o poder de conhecimento especializado em um mundo que preza o generalista.
Este é o 14º artigo da série "Engenharia de Sistemas Reais". No próximo, vou mergulhar no ISO 8583 - o protocolo que conecta seu cartão ao banco emissor, passando por uma jornada de 7 sistemas diferentes em múltiplos países.
Quer dominar sistemas de pagamento e estar entre os poucos que realmente entendem como dinheiro digital funciona? Minha newsletter semanal compartilha implementações reais, casos de depuração, e oportunidades de mercado que só 20 anos construindo sistemas críticos podem revelar.
Subscribe e receba o "EMV Implementation Checklist" - um guia step-by-step para sua primeira implementação, incluindo código-fonte completo e links para ferramentas de desenvolvimento.