NDEF, or NFC Data Exchange Format, is the communication protocol used by NFC (Near-Field Communication) devices. NFC-compatible YubiKeys contain an integrated NFC antenna, which allows them to wirelessly communicate with NFC readers via the NDEF protocol when both devices are within a few centimeters of each other.
NFC readers generate an electromagnetic field, which is capable of powering the YubiKey and transferring data between the devices. This allows the YubiKey to generate and submit Yubico OTPs and OATH HOTPs without being physically connected to a host.
Tapping an NFC reader with a YubiKey is similar to touching the key when it's plugged into a host over USB/Lightning; the action triggers whichever operation the activated OTP application slot is configured with. Unlike USB/Lightning connections, which provide the option to activate either slot based on duration of touch, only one slot may be activated over NFC.
NFC-compatible YubiKeys contain an NDEF tag that can be configured to point to one of the slots. When the YubiKey is scanned by an NFC reader, the slot that is pointed to by the NDEF tag will activate. By default, YubiKeys come with the NDEF tag pointing to slot 1 (the short press slot), which is preconfigured with Yubico OTP (and registered with YubiCloud).
Authenticating over NFC with a YubiKey
Authenticating with a YubiKey over NFC involves the following major steps:
The YubiKey is placed on (or very close to) an NFC reader that is connected to a host device.
The electromagnetic field generated by the reader powers the YubiKey and triggers the generation of an OTP.
The YubiKey translates the binary OTP ciphertext to a UTF-encoded string representation of the OTP's ModHex characters. For example, the 4-bit binary string "0000" represents the ModHex character "c". In UTF-8, the letter "c" is represented by the 8-bit binary string "0110 0011".
The OTP (as part of a text string or URI in an NDEF message) is transmitted through the YubiKey's integrated NFC antenna to the host device via the NFC reader's electromagnetic field.
The application on the host device receives and decodes the NDEF message and validates the OTP.
Like HID, the USB communication protocol, NDEF uses byte arrays to communicate data between device and host. These byte arrays, referred to as NDEF "messages", contain one or more records. Each NDEF record includes a header and a payload. The payload contains data (the OTP or HOTP in this case), and the header describes how to interpret that data.
The NDEF record header includes the following fields:
- Message flags, including the Type Name Format (TNF): 1 byte.
- Type length: 1 byte.
- Payload length: 1-4 bytes.
- ID length: 1 byte.
- Payload type: variable.
- Payload ID: variable.
The header fields that are most important to the YubiKey are the TNF and payload type. The YubiKey sends OTPs and HOTPs over NFC as part of text strings or URIs, which are pre-defined by the NDEF Record Type Definition (RTD) specifications. To send this type of data, the TNF has to be set to 0x01 (Well-Known). From there, the payload type can be set to "T" for text strings or "U" for URIs.
To send a text payload, the payload field must include a status byte followed by the text, with each character represented by their UTF code (in byte form). The status byte specifies the UTF encoding type (0 for UTF-8 or 1 for UTF-16) and the length of the language code (the encoding type and language type tell the receiving party how to interpret the text bytes correctly). The text field begins with the language code (e.g. en-US for United States English) followed by the actual payload (the OTP or HOTP).
The YubiKey uses UTF-8 encoding by default, but the encoding type can be changed to UTF-16 via the API's UseUtf16Encoding() method if you wish. Similarly, the key uses the en-US language code by default, but this can also be changed with the WithLanguage() method.
To send a URI payload, the payload field must include a URI ID code (1 byte) followed by the rest of the URI as a UTF-8 byte string (receiving parties expect URIs to be in UTF-8 format only). The ID code is used to represent commonly used addresses in order to reduce the size of the URI record. For example, "https://www." is represented by the ID code of 0x02. When the NDEF tag is configured with a URI, the SDK will make a best effort at identifying the prefix and automatically setting the right value. You may not explicitly set the URI ID code via the API.
Slot configuration compatibility
Only Yubico OTPs and OATH HOTPs can be communicated successfully over NDEF.
You can still point the NDEF tag to a slot that is configured with a static password or challenge-response, but the YubiKey will not be able to communicate these configurations properly using the NDEF protocol.
For static passwords, the YubiKey will send the static text or URI followed by the HID usage IDs of the password characters. HID usage IDs cannot be interpreted correctly over NFC since the host expects to receive UTF characters. For example, the letter "a" is represented by the binary string "0000 0100" in HID and by the binary string "0110 0001" in UTF-8/ASCII. These binary code differences, along with the fact that upper case letters are signaled through the modifier key state in HID usage reports, mean that a password in HID form cannot be easily translated to UTF form. The YubiKey firmware does not have this translation capability, and the SDK does not include the functionality to configure the key with both the HID and UTF representations of a static password during configuration.
For challenge-response, the YubiKey will send the static text or URI with nothing after. (The challenge itself cannot be sent via NDEF.)
.NET SDK NDEF functionality
The SDK provides the following major methods related to NDEF:
The ConfigureNdef() method allows you to point the NDEF tag to one of the two OTP application slots. When the YubiKey is scanned by an NFC reader, the slot that is pointed to by the NDEF tag will activate, causing the generation of whichever password type was configured for that slot.
AsText() and AsUri() essentially allow you to toggle the payload type of the NDEF record to "T" or "U". They also provide the option to prepend text or an address to the OTP, which all becomes part of the NDEF record payload. With AsUri(), the intended usage is to prepend the address for an OTP validation server, such as YubiCloud.
For an example of how to use these methods, please see the ConfigureNdef() how-to guide.
The ReadNdefTag() method allows an application to receive the OTP payload that is generated when a YubiKey is scanned by a connected NFC reader. It is left to the developer to handle the payload and validate the OTP appropriately.
For an example of how to use this method, please see the ReadNdefTag() how-to guide.