User Tools

Site Tools


cvend

cVEND NFC Reader

cVEND is the NFC reader on the bottom half of the PM3

cvend_plug.pdf

The associated serial device appears to be at /dev/ttymxc3 in Linux.

cVEND protocol notes

Boot-up / initialization logic analyzer trace (open with the Saleae app):

cvend-bootup.sal

IPP packet structure

offset length name description
0 1 always 0xBC
1 1 seq monotonically increasing from 1; skips 0 when rolling over
2 1 flags unknown, usually 0
3 1 msgType
4 2 msgLen length of msgData
6 1 hdrCRC CRC-8-Dallas/Maxim over previous 6 bytes
7 msgLen msgData message payload, structure depends on msgType
7+msgLen 4 msgCRC little endian inverted IEEE CRC-32 over msgData, iff msgType & 0x80

IPP message types

msgType dir name description
0x02 Version
0x03 VersionReply (device ID from rockchip otp, firmware version, cwd) as length-prefixed strings
e.g. “\x18\x00\xdd\xf5\x14cS01.05.46-49.00-2-2\x0502.35\n/home/app0”
0x04 Status
0x05 StatusReply e.g. “\x00\x00”
0x07 Heartbeat sent periodically by reader
0x0f Startup sent by reader after startup, approx 1 minute after power on, e.g. “\x00”
0x10 Reset
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”
0x22 Buzzer makes the cvend beep; u16 frequency, u16 duration. e.g. “\x06\x00\x01\x00”
0x32 CardRelease registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)
0x46 AbortCardHandling registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&)
0x96 PutFile
0x97 PutFileReply
0x98 GetFile
0x99 GetFileReply
0x9a DeleteFile
0x9b DeleteFileReply
0x9c FileInfo
0x9d FileInfoReply
0xa4 FileList
0xa5 FileListReply
0xaa SetTime
0xab SetTimeReply
0xac ITSOData conditionally registered in ItsoIppHandler::_handleItsoCtrlReq(CommBuffer&, IppHeader const&, unsigned char const*)
0xad ITSODataReply
0xae ITSOCtrl registered in ItsoIppHandler::ItsoIppHandler(ProxCardItso&)
0xaf ITSOCtrlReply
0xb1 ISORead sent by reader when ISO14443A card presented, after enabling Iso with 0xe4
card UID at offset 2
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
0xb6 SAMCtrl registered in SamCtrl::SamCtrl()
0xb7 SAMCtrlReply
0xb9 DESFireRead sent by reader when DESFire card presented, after enabling DESFire with 0xe4
0xba unknown, registered in ProxCardDesfire::ProxCardDesfire(RFIDReader&)
0xbb DESFireCardRemoved sent by reader when DESFire card removed from field
0xbc DESFireCommand sends desfire command, documented in 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()
0xd0 EMV first byte selects subcommand (0 = load config, 1 = preprocess, 2 = toggle polling)
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&)
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
0xe6 unknown, registered in GirogoIppHandler::GirogoIppHandler(RFIDReader&, ProxCardGirogo*)
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&)
0xed Log first byte specifies log level (1=INFO, 2=WARNING, 3=ERROR) followed by null-terminated human-readable log message

→ - Host to Reader
← - Reader to Host

Sample reader flow

  1. Host enables desired card type with ProxCardFunction.
  2. Reader acknowledges with ProxCardFunctionReply.
  3. 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).
  4. 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.
  5. Card data can then be queried by sending the equivalent command packets. DESFire commands are documented in m075031_desfire.pdf. Example for reading a page off a DESFire ( all of the following packets are of type DESFireCommand and DESFireCommandReply):
  6. 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.
  7. Reader responds with a status code in accordance with the documentation. This comes in the form of a DESFireCommandReply packet.
  8. Host sends read command. To read the full contents of file 0x00, the body is 0xBD00000000000000.
  9. Reader responds with the status code and file data.

Reader -> Host

packet type 0xED seems to be a log message:

\xBC\xB9\0\xED\0h\xB6\x0306:48:13 Error: feclr_transceive() failed: error: 0, status: 0x00000008 

0xB9 - card read?

Host -> reader

0xBC - appears after every read (ack of some sort?)

Card read flow

R: BCB600B9000BBB0C000000048A6EF29B149079CB8CF0
H: BC2200BC000158602A714F60           # DESFire GetVersion (60)
R: BCB722BD001E3D00000401013300160504010103001605048A6EF29B149020487330305022C98C7E9B
H: BC2300BC0004AA5A2BC3F911A8A56A     # DESFire SelectApplication (5A 2BC3F9)
R: BCB823BD0002E301FDE0523164

Another one (same payloads as previous but with different seq/hdrCRC):

R:  BCBF00B9000B480C000000048A6EF29B149079CB8CF0
H:  BC2400BC0001C4602A714F60
R:  BCC024BD001E0800000401013300160504010103001605048A6EF29B149020487330305022C98C7E9B
H:  BC2500BC0004365A2BC3F911A8A56A
R:  BCC125BD00027401FDE0523164

Sample log message:

BCB000ED0068450330363A34383A3038204572726F723A206665636C725F7472616E7363656976652829206661696C65643A206572726F723A20302C207374617475733A2030783030303030303038200A205B524649445265616465724D756C74694170702E6370703A3135395D0002CD1533BCB100ED0046B40130363A34383A3038207472616E7363656976652065
00000000  bc b0 00 ed 00 68 45 03 30 36 3a 34 38 3a 30 38  |¼°.í.hE.06:48:08|
00000010  20 45 72 72 6f 72 3a 20 66 65 63 6c 72 5f 74 72  | Error: feclr_tr|
00000020  61 6e 73 63 65 69 76 65 28 29 20 66 61 69 6c 65  |ansceive() faile|
00000030  64 3a 20 65 72 72 6f 72 3a 20 30 2c 20 73 74 61  |d: error: 0, sta|
00000040  74 75 73 3a 20 30 78 30 30 30 30 30 30 30 38 20  |tus: 0x00000008 |
00000050  0a 20 5b 52 46 49 44 52 65 61 64 65 72 4d 75 6c  |. [RFIDReaderMul|
00000060  74 69 41 70 70 2e 63 70 70 3a 31 35 39 5d 00 02  |tiApp.cpp:159]..|
00000070  cd 15 33 bc b1 00 ed 00 46 b4 01 30 36 3a 34 38  |Í.3¼±.í.F´.06:48|
00000080  3a 30 38 20 74 72 61 6e 73 63 65 69 76 65 20 65  |:08 transceive e|

There are 4 bytes appended at the end of some messages (presumably those with ID LSB=1, if the Rust struct description is accurate?) which is not a CRC32 with any polynomial I recognize, but a CRC32 of (message + CRC) is always 0xFFFFFFFF. These 4 bytes are NOT included in the length.

The checksum is _appended_, as can be seen by it following the log message (after the final \0).

is it a _little endian_, _bit negated_ CRC32. Which is weird because the length is big endian?..

* Host only sends two types of messages after initialization - both of type 0xBC. One is length 1 and the body is always 0x60 (+ negated CRC 2A714F60), the other is of length 4 and the body is always 5A2BC3F9 (+ negated-CRC 11A8A56A)

cvend.txt · Last modified: by penguinowl