User Tools

Site Tools


cvend

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
cvend [2026-02-28 11:50 UTC (5 weeks ago)] – byte 2 not always 0 doofcvend [2026-03-17 20:08 UTC (2 weeks ago)] (current) – [Card reader flow] penguinowl
Line 1: Line 1:
 ====== cVEND NFC Reader ====== ====== cVEND NFC Reader ======
- 
  
 cVEND is the NFC reader on the bottom half of the PM3 cVEND is the NFC reader on the bottom half of the PM3
Line 7: Line 6:
  
 The associated serial device appears to be at ''%%/dev/ttymxc3%%'' in Linux. The associated serial device appears to be at ''%%/dev/ttymxc3%%'' in Linux.
- 
  
 ===== cVEND protocol notes ===== ===== cVEND protocol notes =====
Line 20: Line 18:
 | 0 | 1 | | always 0xBC | | 0 | 1 | | always 0xBC |
 | 1 | 1 | ''seq'' | monotonically increasing from 1; skips 0 when rolling over | | 1 | 1 | ''seq'' | monotonically increasing from 1; skips 0 when rolling over |
-| 2 | 1 | | unknown, usually 0 |+| 2 | 1 | ''flags'' | unknown, usually 0 |
 | 3 | 1 | ''msgType'' | | | 3 | 1 | ''msgType'' | |
 | 4 | 2 | ''msgLen'' | length of ''msgData'' | | 4 | 2 | ''msgLen'' | length of ''msgData'' |
Line 38: Line 36:
 | 0x10 | -> | Reset | | | 0x10 | -> | Reset | |
 | 0x11 | <- | ResetReply | | | 0x11 | <- | ResetReply | |
-| 0x20 | -> | Leds | sets LED status, u32 bitmap; the only two externally visible LEDs on the PM3 are "\x00\x00\x00\x40" and "\x00\x00\x00\x80" |+| 0x20 | -> | LEDs | sets LED status, u32 bitmap; the only two externally visible LEDs on the PM3 are "\x00\x00\x00\x40" and "\x00\x00\x00\x80" |
 | 0x22 | -> | Buzzer | makes the cvend beep; u16 frequency, u16 duration.  e.g. "\x06\x00\x01\x00" | | 0x22 | -> | Buzzer | makes the cvend beep; u16 frequency, u16 duration.  e.g. "\x06\x00\x01\x00" |
-| 0x32 | -> | | //unknown, registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)//+| 0x32 | -> | CardRelease | // registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)//
-| 0x46 | -> | | //unknown, registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)// |+| 0x46 | -> | AbortCardHandling | //registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)// |
 | 0x96 | -> | PutFile | | | 0x96 | -> | PutFile | |
 | 0x97 | <- | PutFileReply | | | 0x97 | <- | PutFileReply | |
Line 59: Line 57:
 | 0xaf | <- | ITSOCtrlReply | | | 0xaf | <- | ITSOCtrlReply | |
 | 0xb1 | <- | ISORead | sent by reader when ISO14443A card presented, after enabling Iso with 0xe4\\ card UID at offset 2 | | 0xb1 | <- | ISORead | sent by reader when ISO14443A card presented, after enabling Iso with 0xe4\\ card UID at offset 2 |
-| 0xb4 | -> | APDUProx | //registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)// |+| 0xb3 | <- | ISOCardReleased | sent by reader after ISO1443A card released with 0x32 or 0x46 | 
 +| 0xb4 | -> | APDUProx | CLA, INS, P1, P2, Lc (2 bytes), Data, Le (2 bytes), and 2 other mystery bytes, in some order |
 | 0xb5 | <- | APDUProxReply | | | 0xb5 | <- | APDUProxReply | |
 | 0xb6 | -> | SAMCtrl | //registered in SamCtrl::SamCtrl()// | | 0xb6 | -> | SAMCtrl | //registered in SamCtrl::SamCtrl()// |
Line 65: Line 64:
 | 0xb9 | <- | DESFireRead | sent by reader when DESFire card presented, after enabling DESFire with 0xe4 | | 0xb9 | <- | DESFireRead | sent by reader when DESFire card presented, after enabling DESFire with 0xe4 |
 | 0xba | -> | | //unknown, registered in ProxCardDesfire::ProxCardDesfire(RFIDReader&)// | | 0xba | -> | | //unknown, registered in ProxCardDesfire::ProxCardDesfire(RFIDReader&)// |
-| 0xbc | -> | | //unknownregistered in ProxCardDesfire::ProxCardDesfire(RFIDReader&)// +| 0xbb | <- | DESFireCardRemoved | sent by reader when DESFire card removed from field | 
-| 0xbe | <- | PICCRead | sent by reader when a card is read, containing UID, historical bytes, and other data |+| 0xbc | -> | DESFireCommand sends desfire commanddocumented in {{ 0:m075031_desfire.pdf}} | 
 +| 0xbd | <- | DESFireCommandReply | response to command, documented above 
 +| 0xbe | <- | UnhandledCard | sent by reader when a card is presented that is not supported by any enabled ProxCardFunction, containing UID, historical bytes, and other data |
 | 0xce | -> | | //unknown, registered in IppHandling::IppHandling()// | | 0xce | -> | | //unknown, registered in IppHandling::IppHandling()// |
-| 0xd0 | -> | | //unknown, registered in EmvIppHandler::EmvIppHandler()// +| 0xd0 | -> | EMV first byte selects subcommand (0 = load config, 1 = preprocess, 2 = toggle polling) | 
-| 0xd1 | <- | EMVTransactionSuccessUnk | sent by reader after startup and certain nfc state changes, format and semantics not yet understood |+| 0xd1 | <- | EMVStatus | sent by reader after startup and certain nfc state changes, format and semantics not yet understood |
 | 0xd4 | -> | | //unknown, registered in ProxCardUltralightC::ProxCardUltralightC(RFIDReader&)// | | 0xd4 | -> | | //unknown, registered in ProxCardUltralightC::ProxCardUltralightC(RFIDReader&)// |
-| 0xe4 | -> | ProxCardFunction | first 2 bytes specify function (4 = VdvKa, 5 = MifareClassic, 6 = Iso, 7 = Desfire, 8 = Girogo, 9 = Itso, 10 = UltralightC), remaining bytes unknown\\ ''00070101'' sent by PM3 to enable DESFire reading at startup |+| 0xe4 | -> | ProxCardFunction | first 2 bytes specify function (4=VdvKa, 5=MifareClassic, 6=Iso, 7=Desfire, 8=Girogo, 9=Itso, 10=UltralightC), next byte must be 1, next byte (0=disable, 1=enable)\\ ''00070101'' sent by PM3 to enable DESFire reading at startup |
 | 0xe5 | <- | ProxCardFunctionReply | | | 0xe5 | <- | ProxCardFunctionReply | |
 | 0xe6 | -> | | //unknown, registered in GirogoIppHandler::GirogoIppHandler(RFIDReader&, ProxCardGirogo*)// | | 0xe6 | -> | | //unknown, registered in GirogoIppHandler::GirogoIppHandler(RFIDReader&, ProxCardGirogo*)// |
-| 0xe8 | -> | | //unknown, registered in SecurityServicesIppHandler::SecurityServicesIppHandler()// |+| 0xe8 | -> | SecurityServices first byte selects subcommand (0=GetVersionOfKey, 1=RemoveX509Cert, 2=RemoveKeyBlock, 3=ImportX509Cert, 4=ExportX509Cert, 5=ImportKeyBlock, 6=ExportKeyBlock, 7=GenerateKSKPair), remaining bytes unknown | 
 +| 0xe9 | <- | SecurityServicesReply | |
 | 0xea | -> | | //unknown, registered in ProxCardMifareClassic::ProxCardMifareClassic(RFIDReader&)// | | 0xea | -> | | //unknown, registered in ProxCardMifareClassic::ProxCardMifareClassic(RFIDReader&)// |
-| 0xed | <- | Log | human-readable log message from reader |+| 0xed | <- | Log | first byte specifies log level (1=INFO, 2=WARNING, 3=ERROR) followed by null-terminated human-readable log message |
  
 -> - Host to Reader\\ -> - Host to Reader\\
 <- - Reader to Host <- - Reader to Host
 +
 +==== Sample reader flow ====
 +
 +  - Host enables desired card type with ''ProxCardFunction''.
 +  - Reader acknowledges with ''ProxCardFunctionReply''.
 +  - Reader waits for card, seems like it eventually goes to sleep without any stimuli. Might have to keep it awake by sending packets occasionally (e.g. ''Status'').
 +  - When card is scanned, reader sends the corresponding read packet if the card type is enabled (e.g. ''DESFireRead'' for DESFire). If type is not enabled, sends ''UnhandledCard''.
 +  - Card data can then be queried by sending the equivalent command packets. DESFire commands are documented in {{ 0:m075031_desfire.pdf }}. Example for reading a page off a DESFire ( all of the following packets are of type ''DESFireCommand'' and ''DESFireCommandReply''): 
 +  - Host sets DESFire application (e.g. packet type ''DESFireCommand'' with body ''0x5AF210E0'' for application ID ''0xE010F2''). **NOTE:** The application ID will vary between issuer/agency. For example, the stock software uses application ID ''F9C32B'' while Portland's TriMet uses the one in the example. You can check this for your card with an NFC reader or app. 
 +  - Reader responds with a status code in accordance with the documentation. This comes in the form of a ''DESFireCommandReply'' packet.
 +  - Host sends read command. To read the full contents of file 0x00, the body is ''0xBD00000000000000''.
 +  - Reader responds with the status code and file data.
  
 ==== Reader -> Host ==== ==== Reader -> Host ====
cvend.1772279410.txt.gz · Last modified: by doof