emv-3ds
EMV 3-D Secure 2.x protocol implementation in Rust.
A zero-dependency* Rust crate providing the complete message layer and transaction state machine for the EMV 3DS 2.x specification — the global standard used by Visa (3DS 2.0/2.2), Mastercard (Identity Check), American Express (SafeKey 2.0), and all major card networks to perform strong customer authentication (SCA) during card-not-present payments.
*Runtime dependencies are serde, serde_json, uuid, thiserror, and chrono only.
What is EMV 3-D Secure?
EMV 3DS (3-D Secure version 2.x) is the payment authentication protocol defined by EMVCo that enables issuers to verify cardholder identity during e-commerce transactions without redirecting to a static password page.
The protocol involves three parties:
| Role | Abbreviation | Responsibility |
|---|---|---|
| 3DS Server | 3DSS | Merchant-side component; sends AReq, receives ARes |
| Directory Server | DS | Card-scheme routing layer (Visa, Mastercard) |
| Access Control Server | ACS | Issuer-side component; authenticates cardholder |
A transaction follows one of two paths:
Frictionless: 3DSS ──AReq──► DS ──► ACS ──ARes(Y/A)──► 3DSS
Challenge: 3DSS ──AReq──► DS ──► ACS ──ARes(C)───► 3DSS
browser ──CReq──► ACS ──CRes(Y/N)──► 3DSS
Features
- All five protocol messages —
AReq,ARes,CReq,CRes,Errowith every field from the EMVCo 3DS Core Specification 2.3. - Correct wire format —
#[serde(rename)]for every acronym field (threeDSServerTransID,acsTransID,dsTransID,acsURL) thatrename_all = "camelCase"would mangle. - Transaction state machine — type-safe lifecycle from
CreatedthroughAwaitingARes→AwaitingCRes→Authenticated/NotAuthenticated/Failed, with invalid-transition errors. - Coded value enums —
TransStatus,Eci,ChallengeIndicator,MessageVersion,TransStatusReason(21 codes), and all other spec enumerations. - ECI / liability shift helpers —
Eci::has_liability_shift(),TransStatus::is_authenticated(),AuthenticationResponse::requires_challenge(). - ISO 4217 currency —
Currencynewtype with zero-padded spec string,Amountwithspec_amount/spec_currency/spec_exponentEMVCo field getters. - Message envelope —
Messageenum withfrom_json/to_jsonthat peeks atmessageTypefor dispatch without duplicating the field on the wire. - Quality-gated — 47 tests (unit + integration + proptest), 0
cargo-mutantssurvivors,clippy -D warningsclean.
Quick start
[]
= "0.1"
Build and send an AReq
use ;
use ;
let areq = AuthenticationRequest ;
let json = to_string?;
// POST json to the Directory Server endpoint
Drive the state machine
use TransactionState;
use ChallengeWindowSize;
// 1. Create transaction
let state = new;
// 2. Send the AReq — state machine gives you back the serialized message
let = state.areq_sent?;
// → POST serde_json::to_string(&outbound_areq) to the DS
// 3. Receive the ARes
let ares: AuthenticationResponse = from_str?;
let state = state.receive_ares?;
match &state
// 4. After challenge: receive CRes
let cres: ChallengeResponse = from_str?;
let state = state.receive_cres?;
Parse any incoming message
use Message;
let msg = from_json?;
match msg
Message types
| Struct | Wire name | Direction | Purpose |
|---|---|---|---|
AuthenticationRequest |
AReq |
3DSS → DS → ACS | Initiate authentication |
AuthenticationResponse |
ARes |
ACS → DS → 3DSS | Outcome or challenge redirect |
ChallengeRequest |
CReq |
Browser/SDK → ACS | Submit challenge data |
ChallengeResponse |
CRes |
ACS → 3DSS | Challenge outcome |
ErrorMessage |
Erro |
Any → Any | Protocol error |
Transaction state machine
Created
│ areq_sent()
▼
AwaitingARes
│ receive_ares()
├─ Y/A ──────────────────────► Authenticated (terminal)
├─ N/U/I/R ──────────────────► NotAuthenticated (terminal)
└─ C/D
│
▼
AwaitingCRes
│ receive_cres()
├─ Y ────────────────────► Authenticated (terminal)
└─ N/U ──────────────────► NotAuthenticated (terminal)
Any state + receive_error() ──► Failed (terminal)
ECI values
| Constant | Wire | Network | Meaning | Liability shift |
|---|---|---|---|---|
Eci::VisaFullyAuthenticated |
05 |
Visa | Full 3DS auth | ✓ |
Eci::VisaAttempted |
06 |
Visa | Attempted processing | ✓ |
Eci::VisaNotAuthenticated |
07 |
Visa | Failed / not enrolled | ✗ |
Eci::MastercardFullyAuthenticated |
02 |
Mastercard | Full 3DS auth | ✓ |
Eci::MastercardAttempted |
01 |
Mastercard | Attempted processing | ✓ |
Eci::MastercardNotAuthenticated |
00 |
Mastercard | Failed / not enrolled | ✗ |
Roadmap
- PReq / PRes — Directory Server preparation request (card range negotiation)
- RReq / RRes — Results request for decoupled and app-based authentication
- JWE envelope — EMVCo-mandated end-to-end encryption for
acctNumberandsdkEncDatafields - DS certificate management — JWK / PKCS#12 signing for AReq integrity
Spec conformance
This crate targets the EMVCo 3DS Core Specification v2.2.0 and v2.3.0. The spec is available (registration required) at emvco.com.
EMVCo 3DS is an open standard with no royalty requirements on implementations.
License
MIT — see LICENSE.