Verify API

Verify physical authenticity and uniqueness

Our Verify API gives your application or service the ability to verify not only the uniqueness and authenticity of physical components and products containing VivoKey secure microchips, but also the unique tap or scan event as a moment in time. Using strong standards based cryptographic proofs over NFC (contactless near field communication), successful verification results in a cryptographically signed authenticity JWT issued with relevant chip and tap / scan event information.

Table of Contents

API interface and keys

The VivoKey Verify API is designed to be open and extremely easy to use. You will need an API key to talk to Verify API, but the only requirement to get one is to submit your email address. An API key will be automatically generated and emailed to you instantly. The API key must be included in the HTTP header X-API-VIVOKEY for every call to an API endpoint. The Content-Type must be application/json and the JSON object data must be posted as raw data, not form encoded data, or your posted data will not be processed.

Interface URL, http headers, etc.

Base URL: https://auth.vivokey.com

Content-Type: application/json

X header: X-API-VIVOKEY

Method: HTTP POST

API key: Get one here!

Get your API key

Getting your own Verify API key is as simple as submitting your email address. The email address you use will be the basis for your developer account ID, and any API keys sent to this email address will all be registered to the same developer account ID. This becomes important when considering multiple applications which may use a Verify API key. The Developer ID is also included as a claim in the authenticity JWT returned by this API upon successful verification.

Now that you have your own API key, you would submit it with your calls to Verify API endpoints as an http header value. For example, when using CURL to interact with the API, you would prepare your parameters like so;

curl -H "Content-Type: application/json" -H "X-API-VIVOKEY: d28a293f-44ec-4f2a-bf56-6bd546f47e1f" -X POST https://auth.vivokey.com/challenge -d '{"scheme": 1}'

Error or failure modes are rather simple;


  • HTTP error code 400 (Bad Request) will be returned if there are syntactical problems with the data submitted.
  • HTTP error code 401 (Unauthorized) will be returned if you forget to include the X-API-VIVOKEY header or provide an invalid API key.
  • HTTP error code 406 (Not acceptable) will be returned from /session if there is a problem verifying authenticity of the chip
  • HTTP error code 418 (I'm a teapot) will be returned if requests to /session use expired tokens.

VivoKey authenticity token (JWT)

Once a successful cryptographic verification of VivoKey product is completed using the API, a signed VivoKey authenticity token is issued in JWT format. The typical format of a JWT is made up of three Base64-URL strings separated by dots that can be easily passed in HTML and HTTP environments, while being more compact when compared to XML-based standards such as SAML.

Example Json Web Token (JWT)

Various aspects of the authenticity token will be referenced in the API documentation below, so you might want to gain a cursory understanding of its structure and purpose before proceeding with the API documentation.

Two ways to verify

Depending on the product type, VivoKey offers two ways to verify authenticity and uniqueness. The easiest type of check is a signature verification, and the other more secure and thorough type of check is a challenge verification.

Signature verification

Pros:

  • Very easy to use
  • No mobile app required, just tap
  • Minimal web server side code

Cons:

  • Not as secure as challenge / response
  • No guarantee of scan time, only uniqueness
  • Not appropriate for high security applications

Challenge verification

Pros:

  • Very secure tap verification
  • Verified tap event time / date
  • Appropriate for high security applications

Cons:

  • More complex than signature verification
  • Requires mobile app on phones
  • Multiple API calls required for complete verification

Signature verification

There are some circumstances where performing challenge verification of a VivoKey secure NFC transponder might not be possible or preferred. For example, it would not be ideal to ask customers to have to install a special application in order to verify the authenticity of a retail product in a store. They should just be able to tap their phone to open website that shows the product is authentic. This is exactly the kind of scenario for which signature verification is ideal.

Signature verification is not supported by all VivoKey products. Here is a breakdown of support by product.

  • Spark 1 - Does not support signatures
  • Spark 2 - Supports signatures in read-only VivoKey URLs
  • AuthentiChip - Supports signatures in read-only customized URLs
  • Apex - NFC Sharing applet supports signatures in dynamically writable URLs

Signature verification flow

Signature verification works by embedding dynamic signature data within the URL programmed into a supported VivoKey microchip. The user simply taps a VivoKey enabled product with their phone and the native browser will open the website URL sent from the chip. This URL includes a dynamically generated signature value that the web server sends to the Verify API to validate the embedded signature is unique and authentic. If the signature is valid and the scan is unique, a signed authenticity JWT is issued.

API endpoint for signature verification

The base URL for the Verify API is <strong>https://auth.vivokey.com</strong> and there are only one relevant endpoint for signature validation. In order to verify a unique signature generated for each tap, submit the URL data that includes the signature to the /validate API endpoint.

/validate
The /validate endpoint is used to verify signatures generated for each unique read of supported VivoKey chips and products. Reading the signature is easily done with any NFC enabled smartphone. No special app is needed, just scanning the product will open the signed URL on the phone's native web browser. The web server can then pass signature data to the /validate endpoint for authenticity checking. Remember to ensure your posts have the HTTP header Content-Type set to application/json or your post data will not be processed.

The URL the browser has opened, which includes the signature data, can be easily detected by the web server and sent to the /validate endpoint to verify uniqueness and authenticity. The endpoint detects and verifies the signature value included in the URL data sent, then returns a JSON object. If the signature is valid and not expired (already used), a token value is also returned containing a signed authenticity JWT. The atp claim value within the JWT will be cmac to indicate the type of verification used.

KVPs sent to /validate

Key Description

signature

The signature data from the VivoKey product. See below ⤷ for examples.

KVPs received from /validate

Key Description
result

The result of the signature check. Possible values are success, expired, and invalid. The response value success means that the signature is valid and current, as in this signature is the most recent CMAC or highest counter value the server has validated from this product to date. A result of expired means that the signature contains a counter value that is not as recent as what has already been validated by the server. A result of invalid means there is a problem with the signature.

token

[conditional] The token value contains a signed JWT called an authenticity token. The token payload contains important verified data which can be used by your application or service, as well as passed between services as necessary. Each service or application which receives the authenticity token can validate its signature to verify authenticity of the token and its contents without ever having to call an API. The token KVP will only be supplied if the result key value is success.

Signature verification examples

Verify Spark 2 / AuthentiChip signatures
Spark 2 and AuthentiChip products have a unique URL that contains a signature which updates each time the chip is tapped by an NFC phone or reader device. The URL written to the Spark 2 begins with https://vivokey.co/ followed by a unique ID and then the signature. To verify the signature, submit the URL data (which includes the signature) to the /validate endpoint. For example, if you read the URL from the Spark 2 as https://vivokey.co/3c8d418757dcce5459f5b8576a5fb652/?sun=042468222F5C80-00003C-AE4F60A1AF728AB4 then you would submit the following to the /validate endpoint.
{“signature“: “vivokey.co/3c8d418757dcce5459f5b8576a5fb652/?sun=042468222F5C80-00003C-AE4F60A1AF728AB4”}
{result“: “success”,token“: “eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImprM3JXNEhBVm9HSzl6QVhsdnVzdnMxdTFLeVlMRUxYRG10OXlTOXViYUkifQ.eyJ0eXBlIjoyLCJwcm9kdWN0IjoyLCJhdHAiOiJjbWFjIiwic3ViIjoiZDYwOGFhZGYwYjI1MmE4N2EwYmEyMGQ3NTQ1NDM1N2Y3Y2Y5NDZlNWZjM2VjMmQ4YjViZmY4NGRhODg4Yzk5ZSIsImlhdCI6MTY4Nzg0MjAzMSwiZXhwIjoxNjg3ODQyMDYxLCJpc3MiOiJodHRwczovL2F1dGgudml2b2tleS5jb20iLCJqdGkiOiI2ODY0NTdjMjBmNjZkOWU1YWI3ZWQyMzMxYmYyOGMyNWE0ZjI4NGQ0MTAyZWFjMmE2YTUxNjc0NjdmMTcxYTI2In0.guSNm14-Nwf7nniN2Am9efCZCyW86ZXjC4F5Y08UzAVCqO4lbYTT5Wb6ip3g4Wf-OFCQNoe1G-E1NNZBlz-FeA”}
Expired signatures

If you send the same signature a subsequent time, which may occur if someone attempts to reload a webpage for example, it will return as expired. This means the signature is valid, but it is no longer considered unique.

{“signature“: “vivokey.co/3c8d418757dcce5459f5b8576a5fb652/?sun=042468222F5C80-00003C-AE4F60A1AF728AB4”}
{"result":"expired"}
CMAC Signatures with NFC Sharing for Apex

The NFC Sharing app for Apex may have (this feature is optional) the NFC Sharing applet installed, which supports signatures. The signature changes each time NFC data is read from the NFC Sharing applet. To verify the signature, submit the URL data which includes the signature data element to the /validate API endpoint. For example, if you program the NFC Sharing app with the following URL data;

https://testing.com/signature/?s={AES128_CMAC_SIGNATURE_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}

When scanning with a phone, a browser will open to the URL with the signature marker dynamically replaced with unique signature data;

https://testing.com/signature/?s=FF0A01128731FBB6-000005-E92AB4B81AAA1FEAB2C4D2AF4665AD19

You would then submit the signature data to the /validate endpoint like this;

{“signature“: “testing.com/signature/?s=FF0A01128731FBB6-000005-E92AB4B81AAA1FEAB2C4D2AF4665AD19”}

{result“: “success”,token“: “eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImprM3JXNEhBVm9HSzl6QVhsdnVzdnMxdTFLeVlMRUxYRG10OXlTOXViYUkifQ.eyJ0eXBlIjoyLCJwcm9kdWN0IjoyLCJhdHAiOiJjbWFjIiwic3ViIjoiZDYwOGFhZGYwYjI1MmE4N2EwYmEyMGQ3NTQ1NDM1N2Y3Y2Y5NDZlNWZjM2VjMmQ4YjViZmY4NGRhODg4Yzk5ZSIsImlhdCI6MTY4Nzg0MjAzMSwiZXhwIjoxNjg3ODQyMDYxLCJpc3MiOiJodHRwczovL2F1dGgudml2b2tleS5jb20iLCJqdGkiOiI2ODY0NTdjMjBmNjZkOWU1YWI3ZWQyMzMxYmYyOGMyNWE0ZjI4NGQ0MTAyZWFjMmE2YTUxNjc0NjdmMTcxYTI2In0.guSNm14-Nwf7nniN2Am9efCZCyW86ZXjC4F5Y08UzAVCqO4lbYTT5Wb6ip3g4Wf-OFCQNoe1G-E1NNZBlz-FeA”}
Expired signatures

If you send the same signature a subsequent time, which may occur if someone attempts to reload a webpage for example, it will return as expired. This means the signature is valid, but it is no longer considered unique.

{"result":"expired"}

Live signature verification test

Using the form below you can test signature verification using your API key and signature data.

Security caveats of signature verification

Signature verification is convenient, but there are security issues you should be aware of before deciding if signature based verification is the right choice for your application.

Signature stockpiling for later use

Powered by the NFC field emitted from a phone or reader, VivoKey secure NFC microchips do not have a way to keep track of the current time and date. This means all unique signatures must be generated using an incremental counter method. This opens up the possibility that someone could perform a series of reads in rapid succession to stockpile a large number of valid unique signatures and use them at a later time with our Verify API. The actual risk to your application's security depends entirely on your how your application is designed.

Signatures use a counter value incremented within the NFC chip itself, which cannot be rolled back. The Verify API will always look for a counter value embedded in the signature that is higher than the last counter value that was validated by the server. If an attacker performs a series of reads to gain a collection of signatures, and someone performs a legitimate read where the signature is validated, then all previous stockpiled signatures will be immediately invalidated.

Example stockpile attack scenario
  • An attacker reads 10 signatures with counter values 89103 to 89118, but does not open a web browser or verify the signatures. These signatures are stockpiled for future attacks.
  • A legitimate user taps and receives signature 89119. As part of the normal tap process, a web browser is opened on the phone and the application verifies the signature with the Verify API.
  • The Verify API verifies signature 89119 as valid and unique, and issues a secure signed JWT for this tap / scan event. Internally, the Verify API server has staked this specific secure NFC chip's counter at 89119.
  • The attacker attempts to use signature 89103, but receives "expired", and critically, does not receive a secure signed JWT. This means all of the signatures which were stockpiled earlier are no longer able to be used to receive a signed JWT.

If your application requires secure verification, or requires the date and time of the chip tap / scan event to be accurate, then use the challenge verification option. With challenge verification, the Verify API server generates a time stamped challenge which is integrated into the authenticity JWT issued for a verified legitimate tap or scan.

Challenge verification

The challenge verification process is a high security check which ensures the authenticity of the chip as well as the exact time it was tapped by a phone or read by a reader.

Challenge / response flow

The challenge / response verification method is the most secure way to verify authenticity of the VivoKey chip or technology as well as ensure the time of the tap or scan event is accurate. Once the VivoKey enabled product is tapped, a time stamped cryptographic challenge is obtained from the Verify API. That challenge is passed to the VivoKey product, and a secure cryptographic response is generated. This response is sent back to the Verify API for an authenticity check. If verified, a signed authenticity JWT is issued which includes all the relevant detail about the chip and the time it was tapped or scanned.

API endpoints for challenge verification

The base URL for the Verify API is https://auth.vivokey.com and there are only two relevant endpoints for the challenge flow; challenge and session. These endpoints consume and return JSON objects. Remember to ensure your posts have the HTTP header Content-Type set to application/json or your post data will not be processed.

/challenge

This endpoint consumes a JSON object that contains one or more key-value pairs (KVP), depending on which chip type is being challenged. The endpoint will return a JSON object that contains a payload and token KVP. Challenges expire after 30 seconds.

KVPs sent to /challenge

Key Description

scheme

The scheme value can be 1 or 2. The scheme informs the challenge generator which type of VivoKey chip is being challenged. Scheme 1 requires no other KVPs, and generates a basic challenge for Spark 1 chips. Scheme 2 requires both uid and message KVPs, and generates a challenge for Spark 2 chips as well as Apex chips running the Spark app.

uid

[conditional] The UID is the unique identifier or serial number of the chip being challenged. Certain VivoKey chips have a 7 byte UID while others have an 8 byte UID. Value is represented in hexadecimal notation. The uid KVP is not required for scheme 1 challenges. 

message

[conditional] For Spark 2 and Apex chips running the Spark app, the chip will generate a PCD challenge as part of the mutual authentication scheme used by the chip. This message is passed to the /challenge endpoint and an appropriate chip challenge is generated. Value is represented in hexadecimal notation. The message KVP is not required for scheme 1 challenges. 

KVPs received from /challenge

Key Description

payload

The payload value is the VivoKey authenticity challenge which must be passed to the chip for processing.

token

The token value is an encrypted JWT called a challenge token. The challenge token must be passed to /session along with the chip response for the associated payload to /session. Challenge tokens expire after 30 seconds.

/session

This endpoint consumes a JSON object that contains three KPVs. The endpoint will return a JSON object that contains a token KVP, the value of which is an authenticity JWT. The purpose of a signed token is to carry all of the valuable validated authenticity information inside itself, ensuring you can verify that a specific VivoKey chip scan was validated as authentic at a specific time, and has not been altered by any 3rd parties in transit. Authenticity tokens expire after 30 seconds and should not be used after it has expired (after exp). The endpoint is called session because the authenticity token is meant to be used to establish and maintain an authenticated session for VivoKey services, but you can also use it within your own services and applications.

KVPs sent to /session

Key Description

uid

The UID is the unique identifier or serial number of the chip being challenged. Certain VivoKey chips have a 7 byte UID while others have an 8 byte UID. Value is represented in hexadecimal notation.

response

For Spark 1, the response is a basic challenge response. For Spark 2 and Apex chips running the Spark app, the chip will generate a mutually authenticated response. The value is represented in hexadecimal notation.

token

The challenge token obtained from /challenge is sent back to /session.

cld

[optional] You have the option of passing any properly json encoded arbitrary data along with the chip response to /session (up to 1024 bytes). This data will be encoded into the authenticity token as a JWT claim called cld (client data). This allows you to embed application and user / client session relevant data to the chip scan event, making it part of the authenticity token. Passing the authenticity token to your backend or other services offers the same verifiable integrity protection to this data, meaning you can simply pass the token around your various external and internal endpoints with your application data rather than along side it.

KVPs received back from /session

Key Description
token

The token value contains a signed JWT called an authenticity token. The token payload contains important verified data which can be used by your application or service, as well as passed between services as necessary. Each service or application which receives the authenticity token can validate its signature to verify authenticity of the token and its contents without ever having to call an API.

Challenge verification examples

Sending data to a passive RFID transponder (all NFC transponders are RFID transponders, but not all RFID transponders comply with NFC standards) is done using commands over a particular "air interface" for the product in question. VivoKey products communicate using 13.56MHz RFID standards ISO14443A and ISO15693, and comply with NFC standards for Type 4 and Type 5.

Exactly how you send commands to VivoKey products over NFC depends on the operating environment. For example, using Swift for iOS to submit a TAM1 challenge to a Spark 1 means you would use the authenticate command. On Android you would use NfcV and transcieve. If you are using PC/SC to communicate, Spark 2 speaks APDUs natively while the Spark 1 may need commands sent over PC/SC as the payload of a standard "transparent" APDU. Response data would also be suffixed with standard APDU status words (SW1, SW2) with values of 90 00 which means "command successfully executed".

Spark 1

The Spark 1 is an ISO15693 RFID transponder which complies with NFC Type 5 (NfcV for Android, NFCTagTypeISO15693 for iOS). If you are communicating with the Spark 1 over PC/SC using a CCID compliant reader, you may need to send ISO15693 commands using an APDU compliant command format or command wrapper. Check your reader's documentation.

Authentication using TAM1 challenge response (tam)

For this example we will use a Spark 1 with an 8 byte UID of e0 04 01 18 00 9c 5d 52 (in MSB byte order). While not important for our API, it's useful to know the UID format of ISO15693 transponders is broken down into the following structure;

 

Byte 1: This is the Manufacturer code. It is assigned by the NFC Forum to each manufacturer. For example, Texas Instruments has the manufacturer code 0x01, NXP Semiconductors has 0x04, and so on.

 

Byte 2-3: These bytes represent the chip type. This is a unique code assigned by the manufacturer to each type of chip they produce.

 

Byte 4-8: These bytes represent the chip serial number. This is a unique number assigned by the manufacturer to each chip. It ensures that each chip produced by the manufacturer has a unique identifier.

STEP 1. Obtain a challenge from the /challenge endpoint

 

For Spark 1, send scheme value 1 to the API endpoint and receive a challenge payload and challenge token back.

 

{"scheme": 1}

 

{"payload":"3bc96bc85fa3405fe416","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..MhqTUKrdDjZSHSq9.EFn2Utl-hR1dTIwX_l9EREO4UPI6U2R1VtiFMcJfZUeSDiWBQCBan887f2cDKG0PRZWCwAL6gZim4dv02ruIyQZk3_OuMrMsmElEeDxkEzrOVOloLpiFL2wjug06fKcWXbhh5flImmRyuH5bbB_k8qWjD6HfDPiz8R0wFJ3gArx6-8sPbfLBF2UqIBIGBISMqerfz4nzsJY.f0f2S0RA5wGVUGfE2my8bg"}

STEP 2. Send authenticate command to Spark 1

The ISO15693 command represented in hexadecimal format below must be sent to the Spark 1 as an "addressed command", meaning you must include the UID of the transponder the command is intended for within the command structure itself, and the UID must be sent in LSB order. The challenge obtained from the server is appended to the end.

 

Command Code: 20 (authenticate)

Request Flag: 35

UID: 52 5d 9c 00 18 01 04 e0 (8 bytes of UID in LSB order)

CSI: 00 (AES)

RFU: 00

Key: 02

Challenge: 3b c9 6b c8 5f a3 40 5f e4 16

 

Complete command: 20 35 52 5d 9c 00 18 01 04 e0 00 00 02 3b c9 6b c8 5f a3 40 5f e4 16

 

How you send this command depends entirely on the operating environment. For example, using Swift for iOS means you use the authenticate command, which handles the command 20 and UID details for you. On Android you would use NfcV and transcieve. You should receive 12 bytes of data back which is the encrypted response data for the challenge you sent to the chip. If you are using PC/SC to communicate with the Spark 1, this data may be suffixed with APDU status words (SW1, SW2) with a success value of 90 00.

STEP 3. Submit the TAM1 response to the /session endpoint

 

{"uid":"e0040118009c5d52","response":"8d77260aea0fb3a68dc158aabbecb7a3","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..MhqTUKrdDjZSHSq9.EFn2Utl-hR1dTIwX_l9EREO4UPI6U2R1VtiFMcJfZUeSDiWBQCBan887f2cDKG0PRZWCwAL6gZim4dv02ruIyQZk3_OuMrMsmElEeDxkEzrOVOloLpiFL2wjug06fKcWXbhh5flImmRyuH5bbB_k8qWjD6HfDPiz8R0wFJ3gArx6-8sPbfLBF2UqIBIGBISMqerfz4nzsJY.f0f2S0RA5wGVUGfE2my8bg"}

 

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImprM3JXNEhBVm9HSzl6QVhsdnVzdnMxdTFLeVlMRUxYRG10OXlTOXViYUkifQ.eyJ0eXBlIjoxLCJwcm9kdWN0IjozLCJhdHAiOiJ0YW0iLCJzdWIiOiJlOGU5OGQ0MTVmM2I5NzI2MTE1ZjRiYjQ1NTNkZThmYTgxZTM3MjY5ZDUxMDkxNDgzMzUxMDYyMTMwOGFjN2IwIiwiaWF0IjoxNjg4NTgyNjkxLCJleHAiOjE2ODg1ODI3MjEsImlzcyI6Imh0dHBzOi8vYXV0aC52aXZva2V5LmNvbSIsImp0aSI6ImIzMTM5NmRmYzA0MGJjZmRmOTI4MGYwY2JlZDVjMWE2Zjk2NDUzZmZlM2VlMDljNWU0NTNmM2JkODIwMjM0ZjkifQ.Ep6PL6ySPagUHVeq-DSmASmui2VwJhaBk9ifSpIqTMQ_mlGrrAVxnCApjSC9jqQV2h5YGCISNpQN8ELWh0-YtQ"}

 

Inspect authenticity JWT with jwt.io (includes public key to validate signature)

Spark 2 / AuthentiChip

Spark 2 and AuthentiChip products are based on ISO14443A RFID transponders which comply with NFC Type 4 (NfcA for Android, NFCTagTypeISO7816Compatible for iOS). They use a traditional APDU compliant command format so communicating with a CCID reader over PS/SC should be straightforward.

Authentication using mutual authentication (mau)

For this example, we will use a Spark 2 chip with a 7 byte UID of 04 29 38 22 2f 5c 80. The Spark 2 mutual authentication functions are tied to the standard NFC NDEF application ID (AID) for NFC Forum Type 4 transponders (D2 76 00 00 85 01 01 00), so we must start by selecting this AID using the standard select APDU command.

STEP 1. Select the NDEF AID

CLA: 00

INS: A4

P1: 04

P2: 0C

Lc: 07

Data: D2 76 00 00 85 01 01

Le: 00

Complete APDU: 00 A4 04 0C 07 D2 76 00 00 85 01 01 00

Receive: 90 00

You should receive no payload response, only status words SW1, SW1 with values of 90 00 which means "success".

STEP 2. Request PCD challenge

The Spark 2 begins mutual authentication by issuing a PCD challenge - a challenge issued to the Verify API to prove VivoKey has the same key the chip does. This must be sent to the /challenge endpoint to obtain a PICC challenge - a challenge to the Spark 2 to prove it has the same key VivoKey does.

CLA: 90

INS: 71

P1: 00

P2: 00

Lc: 02

Key: 02

Data: 00

Le: 00

Complete APDU: 90 71 00 00 02 02 00 00

Receive: 87 86 b1 2d 5e e7 14 de 5a 97 c5 c0 98 00 b6 68 91 af

The data received back should contain a 16 byte PCD challenge followed by two status words (SW1, SW2) with the values 91 AF which means "additional frame" expected. The Spark 2 is now expecting the next thing sent to it be a response to the PCD challenge along with a PICC challenge from the API.

STEP 3. Obtain a challenge from /challenge endpoint

For Spark 2, post a JSON object with a scheme value or 2, a message value of the 16 byte PCD challenge, and the UID of the Spark 2 to /challenge

{"scheme": 2, "message":"8786b12d5ee714de5a97c5c09800b668", "uid":"042938222f5c80"}

You will receive a payload that contains the combined PCD response and PICC challenge, and an encrypted challenge token.

{"payload":"cebee779170430704d6f9b28e5a2cad5eff20ce410233b19e4a9ae64e030c10f","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..7E30dAWbIIDnmTAK.N6EIEvT2VshQZd30z7S1IjKavR5IvHHOVxnrYDaEnMxX1rhghXHcxbdjnHIr2sTaAaGJfq5s_6gosLnbrAMJmmZloBAcsPuuUztp6sket9s8ZijXy7npChU9XrlTW-0RMGNx2l5X8BMVL4yAP5varLD7lMMnFPHqrJ6XvNx4N0ehW4pTRg_VqpJ08HeTbgR42zjFV8V_yIUQP28Ia7SCDmIn6ot-5Rl0.mmnJMa-1iv37SHXI_sOtEw"}

STEP 4. Send PCD response and PICC challenge

The Spark 2 now wants a 16 byte response along with a 16 byte PICC challenge it will respond to.

CLA: 90

INS: AF

P1: 00

P2: 00

Lc: 20

Data: ce be e7 79 17 04 30 70 4d 6f 9b 28 e5 a2 ca d5 ef f2 0c e4 10 23 3b 19 e4 a9 ae 64 e0 30 c1 0f

Le: 00

Complete APDU: 90 AF 00 00 20 ce be e7 79 17 04 30 70 4d 6f 9b 28 e5 a2 ca d5 ef f2 0c e4 10 23 3b 19 e4 a9 ae 64 e0 30 c1 0f 00

Receive: b6 0b 6b 38 ad 5c 8e d6 f0 b4 27 85 90 ce 9f 63 85 62 92 47 05 c7 c6 3f 89 d5 3d 67 4c 99 04 19 91 00

The data received should contain a 32 byte response followed by two status words (SW1, SW2) with the values 91 00 which means "ok".

STEP 5. Submit the mutual auth response to the /session endpoint

The final step is to post the chip's UID, message response, and the original challenge token obtained in Step 2 from the /challenge endpoint to the /session endpoint.

{"uid":"042938222f5c80","response":"b60b6b38ad5c8ed6f0b4278590ce9f638562924705c7c63f89d53d674c990419","cld":"{\u0022data\u0022:\u0022testing\u0022}","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..7E30dAWbIIDnmTAK.N6EIEvT2VshQZd30z7S1IjKavR5IvHHOVxnrYDaEnMxX1rhghXHcxbdjnHIr2sTaAaGJfq5s_6gosLnbrAMJmmZloBAcsPuuUztp6sket9s8ZijXy7npChU9XrlTW-0RMGNx2l5X8BMVL4yAP5varLD7lMMnFPHqrJ6XvNx4N0ehW4pTRg_VqpJ08HeTbgR42zjFV8V_yIUQP28Ia7SCDmIn6ot-5Rl0.mmnJMa-1iv37SHXI_sOtEw"}

If successfully validated, you will receive a signed authenticity JWT in return which contains all the relevant information about the verified chip and the verification event.

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImprM3JXNEhBVm9HSzl6QVhsdnVzdnMxdTFLeVlMRUxYRG10OXlTOXViYUkifQ.eyJ0eXBlIjoyLCJwcm9kdWN0IjoyLCJhdHAiOiJtYXUiLCJjbGQiOiJ7XCJkYXRhXCI6XCJ0ZXN0aW5nXCJ9Iiwic3ViIjoiNjQ4ZmU2NThkYTdjNDg3NzkxZTg4ODc3NTE1M2Q3NjRmZWNlMzJmYjNkMTg5NWU1N2YxZDNjMzY5ZGFkYTk1MyIsImlhdCI6MTY4ODU4MzM4NSwiZXhwIjoxNjg4NTgzNDE1LCJpc3MiOiJodHRwczovL2F1dGgudml2b2tleS5jb20iLCJqdGkiOiIwZTgyYzFlZTllN2I4M2Q0Mzg1NWU2YjM4OGYxZmZiODI3OTE2MzQxNWJiNWU2NTMzODU4MDc4ZGY5YjM4ZTVhIn0.7xNJ6fSjf_CuawElJMZvnwvDF5VwBr6j4Wac2r2F7R0BoNydCXeQ72MUteZdwHDE6Z5_roh1sBrY-zs9pmQ-UA"}

Spark application for Apex

The Spark applet for Apex enables the Verify API to perform a challenge verification with the Apex. The Spark applet uses traditional APDU compliant command format so communicating with a CCID reader over PS/SC should be straightforward.

Mutual authentication using Spark applet on Apex

The Spark applet on Apex does not use the chip UID. Instead, the Spark applet is programmed with an 8 byte "Spark ID" that is returned as a result of selecting the Spark application. In this example, we will use ff 00 01 8d 2c 10 16 79 as the Spark ID. Start by selecting the Spark applet AID A000000846737061726B3201 using the standard select APDU command.

STEP 1. Select the Spark AID

CLA: 00

INS: A4

P1: 04

P2: 00

Lc: 0C

Data: A0 00 00 08 46 73 70 61 72 6B 32 01

Le: 00

Complete APDU: 00 A4 04 00 0C A0 00 00 08 46 73 70 61 72 6B 32 01 00

Receive: ff 0a 01 8d 2c 10 16 79 90 00

For this example, we will receive a payload response containing an 8 byte Spark ID (ff0a018d2c101679) followed by status words SW1, SW1 with values of 90 00 which means "success". You will need to use the Spark ID instead of the Apex chip's UID when interacting with the Verify API.

STEP 2. Request PCD challenge

The Spark applet begins mutual authentication by issuing a PCD challenge - a challenge to the API to prove we know the same key it does. This must be sent to the /challenge endpoint to obtain a PICC challenge - a challenge to the Spark applet to prove it knows the same key we do.

CLA: 90

INS: 71

P1: 00

P2: 00

Lc: 02

Key: 02

Data: 00

Le: 00

Complete APDU: 90 71 00 00 02 02 00 00

Receive: 87 86 b1 2d 5e e7 14 de 5a 97 c5 c0 98 00 b6 68 91 af

The data received back should contain a 16 byte PCD challenge followed by two status words (SW1, SW2) with the values 91 AF which means "additional frame" expected. This PCD challenge is what is sent to the /challenge endpoint to obtain a PICC challenge along with a PCD response. The Spark applet is now expecting the next thing sent to be a response to the PCD challenge along with a PICC challenge from the Verify API.

STEP 3. Obtain a challenge from /challenge endpoint

To obtain a challenge, post a JSON object with a scheme value or 2, a message value of the 16 byte PCD challenge, and the Spark ID (not the UID of the Apex) to /challenge.

{"scheme": 2, "message":"8786b12d5ee714de5a97c5c09800b668", "uid":"ff0a018d2c101679"}

You will receive a payload that contains the combined PCD response and PICC challenge, and an encrypted challenge token.

{"payload":"cebee779170430704d6f9b28e5a2cad5eff20ce410233b19e4a9ae64e030c10f","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..7E30dAWbIIDnmTAK.N6EIEvT2VshQZd30z7S1IjKavR5IvHHOVxnrYDaEnMxX1rhghXHcxbdjnHIr2sTaAaGJfq5s_6gosLnbrAMJmmZloBAcsPuuUztp6sket9s8ZijXy7npChU9XrlTW-0RMGNx2l5X8BMVL4yAP5varLD7lMMnFPHqrJ6XvNx4N0ehW4pTRg_VqpJ08HeTbgR42zjFV8V_yIUQP28Ia7SCDmIn6ot-5Rl0.mmnJMa-1iv37SHXI_sOtEw"}

STEP 4. Send PCD response and PICC challenge

The Spark applet is waiting for a 16 byte PCD response along with a 16 byte PICC challenge it will respond to.

CLA: 90

INS: AF

P1: 00

P2: 00

Lc: 20

Data: ce be e7 79 17 04 30 70 4d 6f 9b 28 e5 a2 ca d5 ef f2 0c e4 10 23 3b 19 e4 a9 ae 64 e0 30 c1 0f

Le: 00

Complete APDU: 90 AF 00 00 20 ce be e7 79 17 04 30 70 4d 6f 9b 28 e5 a2 ca d5 ef f2 0c e4 10 23 3b 19 e4 a9 ae 64 e0 30 c1 0f 00

Receive: b6 0b 6b 38 ad 5c 8e d6 f0 b4 27 85 90 ce 9f 63 85 62 92 47 05 c7 c6 3f 89 d5 3d 67 4c 99 04 19 91 00

The data received back should contain a 32 byte response followed by two status words (SW1, SW2) with the values 91 00 which means "ok".

STEP 5. Submit the mutual auth response to the /session endpoint

The final step is to post the Spark ID, message response, and the original challenge token obtained in Step 2 from the /challenge endpoint to the /session endpoint.

{"uid":"ff0a018d2c101679","response":"b60b6b38ad5c8ed6f0b4278590ce9f638562924705c7c63f89d53d674c990419","cld":"{\u0022data\u0022:\u0022testing\u0022}","token":"eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..7E30dAWbIIDnmTAK.N6EIEvT2VshQZd30z7S1IjKavR5IvHHOVxnrYDaEnMxX1rhghXHcxbdjnHIr2sTaAaGJfq5s_6gosLnbrAMJmmZloBAcsPuuUztp6sket9s8ZijXy7npChU9XrlTW-0RMGNx2l5X8BMVL4yAP5varLD7lMMnFPHqrJ6XvNx4N0ehW4pTRg_VqpJ08HeTbgR42zjFV8V_yIUQP28Ia7SCDmIn6ot-5Rl0.mmnJMa-1iv37SHXI_sOtEw"}

If successfully validated, you will receive a signed authenticity JWT in return which contains all the relevant information about the verified chip and the verification event.

{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6ImprM3JXNEhBVm9HSzl6QVhsdnVzdnMxdTFLeVlMRUxYRG10OXlTOXViYUkifQ.eyJ0eXBlIjoyLCJwcm9kdWN0IjoyLCJhdHAiOiJtYXUiLCJjbGQiOiJ7XCJkYXRhXCI6XCJ0ZXN0aW5nXCJ9Iiwic3ViIjoiNjQ4ZmU2NThkYTdjNDg3NzkxZTg4ODc3NTE1M2Q3NjRmZWNlMzJmYjNkMTg5NWU1N2YxZDNjMzY5ZGFkYTk1MyIsImlhdCI6MTY4ODU4MzM4NSwiZXhwIjoxNjg4NTgzNDE1LCJpc3MiOiJodHRwczovL2F1dGgudml2b2tleS5jb20iLCJqdGkiOiIwZTgyYzFlZTllN2I4M2Q0Mzg1NWU2YjM4OGYxZmZiODI3OTE2MzQxNWJiNWU2NTMzODU4MDc4ZGY5YjM4ZTVhIn0.7xNJ6fSjf_CuawElJMZvnwvDF5VwBr6j4Wac2r2F7R0BoNydCXeQ72MUteZdwHDE6Z5_roh1sBrY-zs9pmQ-UA"}

Appendix

Definitions

APDU = Application Protocol Data Unit. This is a specific schema which specifies the organization, security, and commands for interchange between the reader and ISO/IEC 7816-4 compliant chips. Wikipedia

PCD = Proximity Coupling Device. This is the RFID or NFC reader, also called an "interrogator". It is the device which provides the magnetic field for an RFID or NFC contactless card to induct power from and communicate over is called the PCD. The use of the term "proximity" is unfortunate because that term has been adopted by certain standards like ISO14443 as "proximity technologies", while the ISO15693 standard refers to itself as a "vicinity technology". However, the term VCD is not a widely used term, so for all intents and purposes, both "proximity" and "vicinity" technology standards refer to the reader device as a "PCD".

PICC = Proximity Integrated Circuit Card. This is the RFID chip, card, fob, etc. which the PCD powers and communicates with. The term "proximity"  here specifically means a set of "proximity technologies" which includes ISO14443 standard contactless cards.

VICC = Vicinity Integrated Circuit Card. This is the RFID chip, card, fob, etc. which the PCD powers and communicates with. The term "proximity"  here specifically means a set of "proximity technologies" which includes ISO15693 standard contactless cards.

SW1 , SW2 = Status Word 1, Status Word 2. When sending an APDU from a PCD to a PICC, responses may or may not contain response data, but they will almost always contain status words 1 and 2. These are codes which report the resulting status of the APDU which was sent. Status words of 90 and 00 together mean "Success", for example. These bytes will appear after any data which may have been returned from the card in response to the APDU request.

APDU Response Codes (SW1, SW2)

When working with PC/SC readers, APDU messages are used to exchange data between the application and both the transponder on the reader and the reader itself. Responses returned generally will contain a two "status word" byte suffix which gives clues as to the result of your request.

SW1SW2(Info/Warning/Error/Security) Description
6(E) Class not supported.
61(I) Response bytes still available
61XX(I) Command successfully executed; ‘XX’ bytes of data are available and can be requested using GET RESPONSE.
62(W) State of non-volatile memory unchanged
6200(W) No information given (NV-Ram not changed)
6201(W) NV-Ram not changed 1.
6281(W) Part of returned data may be corrupted
6282(W) End of file/record reached before reading Le bytes
6283(W) Selected file invalidated
6284(W) Selected file is not valid. FCI not formated according to ISO
6285(W) No input data available from a sensor on the card. No Purse Engine enslaved for R3bc
62A2(W) Wrong R-MAC
62A4(W) Card locked (during reset( ))
62CX(W) Counter with value x (command dependent)
62F1(W) Wrong C-MAC
62F3(W) Internal reset
62F5(W) Default agent locked
62F7(W) Cardholder locked
62F8(W) Basement is current agent
62F9(W) CALC Key Set not unblocked
62FX(W) –
62XX(W) RFU
63(W) State of non-volatile memory changed
6300(W) No information given (NV-Ram changed)
6381(W) File filled up by the last write. Loading/updating is not allowed.
6382(W) Card key not supported.
6383(W) Reader key not supported.
6384(W) Plaintext transmission not supported.
6385(W) Secured transmission not supported.
6386(W) Volatile memory is not available.
6387(W) Non-volatile memory is not available.
6388(W) Key number not valid.
6389(W) Key length is not correct.
63C0(W) Verify fail, no try left.
63C1(W) Verify fail, 1 try left.
63C2(W) Verify fail, 2 tries left.
63C3(W) Verify fail, 3 tries left.
63CX(W) The counter has reached the value ‘x’ (0 = x = 15) (command dependent).
63F1(W) More data expected.
63F2(W) More data expected and proactive command pending.
63FX(W) –
63XX(W) RFU
64(E) State of non-volatile memory unchanged
6400(E) No information given (NV-Ram not changed)
6401(E) Command timeout. Immediate response required by the card.
64XX(E) RFU
65(E) State of non-volatile memory changed
6500(E) No information given
6501(E) Write error. Memory failure. There have been problems in writing or reading the EEPROM. Other hardware problems may also bring this error.
6581(E) Memory failure
65FX(E) –
65XX(E) RFU
66(S)
6600(S) Error while receiving (timeout)
6601(S) Error while receiving (character parity error)
6602(S) Wrong checksum
6603(S) The current DF file without FCI
6604(S) No SF or KF under the current DF
6669(S) Incorrect Encryption/Decryption Padding
66XX(S) –
67(E)
6700(E) Wrong length
67XX(E) length incorrect (procedure)(ISO 7816-3)
68(E) Functions in CLA not supported
6800(E) No information given (The request function is not supported by the card)
6881(E) Logical channel not supported
6882(E) Secure messaging not supported
6883(E) Last command of the chain expected
6884(E) Command chaining not supported
68FX(E) –
68XX(E) RFU
69(E) Command not allowed
6900(E) No information given (Command not allowed)
6901(E) Command not accepted (inactive state)
6981(E) Command incompatible with file structure
6982(E) Security condition not satisfied.
6983(E) Authentication method blocked
6984(E) Referenced data reversibly blocked (invalidated)
6985(E) Conditions of use not satisfied.
6986(E) Command not allowed (no current EF)
6987(E) Expected secure messaging (SM) object missing
6988(E) Incorrect secure messaging (SM) data object
698DReserved
6996(E) Data must be updated again
69E1(E) POL1 of the currently Enabled Profile prevents this action.
69F0(E) Permission Denied
69F1(E) Permission Denied – Missing Privilege
69FX(E) –
69XX(E) RFU
6A(E) Wrong parameter(s) P1-P2
6A00(E) No information given (Bytes P1 and/or P2 are incorrect)
6A80(E) The parameters in the data field are incorrect.
6A81(E) Function not supported
6A82(E) File not found
6A83(E) Record not found
6A84(E) There is insufficient memory space in record or file
6A85(E) Lc inconsistent with TLV structure
6A86(E) Incorrect P1 or P2 parameter.
6A87(E) Lc inconsistent with P1-P2
6A88(E) Referenced data not found
6A89(E) File already exists
6A8A(E) DF name already exists.
6AF0(E) Wrong parameter value
6AFX(E) –
6AXX(E) RFU
6B(E)
6B00(E) Wrong parameter(s) P1-P2
6BXX(E) Reference incorrect (procedure byte), (ISO 7816-3)
6C(E) Wrong length Le
6C00(E) Incorrect P3 length.
6CXX(E) Bad length value in Le; ‘xx’ is the correct exact Le
6D(E)
6D00(E) Instruction code not supported or invalid
6DXX(E) Instruction code not programmed or invalid (procedure byte), (ISO 7816-3)
6E(E)
6E00(E) Class not supported
6EXX(E) Instruction class not supported (procedure byte), (ISO 7816-3)
6F(E) Internal exception
6F00(E) Command aborted – more exact diagnosis not possible (e.g., operating system error).
6FFF(E) Card dead (overuse, …)
6FXX(E) No precise diagnosis (procedure byte), (ISO 7816-3)
9-
9000(I) Command successfully executed (OK).
9004(W) PIN not succesfully verified, 3 or more PIN tries left
9008Key/file not found
9080(W) Unblock Try Counter has reached zero
9100OK
9101States.activity, States.lock Status or States.lockable has wrong value
9102Transaction number reached its limit
910CNo changes
910EInsufficient NV-Memory to complete command
911CCommand code not supported
911ECRC or MAC does not match data
9140Invalid key number specified
917ELength of command string invalid
919DNot allow the requested command
919EValue of the parameter invalid
91A0Requested AID not present on PICC
91A1Unrecoverable error within application
91AEAuthentication status does not allow the requested command
91AFAdditional data frame is expected to be sent
91BEOut of boundary
91C1Unrecoverable error within PICC
91CAPrevious Command was not fully completed
91CDPICC was disabled by an unrecoverable error
91CENumber of Applications limited to 28
91DEFile or application already exists
91EECould not complete NV-write operation due to loss of power
91F0Specified file number does not exist
91F1Unrecoverable error within file
920x(I) Writing to EEPROM successful after ‘x’ attempts.
9210(E) Insufficient memory. No more storage available.
9240(E) Writing to EEPROM not successful.
9301Integrity error
9302Candidate S2 invalid
9303(E) Application is permanently locked
9400(E) No EF selected.
9401Candidate currency code does not match purse currency
9402Candidate amount too high
9402(E) Address range exceeded.
9403Candidate amount too low
9404(E) FID not found, record not found or comparison pattern not found.
9405Problems in the data field
9406(E) Required MAC unavailable
9407Bad currency : purse engine has no slot with R3bc currency
9408R3bc currency not supported in purse engine
9408(E) Selected file type does not match command.
9580Bad sequence
9681Slave not found
9700PIN blocked and Unblock Try Counter is 1 or 2
9702Main keys are blocked
9704PIN not succesfully verified, 3 or more PIN tries left
9784Base key
9785Limit exceeded – C-MAC key
9786SM error – Limit exceeded – R-MAC key
9787Limit exceeded – sequence counter
9788Limit exceeded – R-MAC length
9789Service not available
9802(E) No PIN defined.
9804(E) Access conditions not satisfied, authentication failed.
9835(E) ASK RANDOM or GIVE RANDOM not executed.
9840(E) PIN verification not successful.
9850(E) INCREASE or DECREASE could not be executed because a limit has been reached.
9862(E) Authentication Error, application specific (incorrect MAC)
99001 PIN try left
9904PIN not succesfully verified, 1 PIN try left
9985Wrong status – Cardholder lock
9986(E) Missing privilege
9987PIN is not installed
9988Wrong status – R-MAC state
9A002 PIN try left
9A04PIN not succesfully verified, 2 PIN try left
9A71Wrong parameter value – Double agent AID
9A72Wrong parameter value – Double agent Type
9D05(E) Incorrect certificate type
9D07(E) Incorrect session data size
9D08(E) Incorrect DIR file record size
9D09(E) Incorrect FCI record size
9D0A(E) Incorrect code size
9D10(E) Insufficient memory to load application
9D11(E) Invalid AID
9D12(E) Duplicate AID
9D13(E) Application previously loaded
9D14(E) Application history list full
9D15(E) Application not open
9D17(E) Invalid offset
9D18(E) Application already loaded
9D19(E) Invalid certificate
9D1A(E) Invalid signature
9D1B(E) Invalid KTU
9D1D(E) MSM controls not set
9D1E(E) Application signature does not exist
9D1F(E) KTU does not exist
9D20(E) Application not loaded
9D21(E) Invalid Open command data length
9D30(E) Check data parameter is incorrect (invalid start address)
9D31(E) Check data parameter is incorrect (invalid length)
9D32(E) Check data parameter is incorrect (illegal memory check area)
9D40(E) Invalid MSM Controls ciphertext
9D41(E) MSM controls already set
9D42(E) Set MSM Controls data length less than 2 bytes
9D43(E) Invalid MSM Controls data length
9D44(E) Excess MSM Controls ciphertext
9D45(E) Verification of MSM Controls data failed
9D50(E) Invalid MCD Issuer production ID
9D51(E) Invalid MCD Issuer ID
9D52(E) Invalid set MSM controls data date
9D53(E) Invalid MCD number
9D54(E) Reserved field error
9D55(E) Reserved field error
9D56(E) Reserved field error
9D57(E) Reserved field error
9D60(E) MAC verification failed
9D61(E) Maximum number of unblocks reached
9D62(E) Card was not blocked
9D63(E) Crypto functions not available
9D64(E) No application loaded
9E00PIN not installed
9E04PIN not succesfully verified, PIN not installed
9F00PIN blocked and Unblock Try Counter is 3
9F04PIN not succesfully verified, PIN blocked and Unblock Try Counter is 3
9FXXCommand successfully executed; ‘xx’ bytes of data are available and can be requested using GET RESPONSE.
9xXXApplication related status, (ISO 7816-3)