Skip to content

nfcgate/protocol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 

Repository files navigation

NFCGate Protocol

This repository contains the source files that are used in the server, MitM-Proxy, and main application. They have to be compiled using Google Protocol Buffers, version 2.6.1 (NOT 3.X). The compiled files are already shipped within the respective repositories, so you only need to touch these files if you want to modify the protocol.

Protocol Specifications

The protocol offers a few different functions, each with their own protocol messages. In this document, we'll try to give an overview of how the protocol works. Again, you only need to know this if you want to change it or implement your own clients. The message types are split into three different protobuf files:

  • c2s.proto contains messages passed between client and server. This is mostly related to session management and message passing
  • c2c.proto contains messages passed between two clients. This includes the meat of the protocol: Passing NFC data and status information
  • metaMessage.proto contains only one data type: A Wrapper message that is used to identify the type of message that is passed

Wrapper Messages

Wrapper messages are only required because we can only parse the transferred bytes into Protobuf messages if we know the type of message to parse it into. For this purpose, we introduced the Wrapper message, which contains exactly one message any other message type. That way, we can parse any incoming message as a Wrapper message and then use the functions the parsed message offers to identify which type of message is contained in it.

Developers: If you want to send any message, you MUST wrap it in a Wrapper message. If you want to add new message types, you need to add them to this message type as well (and obviously add support for it in all clients that need to understand it).

Session Management

Clients connect using sessions. Each session can have one or two participants (although a session with one participant is not very useful and generally only happens while you wait for the second device to connect). Sessions are identified using a 6-digit session token, which is generated by the server.

A session is started by a client sending a Session Message (defined in c2s.proto) with an opcode of SESSION_CREATE and a SessionErrorCode of ERROR_NOERROR (this should be the default value anyway). As mentioned before, the Session message MUST be wrapped in a Wrapper message. It can then be serialized into a byte stirng and sent over the wire to the server. The server will parse the Wrapper message, see that it contains a Session message, parse that message, see that it is a session creation request, and then perform the following algorithm (as pseudocode, if errcode is not set, it defaults to ERROR_NOERROR. Replies are always Session messages wrapped in a Wrapper message):

if (client already in session):
    reply (opcode=SESSION_CREATE_FAIL, errcode=ERROR_CREATE_ALREADY_HAS_SESSION)
else:
    generate random unique 6-digit token
    create session identified by that token
    add client to that session
    reply (opcode=SESSION_CREATE_SUCCESS, session_secret=token)

Clients can join a session by sending a Session message with an opcode of SESSION_JOIN, errcode ERROR_NOERROR, and session_secret set to the session token. The message is wrapped in a Wrapper message and passed to the server. The server then performs the following algorithm (pseudocode, if errcode is not defined, it is set to ERROR_NOERROR, replies are always Session messages wrapped in a Wrapper message):

if (token not in database):
    reply (opcode=SESSION_JOIN_FAIL, errcode=ERROR_JOIN_UNKNOWN_SECRET)
else if (session already has two participants):
    reply (opcode=SESSION_JOIN_FAIL, errcode=ERROR_JOIN_SESSION_FULL)
else if (client is already in a session):
    reply (opcode=SESSION_JOIN_FAIL, errcode=ERROR_JOIN_ALREADY_HAS_SESSION)
else:
    Add client to session
    reply (opcode=SESSION_JOIN_SUCCESS)
    notify other client that client has joined session (opcode=SESSION_PEER_JOINED)

Clients can also leave a session by sending a Session message with opcode SESSION_LEAVE and session_secret set to the session token. The server will perform the following algorithm:

if (client not in session identified by session_secret):
    reply (opcode=SESSION_LEAVE_FAIL, errcode=ERROR_LEAVE_NOT_JOINED)
else if (no session with that token exists):
    reply (opcode=SESSION_LEAVE_FAIL, errcode=ERROR_LEAVE_NOT_JOINED)
else:
    remove client from session
    reply (opcode=SESSION_LEAVE_SUCCESS)
    if (session empty):
        remove session from database
    else:
        notify other client (opcode=SESSION_PEER_LEFT)

This concludes the session management part of the protocol.

Message passing between clients

Once two clients are in a session, they can pass messages to each other. The process may seem overly complicated at first glance, but there is a reason for everything, and they will be explained over the course of this documentation.

Data messages

Data messages are a defined in c2s.proto. They are used to wrap messages between clients in a way the server can understand. They contain an error code, which is used by the server to communicate forwarding errors if they occur, and a raw byte array, which contains the byte-encoded message that is passed. The data message is, as always, wrapped in a Wrapper message, and the byte-encoded message is always a serialized Wrapper message. So, sending any message between clients involves the following stack:

Create message that should be passed to other client (discussed later)
Wrap in Wrapper message
Encode as byte array
Put byte array into data message 
Wrap data message in Wrapper message 
Send to server

The reasons such a complicated scheme was chosen is encryption. Originally, we planned to perform end-to-end-encryption on all messages. In that case, a way to pass arbitrary byte strings between clients was required, so the Data message was born. Due to time constraints, the E2E encryption was dropped from the plans for version 1.0, but the code was already built using Data messages. As we are still planning on adding encryption eventually, we decided not to rewrite everything, only to re-write it back once encryption is added, and instead stuck with the Data message. For the same reason, the byte array contained in the data message must contain a Wrapper message: We need to be able to parse it into a known message type, before being able to identify the type of message that was actually sent.

If the server receives a wrapped Data message, it performs the following algorithm (replies are Data messages with an empty byte blob):

if (client is not in a session):
    reply (errcode=ERROR_NO_SESSION)
else if (no second client in session):
    reply (errcode=ERROR_NO_SESSION)
else:
    try to forward message to other client
    if (error occured during forwarding):
        reply (errcode=ERROR_TRANSMISSION_FAILED)
    else:
        reply (errcode=ERROR_NOERROR)

Now that we know how messages are passed between clients, let's take a look at the messages that can be passed. All of the following messages are passed in the same way: Create message, wrap in Wrapper, serialize to byte string, create Data message with byte array, pack in Wrapper, serialize, send to server.

Anticol Messages

Anticol messages (defined in c2c.proto) contain the information from the anticollision loop of the NFC protocol. This includes UID, ATQA, SAK and Historical byte. The messages are populated with the information and sent using Data messages as described above. Is sent as soon as a client is connected to a new NFC Card.

NFCData Messages

NFCData messages (also defined in c2c.proto) contain actual NFC APDUs, encoded as byte strings. Additionally, they contain some information about the source of the APDU (i.e. is it a message from an NFC reader or a reply by the card), encoded in the data_source field.

Status Message

The Status message type (defined in c2c.proto) is used to convey information about the status of a client. The following types are defined:

  • KEEPALIVE_REQ: Keepalive message, used to test if the connection is still alive. MUST be answered with a KEEPALIVE_REP
  • KEEPALIVE_REP: Reply to KEEPALIVE_REQ
  • CARD_FOUND: Sent by a client that is in proximity to a Card. Will be followed by an Anticol message encoding information about the card.
  • CARD_REMOVED: Sent by a client if a previously detected card has left proximity
  • READER_FOUND: Sent if a client is in proximity to a reader
  • READER_REMOVED: Sent if the client leaves proximity to a previously discovered reader
  • NFC_NO_CONN: Sent in response to a NFCData message if the receiving client is not connected to any NFC device and thus cannot act upon the NFCData message.
  • INVALID_MSG_FMT: Sent if a client does not understand a received message. Could indicate that it is a legacy client that does not support new protocol messages, or that a message was corrupted.
  • NOT_IMPLEMENTED: Sent if a client understands a received message, but has not implemented the requested functionality. Mostly used during development if a function is only implemented as a stub, should not occur in production.
  • UNKNOWN_MESSAGE: Sent if a Wrapper message is received, but the encoded message cannot be understood. Similar to INVALID_MSG_FMT.
  • UNKNOWN_ERROR: Catchall status code if something went wrong, but the client is not quite sure what.

It is the responsibility of the clients to act upon these status messages in a sensible way.

About

Protocol specifications and protobuf files for the NFCGate project

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published