Digital Interface Payload
Introduction
This document describes the format of EML's Digital Interface Payload (DIP). It is an encrypted payload which is used to convey card security details for a specific account. It is used in conjunction with the provisioning of virtual/digital card products.
The payload contains all of the details necessary to successfully complete a card not present (CNP) transaction with that account. Namely, it contains:
- Card Number (PAN)
- Expiry Date
- Security Code (CVV2)
Accessing these details involves a simple, on-line, cryptographic exchange between EML and your application. The following sections describe the procedure for this exchange, and the steps required to decrypt and decode the payload.
Source APIs
EML has two different APIs which provide access to the DIP exchange. Both of these APIs accept the same parameters and return the DIP payload in the same format.
- EML Web Services 2.0 (SOAP) API
- See
GetAccountDipRequest
in the Web Services 2.0 documentation. - Normal username/password authentication applies to this request.
- See
- EML Web Services 3.0 (REST) API
- See
POST /accounts/{id}/dip
in the Web Services 3.0 documentation. - Normal OAuth token authentication applies to this request.
- See
Identifiers and Keys
EML has a registry of mobile/web applications which use DIP integration. Before you can complete a DIP exchange, you need to register your application with EML. We will provide you with a UUID for your application, which we refer to as the ApplicationId
.
Each application has one or more application keys. Each key is comprised of a 256-bit pre-shared symmetric key, which we refer to as ApplicationKeySecret
. This secret is never transmitted over an EML API. Each key also has a UUID, ApplicationKeyId
, used to refer to that specific key without needing to disclose its secret.
Key rotation
We expect that DIP decryption will happen as close to the user as is practical. For example, for a mobile application, the application key might be packaged into the application binaries deployed to the user's device.
Having multiple keys per application allows you to rotate keys on a regular basis while providing a grace period for older keys. For this to function correctly, your ApplicationKeyId
must be unique for each new key.
Requesting a DIP
The following diagram illustrates the sequence of events for the requesting and decrypting the DIP.
EmphemeralKey
Generating an For each request for a DIP exchange, you should generate a random, single-use, 256-bit ephemeral key. Where available, you should utilise secure sources of entropy to generate your key.
Programming Resources
- Web/JavaScript: crypto.getRandomValues
- Node.js: crypto.randomBytes
- .NET: RNGCryptoServiceProvider
- Java/Android: java.security.SecureRandom
- Ruby: SecureRandom
- iOS/OSX: SecRandomCopyBytes
Performing an EML API Request
The requirements for the API request vary depending on whether you are using the Web Services 2.0 (SOAP)
API or the Web Services 3.0 (REST)
API. See the relevant documentation for each API to confirm your requirements.
Both APIs minimally accept the following arguments:
- The
ExternalAccountId
of the account to get the card security details from - The
ApplicationId
of your application - The relevant
ApplicationKeyId
for the current user/version - The randomly generated
EphemeralKey
for this request
The following is an example of a simplified request from the Web Services 3.0 API.
// POST: /accounts/YCL80C2SW/dip
{
"application_id": "4bdb92b7-e22e-4012-aed4-9276ec67ead7",
"key_id": "d7d5b030-e94f-43f7-942d-3d6703236b84",
"ephemeral_key": "pwmbH/7quy8Jo0YtcPqz8oRqD53WCa4cHDaKkYueBLk="
}
Decrypting and Decoding the DIP
The following diagram illustrates the process of decoding and decrypting the DIP.
Decoding a byte sequence from Base64
The DIP is transmitted using Base64 encoding. The first step of decoding is to reverse this encoding and produce a byte sequence. A standard, non-variant, RFC 4648 compliant Base64 encoding is used.
Programming Resources
- Web/Javascript: atob
- Node.js: Buffer.from
- .NET: Convert.FromBase64String
- Java: java.util.Base64.Decoder
- Ruby: Base64.decode64
- Android: android.util.Base64
- iOS/OSX: NSData.init
PayloadVersion
Checking the Once you have reversed the Base64 encoding, you can examine the first byte in the resulting byte sequence, the PayloadVersion
byte. This is an 8-bit unsigned integer value, which has the expected value of 1
. Values of 2
through 255
are reserved for future versions of the DIP specification. Future versions may use an alternative decryption/decoding procedure, so you should abort decoding if the PayloadVersion
does not match the expected value.
PayloadKey
Deriving the The final DIP is encrypted using a 256-bit PayloadKey
, derived from the EphemeralKey
and ApplicationKeySecret
. We utilise a SHA-256 HMAC to derive the key.
PayloadKey = HMAC_SHA256(data: EphemeralKey, key: ApplicationKeySecret)
Programming Resources
- Node.js: crypto.createHmac
- .NET: HMACSHA256
- Java/Android: javax.crypto.Mac
- Ruby: OpenSSL::HMAC
- iOS/OSX: CommonCrypto/CommonHMAC
Decrypting with AES-256
We use the Advanced Encryption Standard (AES) to encrypt the DIP. To decrypt the payload, you need to use a compatible AES implementation along with the following parameters:
- Key: 256-bit
PayloadKey
- See above: Deriving the PayloadKey
- IV: 128-bit random IV generated by EML
- Contained within bytes
1:16
of the DIP payload.
- Contained within bytes
- Cipher Text: The encrypted account data
- Contained within bytes
17:48
pf the DIP payload
- Contained within bytes
- Mode: CBC
- Padding mode: PKCS#7 (also PKCS#5 compatible)
Programming Resources
- Node.js: crypto.createCipheriv
- .NET: Aes/AesManaged
- Java/Android: javax.crypto.Cipher
- Ruby: OpenSSL::Cipher
- iOS/OSX: CommonCrypto/CommonCryptor
- Swift: CryptoSwift
Format of the Decrypted Data
Once the DIP is decrypted, you are left with a 27 byte sequence containing the card security details. This byte sequence is a text string, which you can be decode with either UTF-8 or ASCII character encoding.
The text string contains the following elements, separated by semicolons (;
).
- The 16 digit card number (also known as PAN)
- The 6 digit card expiry date, month and year, formatted as
mmyyyy
. - The 3 digit card security code (also known as CVV2)
The string is always exactly 27 characters, and it contains no whitespace characters.
An example decoded string is shown below:
4000000000000001;102018;999
// Card Number = 4000000000000001
// Expiry Date = October 2018
// Security Code = 999