This is an old revision of the document!
Table of Contents
cVEND NFC Reader
cVEND is the NFC reader on the bottom half of the PM3
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):
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 | → | unknown, registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&) | |
| 0x46 | → | unknown, 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 |
| 0xb4 | → | APDUProx | registered in ProxCardCtrl::ProxCardCtrl(RFIDReader&) |
| 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&) | |
| 0xbc | → | unknown, registered in ProxCardDesfire::ProxCardDesfire(RFIDReader&) | |
| 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 | → | unknown, registered in EmvIppHandler::EmvIppHandler() | |
| 0xd1 | ← | EMVTransactionSuccessUnk | 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), remaining bytes unknown00070101 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
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)
