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 frictionless or challenge transaction:
Frictionless: 3DSS ──AReq──► DS ──► ACS ──ARes(Y/A)──► 3DSS
Challenge: 3DSS ──AReq──► DS ──► ACS ──ARes(C)───► 3DSS
browser ──CReq──► ACS ──CRes(Y/N)──► 3DSS
Decoupled: 3DSS ──AReq──► DS ──► ACS ──ARes(D)───► 3DSS
ACS ──RReq──► 3DSS ──RRes──► ACS
DS preparation (card range negotiation):
3DSS ──PReq──► DS ──PRes(card ranges + threeDSMethodURL)──► 3DSS
Features
- All nine protocol messages —
AReq,ARes,CReq,CRes,Erro,PReq,PRes,RReq,RReswith every field from the EMVCo 3DS Core Specification 2.3. - Correct wire format —
#[serde(rename)]for every acronym field (threeDSServerTransID,acsTransID,dsTransID,acsURL,threeDSMethodURL) thatrename_all = "camelCase"would mangle. - Transaction state machine — type-safe lifecycle from
CreatedthroughAwaitingARes→AwaitingCRes/AwaitingRReq→Authenticated/NotAuthenticated/Failed, with invalid-transition errors. - Card range negotiation —
PreparationResponse::range_for_pan()to look up the card range entry (includingthreeDSMethodURL) for a given PAN prefix. - Decoupled authentication —
AwaitingRReqstate,receive_rreq()transition, andResultsResponse::acknowledge()builder for the ACS callback flow. - Coded value enums —
TransStatus,Eci,ChallengeIndicator,MessageVersion,TransStatusReason(21 codes),ActionIndicator,AcsAuthMethod,AuthenticationType,ResultsStatus, 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 — 60 tests (unit + integration + proptest), 0
cargo-mutantssurvivors,clippy -D warningsclean.
Quick start
[]
= "0.2"
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
// 4a. After browser challenge: receive CRes
let cres: ChallengeResponse = from_str?;
let state = state.receive_cres?;
// 4b. After decoupled challenge: receive RReq, send back RRes
let rreq: ResultsRequest = from_str?;
let = state.receive_rreq?;
// POST serde_json::to_string(&rres) back to the ACS
Negotiate card ranges with PReq/PRes
use ;
use MessageVersion;
let preq = PreparationRequest ;
// POST to DS, receive PRes
let pres: PreparationResponse = from_str?;
// Look up the card range for a PAN
if let Some = pres.range_for_pan
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 |
PreparationRequest |
PReq |
3DSS → DS | Request card range data |
PreparationResponse |
PRes |
DS → 3DSS | Card ranges + threeDSMethodURL |
ResultsRequest |
RReq |
ACS → 3DSS | Decoupled/app auth results |
ResultsResponse |
RRes |
3DSS → ACS | Acknowledge results receipt |
Transaction state machine
Created
│ areq_sent()
▼
AwaitingARes
│ receive_ares()
├─ Y/A ──────────────────────► Authenticated (terminal)
├─ N/U/I/R ──────────────────► NotAuthenticated (terminal)
├─ C ────────────────────────► AwaitingCRes
│ │ receive_cres()
│ ├─ Y ──────────► Authenticated (terminal)
│ └─ N/U ────────► NotAuthenticated (terminal)
└─ D ────────────────────────► AwaitingRReq
│ receive_rreq()
├─ 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
- AReq / ARes — Core authentication messages
- CReq / CRes — Browser/SDK challenge messages
- Erro — Protocol error message
- 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.