feat(emv): add nfc_emv_aip_decode — EMV Application Interchange Profile (tag 82) field decode#70
Merged
Merged
Conversation
…le (tag 82) field decode
The nfc_emv_* family already cracks the structured EMV fields the BER-TLV
walker surfaces raw — Track-2 (tag 57), DOL lists, AFL (tag 94), CVM List
(tag 8E). The Application Interchange Profile (tag 82) was named by the
tag dictionary but its 2-byte bitfield was never decoded, even though it is
the single most security-relevant field on an EMV card dump: it advertises
which offline data-authentication method the card supports (SDA / DDA / CDA),
plus cardholder verification, issuer authentication and terminal risk
management.
nfc_emv_aip_decode cracks it. Byte 1's seven defined bits are decoded per
EMV 4.3 Book 3, Annex C1 (SDA 0x40, DDA 0x20, cardholder verification 0x10,
terminal risk management 0x08, issuer authentication 0x04, on-device CVM
0x02 [EMV 4.3+], CDA 0x01). An offline_data_authentication headline picks
the strongest method present (CDA > DDA > SDA), flagging SDA-only cards as
replayable/clone-prone and no-offline-auth cards as online-only — the
takeaway an analyst actually wants.
No confidently-wrong output: byte 1 bit 8 and the whole of byte 2 are RFU
in the EMV Book 3 contact profile (contactless kernels repurpose byte 2),
so they are surfaced raw with a note rather than guessed. Gated structurally
to exactly 2 bytes. Accepts either the 2-byte AIP value directly or a full
EMV BER-TLV blob (tag 82 extracted automatically). Offline transform —
reads bytes, transmits nothing — so it is Low risk like the rest of the
EMV decode family.
Verification: the decode is a direct mask of the published EMV bit table,
so the single-bit vectors (each byte-1 bit in isolation) are authoritative;
plus realistic combined values (7C00 offline-DDA card, 1800 online-only,
0500 CDA), the CDA>DDA>SDA headline precedence, and RFU-note + 2-byte-gate
rejection paths. gofmt / go vet / golangci-lint (0 issues) /
go test -race (emv, risk, tools) all green.
Wired: internal/emv/{aip.go,aip_test.go}, internal/tools/emv_aip.go,
risk.go classification (Low, consistency test green), registry_size_test.go
676 -> 677 (count + comment), and the gap-analysis EMV row.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
nfc_emv_aip_decode, extending thenfc_emv_*decode family (Track-2 / DOL / AFL / CVM-List) to the Application Interchange Profile (tag 82) — the 2-byte bitfield the BER-TLV walker named but never decoded.The AIP is the single most security-relevant field on an EMV card dump: it advertises the card's offline data-authentication method (SDA / DDA / CDA), plus cardholder verification, issuer authentication, and terminal risk management.
Decode
Byte 1's seven defined bits per EMV 4.3 Book 3, Annex C1:
An
offline_data_authenticationheadline picks the strongest method present (CDA > DDA > SDA), flagging SDA-only cards as replayable/clone-prone and no-offline-auth cards as online-only.No confidently-wrong output
Byte 1 bit 8 and the whole of byte 2 are RFU in the contact profile (contactless kernels repurpose byte 2), so they're surfaced raw with a note, never guessed. Gated structurally to exactly 2 bytes. Accepts the 2-byte AIP value directly or a full EMV BER-TLV blob (tag 82 auto-extracted). Offline transform, transmits nothing → Low risk.
Verification
The decode is a direct mask of the published EMV bit table, so the per-bit vectors are authoritative. Tests cover: each byte-1 bit in isolation; realistic combined values (
7C00offline-DDA card,1800online-only,0500CDA); the CDA>DDA>SDA headline precedence; RFU notes; and the 2-byte structural gate.gofmt/go vetcleangolangci-lint run ./...→ 0 issuesgo test -race ./internal/emv/ ./internal/risk/ ./internal/tools/→ allokWired
internal/emv/{aip.go,aip_test.go},internal/tools/emv_aip.go,risk.go(Low),registry_size_test.go(count + comment), gap-analysis EMV row.