Skip to content

ANASS812/apple-enhanced-contactless-polling

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Apple Enhanced Contactless Polling (ECP)

![ECP Access Home with PN532] ![ECP Transit Clipper with Flipper Zero]

Overview

Enhanced Contactless Polling/Protocol (ECP) is a proprietary extension to the ISO/IEC 14443 (A/B) standard developed by Apple.

It defines a custom data frame that a reader transmits during the polling sequence, giving an end device contextual info about the reader field, allowing it to select an appropriate applet even before any communication starts.

This extension:

  • Helps to make sure that end device will only start communication with the reader if it has something useful to do with it, avoiding error beeps and card clashing;
  • Increases privacy and security as it complicates brute force scanning for available passes on the device in a single tap.
  • Allows automatic usage of non ISO7816-compliant passes:
    • DESFire in native mode and on card-level instead of app-level;
    • Passes without application id: Mifare Plus, Ultralight, Classic etc.
  • Helps with conflict resolution when there are multiple passes with the same AID:
    • For instance, both Gymkit and ISO18013 use NDEF AID for BLE handover. ECP allows to differentiate between them in advance.
  • May serve as a form of NFC DRM, requiring reader manufacturers to pay licensing fees in order to be able to use this feature and provide better experience for Apple users.

Use cases

Express mode for most passes (apart from NFC-F and CATHAY) is implemented using ECP. That includes:

  • Credit cards (For transit fallback);
  • Transit cards;
  • Access passes:
    • University;
    • Office badges;
    • Venue (Theme parks);
    • Apartment;
    • Hotel.
  • Keys:
    • Car;
    • Home.

Other features use ECP as well:

  • Value Added Services (VAS):
    Allows reader to select the VAS applet and try to get pass in advance (although failing to do so), causing pass to appear on a screen for authentication or under a payment card if one is selected.
    ![Image showing VAS] ![Image showing VAS] ![Image showing VAS]
  • GymKit:
    Makes apple watch act as an NDEF tag for BLE handover in order to connect to supported gym equipment.
    ![Image showing GymKit setup]
  • Identity:
    Makes apple device act as an NDEF tag for BLE handover in order to connect to a ISO18013 verfier.

    ![ISO18013 Request promt]

  • CarKey Setup:
    Tells the device what car brand it is, causing a car key setup popup to appear on a screen.

    ![Image showing IOS16 CarKey pairing prompt] ![Image showing IOS17 CarKey pairing prompt]

  • Field ignore:
    Makes apple devices not react (by react meaning displaying a default payment card) to a field generated by other apple devices.
  • AirDrop:
    Replaces field ignore in IOS17 for background reading, used to negotiate an AirDrop session. NameDrop is a special case of AirDrop. Triggers a warp animation.

    ![AirDrop warp animation preview]

  • HomeKit:
    Allows appliances with an NFC reader that lack card emulation mode to convey pairing info and bring up a pairing prompt when a user device is brought near to it.

Device support

Reader side:

  • Can be implemented in software on most devices, provided that a low-level access to NFC hardware is available. In some cases it is required to reimplement parts of the protocol stack in software when doing so.
    HALs/Libraries for most popular chips contain separate confidential versions that include ECP support and are given to approved partners only, but homebrew solution is easy to implement.
    Proof of concept was successfuly tested using PN532, PN5180, ST25R3916(B) chips;
  • IOS has special reader APIs that make the device emit specific ECP frames:
    • NFCVASReaderSession, PaymentCardReaderSession for VAS;
    • MobileDocumentReaderSession for Identity;
    • When using other derivatives of NFCReaderSession, device emits Ignore frame so that other apple devices don't react to it.
  • Android does not have an API for ECP, although some android-based handheld reader manufacturers have implemented this feature in their software.

Device side:

  • Implemented using a customized CRS applet.
  • Can be implemented on chips that allow reading raw frames in emulation mode even before selection.

Decision logic

Upon entering a loop, device does not answer to the first polling frame it sees, instead opting to wait and see what other technologies does the field poll for, allowing it to make a fully informed decision on what applet to select later.

When device makes a decision, it is mostly, although not in all cases (excluding keys) signified by a card image appearing along with a spinner.

Even though ECP is sent during the polling loop, device does not answer to it. Instead it responds to a polling frame related to technology of the pass that the device had decided to use.

![Image showing express mode animation after decision]

When device enters the loop initially:

  • In case of a full polling loop (A,B,F) it waits through one full iteration before making a decision on what applet to select:
(ENTRY) -> A -> ECP_A -> B -> ECP_B -> F -> (DECISION) -> A -> (RESPONSE)
A -> ECP_A -> (ENTRY) -> B -> ECP_B -> F -> A -> ECP_A -> (DECISION) -> B -> (RESPONSE)
  • In case of partial or wierdly-ordered polling loop, behavior is different. For example:
(ENTRY) -> A -> ECP_A -> A -> ECP_A -> (DECISION) -> A -> (RESPONSE)
(ENTRY) -> F -> B -> ECP_B -> A -> F -> B -> (DECISION) -> ECP_B -> A -> (RESPONSE)
(ENTRY) -> A -> ECP_B -> F -> A -> ECP_B -> (DECISION) -> F -> A -> (RESPONSE)
(ENTRY) -> F -> F -> F -> (DECISION) -> F -> (RESPONSE)
(ENTRY) -> A -> A -> A -> (DECISION)
(ENTRY) -> A -> ECP_A -> F -> A -> ECP_A -> F -> (DECISION) -> A -> (RESPONSE)

Characters A, B, and F were used in examples as a shorthand for full polling frame names: WUPA, WUPB, SENSF_REQ respectively. ECP frame has different values depending on a use case _A/B suffix refers to modulation used.

In conclusion, it seems that if reader is polling for:

  • 1 technology, decision is made after third poll, response is given on the fourth;
  • 2 technologies, decision is made after the second polling loop, while the response is given on the third.
  • 3 technologies, decision is made after the first loop, response is given on the second.

Tests were conducted using very big intervals between polling frames. IRL if polling is faster device might respond after more frames than shown, presumably because of internal processing delay.

Although not possible during normal operation, if a reader is polling for multiple cards using express mode that use different technology qualifiers for selection, following technology priority will be applied:

  1. ECP
  2. NFC-F
  3. CATHAY

(BUG) If polling for both ECP and NFC-F, device will display NFC-F card in animation while actually selecting and emulating NFC-A/NFC-B applet.

(NOTE) In IOS17 new AirDrop frame does not follow the beforementioned rules, as device reacts to it on first iteration in all cases.

Structure

Frame format

Each ECP frame consists of a header, version, payload and CRC:

     6A         XX          XX...     XX XX
  [Header]  [Version]  [Payload (n)]  [CRC]
  • Header byte has a constant value of (HEX) 6A;
  • Version number can be either 0x01 or 0x02;
  • Payload: Version-dependant;
  • CRC (Calculated via ISO14443A/B algorithm, according to the modulation used).

Payload

For V1 payload consists only of a single TCI:

  XX XX XX
   [TCI]  
  • TCI is a 3 byte long identifier. More info below.

For V2 payload contains terminal configuration, terminal type, terminal subtype, and data:

     XX       XX        XX         XX...
  [Config]  [Type]  [Subtype]  [Data (n)]
  • Configuration byte has a following binary format:
          1        X        0 0      X X X X
      [Unknown]  [Auth]  [Unknown]  [Length ]  
    
    • Auth: 0b1 if authentication not required, 0b0 otherwise.
      If auth is required pass will be presented on a screen for manual authentication when brought near to the field.
    • Length: defines a length of data.
  • Type contains terminal type:
    • 0x01: Transit;
    • 0x02: Access;
    • 0x03: Identity (Handoff);
    • 0x05: AirDrop.
  • Subtype depends on type. In most cases it has a value of 0x00;
  • Data. Its content and availability depend on terminal type and subtype. Detailed description below.

Data

Data is a part of payload in V2, it contains TCIs and extra data:

  XX XX XX...        XX..
  [TCIs (n)]   [Extra data (n)]
  • TCIs define an array of 3 byte long indentifiers. Standard allows for 0-n long TCI arrays to be conveyed depending on terminal type and subtype;
  • Extra data contents depend on terminal type, subtype, and TCIs:
    • For access/key readers it may contain a 8 byte long unique reader group identifier, which allows to differentiate between them for passes of the same type;
    • For HomeKit it contains pairing information;
    • For NameDrop it carries a 6 byte long BLE MAC address;
    • For AirDrop it carries a 6 byte long zeroed out value.

TCI

TCI, also referred to as Terminal Capabilities Identifier, is an arbitrary three-byte-long value that establishes reader relation to a particular pass type (Home key, Car key, Transit) or system feature (Ignore, GymKit, AirDrop, NameDrop).

The following restrictions apply to the use of TCI:

  • Some TCIs are bound to a reader with particular type and subtype (which requires V2), while others trigger for all types (support V1). It is not known if this behavior is a bug or was intentional;
  • TCIs intended for V1 cannot be used with V2.

TCI format is arbitrary, although several patterns related to grouping of similar functionality can be established:

  • VAS: grouped with the last byte having a value of 0x00, 0x01, 0x02, 0x03 depending on mode;
  • Access (Car/Home/University/Office/Venue): First byte is static, other two link to a particular pass provider;
  • Transit: First byte is static, other two link to a particular transit agency (and their pass);
  • CarKey: usually grouped by car manufacturer, consequent values signal readers on front/back doors,charging pad, etc. First byte is always 0x01. Can be seen in wallet configuration json hosted at smp-device-content.apple.com.

Configuration examples

Note that CRC A/B, ECP Header, Configuration bytes are omitted from this table.

NA - not applicable; XX - any; ?? - unknown

Name Version Type Subtype TCI Data Description
VAS or payment 01 NA NA 00 00 00 NA
VAS and payment 01 NA NA 00 00 01 NA
VAS only 01 NA NA 00 00 02 NA
Payment only 01 NA NA 00 00 03 NA Serves as anti-CATHAY
Ignore 01 NA NA cf 00 00 NA
Transit 02 01 00 XX XX XX XX XX XX XX XX TCI refers to a transit agency, Data is a mask of allowed EMV payment networks for fallback
Transit: Ventra 02 01 00 03 00 00 ?? ?? ?? ?? ??
Transit: HOP Fastpass 02 01 00 03 04 00 ?? ?? ?? ?? ??
Transit: WMATA 02 01 00 03 00 01 ?? ?? ?? ?? ?? Will select a Smart Trip card
Transit: TFL 02 01 00 03 00 02 79 00 00 00 00 Discovered by "Payment Village" and Proxmark community. Allows Amex, Visa, Mastercard, Maestro, VPay
Transit: LA Tap 02 01 00 03 00 05 ?? ?? ?? ?? ??
Transit: Clipper 02 01 00 03 00 07 ?? ?? ?? ?? ??
Access 02 02 XX XX XX XX XX XX XX XX XX XX XX XX TCI refers to a pass provider, Data is reader group identifier
Access: Venue 02 02 00 XX XX XX XX XX XX XX XX XX XX XX
Access: Home Key 02 02 06 02 11 00 XX XX XX XX XX XX XX XX Having more than one key breaks usual ECP logic
Access: Car Pairing 02 02 09 XX XX XX NA TCI refers to a combination of car manufacturer + reader position
Access: Car Pairing: Mercedes 02 02 09 01 02 01 NA
Identity 02 03 00 NA/00 NA/00 Only ECP frame found IRL that lacks a full TCI. Could this mean that TCI length is variable or it could be missing and the extra byte is data instead?
AirDrop 02 05 00 01 00 00 00 00 00 00 00 00 Sent only after device sees a NameDrop frame
NameDrop 02 05 00 01 00 01 XX XX XX XX XX XX Data part contains a BLE MAC-address

Full frame examples

Examples contain frames without CRC, which needs to be calculated according to the modulation used;

  • VAS or payment:
    6a01000000

         6a         01      000000 
      [Header]  [Version]   [TCI]  
    
  • Ignore
    6a01cf0000

         6a         01      cf0000   
      [Header]  [Version]   [TCI]  
    
  • NameDrop:
    6a02890500010001deadbeef6969

         6a         02        89       05      00      010001  deadbeef6969 
      [Header]  [Version]  [Config]  [Type] [Subtype]  [TCI]     [Data]     
    
      1000        1001
      [NA]  [Payload length]
    
  • Access: Car Pairing: Mercedes:
    6a02c30209010201

         6a         02        c3       02      09      010201 
      [Header]  [Version]  [Config]  [Type] [Subtype]  [TCI]  
    

Note that for examples to work 8-bit byte setting should be set in case of NFC-A, 2-byte CRC has to be appended beforehand.

If you have a Proxmark3, you can test those frames using commands hf 14a raw -akc for NFC-A and hf 14b raw -kc -d for NFC-B. After sending the frame try polling the device using the hf 14a reader -sk or hf 14b reader command.

Contributing

Missing pieces

Best way to help is to provide more samples of ECP frames and TCIs.
Especially interesting (missing) are the following:

  • TCIs of transit agencies that use EMV only:
    • France:
      • Dreux;
      • Lyon;
      • Montargis;
      • Nevers;
      • Tarbes-Lourdes.
    • Estonia:
      • Tartu.
    • Finland:
      • Turku.
    • Sweeden:
      • Malmo.
  • Access readers (There might be many unknown variations, so any samples would be welcome):
    • University;
    • Office;
    • Venues;
    • Hotels.
  • HomeKit pairing;
  • Identity (Real device).

Frames missing from the example table but not mentioned above were collected but not yet analyzed and publicized.

Collecting information

Sniffing

One way to get this information is via a sniffing functionality of a device like Proxmark (Easy or RDV2/4) connected to a Proxmark client inside of Termux running on an Android phone. A couple of tidbits encountered:

  • First time using the app I've encountered an issue connecting to Proxmark3 directly as Termux did not connect a device, TCPUART app had to be installed to forward serial connection over the local network to be used in Proxmark client inside of Termux;
  • Some Android phones won't power Proxmark properly through direct connection. Connecting via a USB-C to USB-A dongle can help to overcome this issue.

More info on installing and running Proxmark client on your Android device here.

The command needed to collect traces is hf 14a sniff, after activating the command hold the Proxmark near a reader for a couple of seconds. In some cases it is needed to tap/touch the reader in order to wake it up as it might not poll to save energy.

After that, press a button on a device, traces will be downloaded and can be viewed with a hf 14a list command. You'll know which ones are the ones.

Some other devices might also be able to sniff the frames, but due to a lack of personal experience I cannot recommend any.

Analysing pass files

The second way of retreiving useful information could be pkpass file analysis. There are two known ways of getting those files:

  1. Jailbreaking your iPhone and looking into wallet app data;
  2. Adding a card to a Mac, and accessing its info from the wallet directory:
    1. Go to "~/Library/Passes/Cards/" directory.
    2. Click on any .pkpass, select "Show Package Contents"
    3. Open the "pass.json" file.
    4. Inside the file, search for keywords tci, openloop, ecp, transit, automatic, selection, express, as in example:
      {
        "formatVersion":1,
        "passTypeIdentifier":"paymentpass.com.apple",
        "teamIdentifier":"Apple Inc.",
        "paymentApplications":[
          {
              "secureElementIdentifier":"133713371337",
              "applicationIdentifier":"69696969696969696969",
              "applicationDescription":"Mastercard",
              "defaultPaymentApplication":true,
              "supportsOptionalAuthentication":1,
              "automaticSelectionCriteria":[
                  {
                      "type":"ecp.2.open_loop",
                      "supportsExpress":true,
                      "openLoopExpressMask":"0800000000"
                  }
              ],
              "supportedTransitNetworkIdentifiers":[],
          }
        ]
      }
      
      Other fields removed to reduce space taken
      Here we see that a Mastercard has automatic selection creteria 0800000000 which corresponds to 00001000 in binary for the first byte of transit data mask.
      Other cards can be analysed the same way.

Notes

  • This document is based on reverse-engineering efforts done without any access to original documentation. Consider all information provided here as an educated guess that is not officially cofirmed.
  • If you find any mistakes/typos or have extra information to add, feel free to raise an issue or create a pull request.
  • Refer to resources directory to access json database of ECP frames. This info will also be pushed to Proxmark3 repository from time to time.

References

About

Reverse-engineering Apple Enhanced Contactless Polling

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published