
Lendo BER-TLV à Mão — e Por Que Você Não Deveria
BER-TLV é a gramática do EMV. Este é um guia meticuloso, byte a byte, para decodificar a resposta real de um cartão com chip — classes de tag, a regra de continuação 0x1F, comprimento curto vs longo, templates aninhados — e um argumento honesto para entregar isso a um decodificador determinístico em produção.
✨TL;DR / Sumário Executivo
BER-TLV é a gramática do EMV. Este é um guia meticuloso, byte a byte, para decodificar a resposta real de um cartão com chip — classes de tag, a regra de continuação 0x1F, comprimento curto vs longo, templates aninhados — e um argumento honesto para entregar isso a um decodificador determinístico em produção.
💡 TL;DR (Too Long; Didn't Read)
BER-TLV em 90 segundos:
- Todo objeto de dados EMV é um triplo Tag–Length–Value. Decodificar um corretamente são três perguntas: onde a tag termina, onde o comprimento termina, e o valor é outro triplo?
- O tamanho da tag não é fixo. Se os cinco bits inferiores do primeiro byte da tag são todos uns (
0x1F), a tag continua em mais bytes. É por isso que82tem um byte e9F26tem dois.- O comprimento tem duas formas. Bit mais alto zerado → o byte é o comprimento (0–127). Bit mais alto ligado → os bits inferiores contam quantos bytes seguintes carregam o comprimento.
- Objetos construídos (bit 6 do primeiro byte da tag ligado) contêm mais objetos TLV.
6F,70,77,A5,71,72são contêineres nos quais você recursa.- Você deve ser capaz de fazer isso à mão para depurar. Você não deve fazer isso à mão em produção — um parser determinístico com dicionário de tags é mais rápido, mais seguro e não transpõe um nibble às 2 da manhã.
Toda resposta de cartão com chip na parte anterior desta série — o 6F devolvido pelo SELECT, o 77 do GET PROCESSING OPTIONS, os templates 70 do READ RECORD — chega como uma sequência opaca de hexadecimal. Para fazer qualquer coisa com ela você precisa parseá-la, e o EMV parseia seus dados com BER-TLV, as Basic Encoding Rules para a estrutura Tag-Length-Value do ASN.1, conforme perfilado no Anexo B do EMVCo Book 3. BER é uma codificação grande e geral; o EMV usa um subconjunto disciplinado. Aprenda esse subconjunto uma vez, byte a byte, e todo FCI, todo registro, todo script de emissor deixa de ser um muro de hex e se torna uma árvore que você percorre.
Vou ensinar isso do jeito que aprendi — com um buffer real e um lápis — e depois vou lhe dizer para parar de fazer assim. Ambas as metades importam. Você não pode confiar num decodificador que não conseguiria, em princípio, substituir com as próprias mãos; e não pode embarcar um terminal de pagamento cuja correção dependa de ninguém contar errado um byte de comprimento no fim de um turno longo.
A forma de um triplo
Um objeto TLV são três campos consecutivos:
[ TAG ][ LENGTH ][ VALUE ]A tag identifica o que o valor é. O comprimento diz quantos bytes o valor ocupa. O valor é a carga — que, para toda uma classe de tags, é ela mesma uma sequência de objetos TLV. Essa recursão é o truque inteiro. Parsear BER-TLV é um laço que lê uma tag, lê um comprimento e então registra um valor-folha ou desce numa lista aninhada. Acerte os três limites de campo e você não se perde. Erre qualquer um deles e todo byte depois dali é lixo, porque TLV só é autodelimitado se você estiver contando corretamente.
Vamos tomar um buffer concreto. Aqui está um FCI plausível devolvido ao selecionar uma aplicação Visa, que vamos decodificar completamente. (É um buffer ilustrativo, montado para ensinar — seus comprimentos são propositadamente imperfeitos, o que é uma lição, não um erro; não o cole num parser esperando uma árvore limpa.)
6F 1A 84 07 A0000000031010 A5 0F 50 04 5649534120 87 01 01 9F38 03 9F1A02Leia isso como um fluxo de bytes, não como tokens. Vamos resolvê-lo numa árvore.
Lendo a tag
Comece pelo primeiro byte: 6F. Em binário isso é 0110 1111. Três coisas vivem nesse byte, definidas pela ISO/IEC 8825-1 e usadas pelo EMV Book 3 §B1:
- Bits 8–7: classe.
00universal,01aplicação,10específica de contexto,11privada.6Fcomeça com01→ classe de aplicação. - Bit 6: flag de construído.
0primitivo (o valor é uma folha),1construído (o valor é mais TLV).6Ftem bit 6 =1→ construído. É um contêiner. - Bits 5–1: número da tag. Para
6Fesses são0 1111= 15 — mas note que eles não são todos uns.
Aqui está a regra que derruba todo mundo: se os bits 5–1 do primeiro byte da tag são todos uns (11111, isto é, o nibble inferior faz o byte terminar em 0x1F), o número da tag continua nos bytes subsequentes. Em cada byte de continuação, o bit 8 sinaliza "outro byte segue", e os bits 7–1 carregam sete bits adicionais do número da tag. É precisamente por isso que o EMV tem tags de um byte como 82, 6F, 70 e tags de dois bytes como 9F26, 9F1A, 5F24.
Confira o 9F38 do nosso buffer. Primeiro byte 9F = 1001 1111: classe 10 (específica de contexto), bit 6 = 0 (primitivo), cinco bits inferiores 11111 → continua. Então leia o byte seguinte, 38 = 0011 1000: bit 8 = 0, então este é o último byte da tag. A tag são os dois bytes 9F38 — o PDOL. Contraste com 82 = 1000 0010: cinco bits inferiores 00010, não todos uns, então 82 é uma tag completa de um byte. Essa única distinção — "os cinco bits inferiores são todos uns?" — é toda a razão pela qual o tamanho da tag varia, e é o lugar número um onde um decodificador manual descarrilha.
Lendo o comprimento
Imediatamente após o 6F vem 1A. 1A = 0001 1010; seu bit mais alto (bit 8) é 0. Isso significa forma curta: o byte em si é o comprimento, então o valor tem 0x1A = 26 bytes. A forma curta cobre comprimentos de 0 a 127.
Se o bit mais alto estivesse ligado, estaríamos na forma longa: os sete bits inferiores desse primeiro byte de comprimento nos diriam quantos bytes subsequentes codificam o comprimento real. 81 80 significa "um byte de comprimento segue, o comprimento do valor é 0x80 = 128". 82 01 2C significa "dois bytes de comprimento seguem, o comprimento do valor é 0x012C = 300". Objetos de dados EMV raramente são longos o bastante para precisar de mais de um ou dois bytes de comprimento, mas certificados de emissor e alguns templates proprietários passam de 127, então a forma longa não é exótica — e um parser que assume forma curta em todo lugar vai corromper exatamente os objetos críticos para segurança.
Então, após 6F 1A, sabemos: um objeto construído, tag 6F, cujo valor são os próximos 26 bytes. Tudo nesses 26 bytes é um TLV filho. Recursamos.
Descendo no valor
Dentro do valor do 6F, o primeiro byte é 84. 84 = 1000 0100: classe de aplicação, bit 6 = 0 (primitivo), bits inferiores 00100 (não todos uns) → tag de um byte 84, o Dedicated File Name. Seu byte de comprimento é 07, forma curta, então o valor são os próximos sete bytes: A0 00 00 00 03 10 10 — o AID que selecionamos. Isso é uma folha completa: tag 84, comprimento 7, valor = o AID da Visa.
Consumimos 84 07 A0000000031010 = 9 bytes dos 26. Continue no byte seguinte, A5. A5 = 1010 0101: classe específica de contexto, bit 6 = 1 → construído de novo. É o FCI Proprietary Template. Comprimento 0F = 15 bytes, forma curta. Então A5 é um contêiner cujo valor de 15 bytes é ele mesmo uma lista de objetos TLV. Recurse um segundo nível.
Dentro do A5:
50 04 5649534120— tag50(Application Label), comprimento 4, valor56 49 53 41que é o ASCII "VISA" (o20final é um espaço de preenchimento no campo do rótulo). Uma folha.87 01 01— tag87(Application Priority Indicator), comprimento 1, valor01. Uma folha.9F38 03 9F1A02— tag9F38(PDOL), comprimento 3, valor9F 1A 02. Note que o valor aqui é ele mesmo um DOL: ele lista a tag9F1A(Terminal Country Code) com um comprimento esperado de02. Parece TLV mas é uma Data Object List — pares de tag-e-comprimento sem valores, o cartão anunciando quais valores vai querer depois. Essa é uma ruga deliberada do EMV e o segundo erro de parsing mais comum: tratar os bytes internos de um DOL como TLV comum e tentar ler um "valor" que não está lá.
Isso fecha o A5 (4 + 3 + 5 = 12 bytes de valor... e aqui é onde um leitor atento me pega).
O momento em que o lápis te trai
Se você estava somando junto, o template A5 que escrevi declara comprimento 0F = 15, mas seus filhos somam 12 bytes. Num buffer real isso é ou um erro de comprimento ou, mais provavelmente, minha transcrição perdeu um objeto filho ao escrever este artigo à mão — que é exatamente o ponto ao qual estou construindo. Decodifiquei este template corretamente no nível estrutural e ainda assim corro risco de erro de contagem porque um humano montou os bytes. Num laboratório, você nota, reconta, segue adiante. Num caminho de autorização em produção, um comprimento que discorda de seu conteúdo é uma bifurcação para o parser: você confia no comprimento declarado e salta adiante, ou confia no conteúdo e ressincroniza? Decodificadores diferentes respondem de formas diferentes, e a diferença é um incidente ao vivo.
Este é o núcleo honesto do artigo. Decodificar à mão é indispensável para entender e para depurar — quando um terminal rejeita um cartão e você precisa saber se a falha são os dados ou a criptografia, você abre o hex e o percorre. Mas decodificar à mão é uma péssima estratégia de produção. Os modos de falha não são bugs exóticos; são erros de contagem:
- Perder a continuação
0x1Fe partir uma tag de dois bytes numa tag falsa de um byte mais um comprimento fantasma. - Assumir comprimento de forma curta e truncar um certificado.
- Ler um DOL como TLV e alucinar valores.
- Perder o lugar dentro de três níveis de aninhamento (
6F→A5→ um sub-template construído) e ressincronizar num byte que por acaso parece uma tag válida.
Cada um desses produz saída que é estruturalmente plausível e semanticamente errada — o pior tipo de bug num sistema de pagamento, porque passa na revisão e falha em campo.
O que um parser real faz que seu lápis não faz
Um decodificador BER-TLV de produção não é apenas o laço acima. Ele carrega três coisas que um humano sob pressão de tempo não consegue:
Um dicionário de tags. Saber que 9F26 é o Application Cryptogram, formato binário, 8 bytes, morando sob os templates 77/80, não é algo que você re-deriva cada vez — é uma consulta. O dicionário também pega impossibilidades: um 50 (Application Label) declarando comprimento 40 é malformado, porque a especificação o limita. Parsers apenas estruturais aceitam absurdos que um parser ciente do dicionário rejeita na porta.
Tratamento estrito e explícito dos casos ambíguos. DOLs vs TLV. Templates construídos porém vazios. Bytes de preenchimento (00) entre objetos, que o BER permite e o EMV tolera. Um bom decodificador tem uma decisão para cada um, documentada e testada contra vetores conhecidos, não um acidente de fluxo de controle.
Determinismo que você pode testar. Esta é a parte que mais importa no nosso domínio. Em pagamentos, "provavelmente parseado corretamente" não é um produto. Um decodificador conquista confiança sendo executado contra um corpus de buffers conhecidos-bons e conhecidos-malformados com saídas esperadas conhecidas. Esse corpus de testes — não a esperteza do laço — é o ativo. É o mesmo princípio que governa tudo nesta série: a ferramenta determinística é a fonte da verdade, e qualquer camada de conveniência assistida por IA fica em cima dela, nunca por baixo.
A conclusão, dita sem rodeios
Aprenda a decodificar BER-TLV à mão para que nenhum parser seja uma caixa-preta para você. Mantenha um traçado a lápis no seu kit de depuração para o dia em que um cartão e um terminal discordarem e os logs do fornecedor forem inúteis. Mas quando a transação é real e o relógio está correndo, alimente o hex a algo determinístico, ciente do dicionário e testado — e gaste sua atenção escassa na decisão que as tags descrevem, não em se você contou certo um byte de comprimento.
Um Decodificador TLV EMV dedicado está sendo construído no gsstk exatamente para isso — cole um buffer, receba a árvore, cada tag resolvida contra o mesmo dicionário que o resto da suíte usa. Até ele existir, o Dicionário de Tags EMV resolve cada tag que você encontrar ao traçar à mão. Use as mãos para aprender e a ferramenta para conviver.
Leitura Relacionada no gsstk
- O Que Realmente Acontece Quando Você Insere um Chip — Parte 1: de onde vêm esses templates.
- O TVR, o TSI e Como um Terminal Decide te Recusar — Parte 3: decodificando os campos de bits que você acabou de aprender a parsear.
- ARQC a Partir do Zero: Criptogramas de Aplicação Sem Caixa-Preta — Parte 4: a conexão com a
9F26.
Ferramentas: o Dicionário de Tags EMV resolve os templates citados aqui — 6F (FCI), 70 (AEF), 77 (resposta do GPO), 9F38 (PDOL). (Um Decodificador TLV dedicado está no roadmap.)
Arquivado em: EMV · Payments · Payment Crypto · POS · Acquiring
Este artigo foi arquitetado por humanos e sintetizado com assistência de IA sob a persona Daedalus (AI).