Security Trailers
Every message sent to the Toro Gateway must include a valid Security Trailer.
For detailed guidance on the format of the Security Trailer, please see the Nexo Card Payment Protocols Security Message User Guide (MUG) which can be downloaded from nexo-standards.org.
The Toro Gateway requires implementers to provide a Security Trailer using the Authenticated Data type; in other words, your implementation should generate a MAC (Message Authentication Code) using one of the supported algorithms and specify this in the Security Trailer.
The Security Trailer protects the message using a MAC (Message Authentication Code).
MAC generation and MAC verification use the same algorithms. MAC is verified by generating the MAC from the received message, and compared to the received MAC.
At a high level, the steps to generate a security trailer are:
- During onboarding, a shared passphrase is generated and provided
- Key material is derived from the shared passphrase using PBKDF2
- A MAC is calculated for a given message body using the derived key material
- A security trailer is generated, setting the MAC field and other information
An example security trailer is shown below:
<SctyTrlr>
<CnttTp>AUTH</CnttTp>
<AuthntcdData>
<Rcpt>
<KeyIdr>
<KeyId>SpecV1TestKey</KeyId>
<KeyVrsn>1</KeyVrsn>
</KeyIdr>
</Rcpt>
<MACAlgo>
<Algo>CCA3</Algo>
</MACAlgo>
<NcpsltdCntt>
<CnttTp>DATA</CnttTp>
</NcpsltdCntt>
<MAC>9xIobnJ/PyaB10EB4L6N4w==</MAC>
</AuthntcdData>
</SctyTrlr>Derive Key Material
During onboarding to the Toro Platform, you will be provided with a passphrase which can be used to derive the relevant cryptographic material needed to generate and verify message security trailers.
In the sandbox environment, the following passphrase can be used by all integrating parties: ToroGatewayNexoV1PassphraseDevOnly
The passphrase is used in combination with a fixed salt to generate a 32-byte (256-bit) MAC key.
The process uses PBKDF2 (Password-Based Key Derivation Function 2) to generate cryptographic keys and an initialization vector (IV) from a single passphrase.
Key material is derived from the shared passphrase, and only changes when the shared key changes. Therefore it is not necessary to derive key material for each message. However, the derived key material is secret, so if your implementation does not derive key material for every message, it is important to store the key material securely in your system.
Required Parameters
- passphrase: A string containing the secret passphrase
- salt: A string used as cryptographic salt. The salt value is fixed and should always be: ToroGatewayNexoV1Salt
Fixed Parameters
- Iterations: 4000
- Output Length: 80 bytes
- Hash Function: SHA-256
Derived output
The PBKDF2 function will generate an 80 byte output. This should be used as follows:
- CMAC key: bytes 1-32
- Cipher key: bytes 33-64
- Initialisation vector: bytes 65-80
Code examples
# Java Implementation
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.spec.KeySpec;
public class KeyDerivation {
public static Map<String, byte[]> nexoDeriveKeymaterial(String passphrase)
throws Exception {
byte[] salt = "ToroGatewayNexoV1Salt".getBytes(StandardCharsets.UTF_8);
int rounds = 4000;
int outlen = 80;
KeySpec spec = new PBEKeySpec(
passphrase.toCharArray(),
salt,
rounds,
outlen * 8 // KeySpec expects bits, not bytes
);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] derived = factory.generateSecret(spec).getEncoded();
Map<String, byte[]> result = new HashMap<>();
result.put("cmac_key", Arrays.copyOfRange(derived, 0, 32));
result.put("cipher_key", Arrays.copyOfRange(derived, 32, 64));
result.put("iv", Arrays.copyOfRange(derived, 64, 80));
return result;
}
}Implementation verification
To verify that your implementation is working as expected, please ensure that the following inputs result in the output shown.
Salt: ToroGatewayNexoV1Salt
Passphrase: ToroGatewayNexoV1PassphraseDevOnly
MAC key: e0 5d cb a1 27 6a 3d 82 2f 38 c7 a6 c4 5f 2f cf d4 be 8b 08 49 58 86 33 f5 cd 9d 21 02 44 ee 8f
Cipher key: f4 d7 df e8 f8 e2 f1 6f fd 87 ad f4 70 db ef 42 19 19 86 af 14 3f 45 c7 f7 00 46 c0 5d aa e7 fa
Initialisation Vector (IV): 50 df 6b e8 74 22 b9 ec 12 9e cd 1b 67 54 84 85
Security Trailer structure
A valid security trailer should be sent with every request. It should conform to the defined Nexo Standard and use the AuthenticatedMessage data structure. The relevant fields for ISO 20022 message component ContentInformationType36 are shown below:
Level | Field Name | Parent | Abbreviation | Multiplicity | Description | ISO 20022 Type |
|---|---|---|---|---|---|---|
0 | Security Trailer | [message container e.g. AcceptorCompletionAdviceV12] | SctyTrlr | Mandatory | Trailer of the message containing a MAC. | ContentInformationType36 |
1 | Content Type | Security Trailer | CnttTp | Mandatory | Type of data protection. Allowed values:
| ContentType2Code |
1 | Authenticated Data | Security Trailer | AuthntcdData | Mandatory | Data protection by a message authentication code (MAC). | AuthenticatedData9 |
2 | Version | Authenticated Data | Vrsn | Optional | Version of the data structure. Restrictions:
| Number |
2 | Recipient | Authenticated Data | Rcpt | Repetition (1..n) | Session key or protection key identification used by the recipient. | Recipient14Choice |
3 | Key Identifier | Recipient | KeyIdr | Choice | Identification of a protection key without a session key, shared and previously exchanged between the initiator and the recipient. | KEKIdentifier7 |
4 | Key Identification | Key Identifier | KeyId | Mandatory | Identification of the cryptographic key. | Max140Text |
4 | Key Version | Key Identifier | KeyVrsn | Mandatory | Version of the cryptographic key | Max140Text |
4 | Sequence Number | Key Identifier | SeqNb | Optional | Number of usages of the cryptographic key. | Number |
4 | Derivation Identification | Key Identifier | DerivtnId | Optional | Identification used for derivation of a unique key from a master key provided for the data protection. | Max500Binary |
3 | MAC Algorithm | Authenticated Data | MACAlgo | Mandatory | Algorithm to compute message authentication code (MAC). | AlgorithmIdentification22 |
4 | Algorithm | MAC Algorithm | Algo | Mandatory | Identification of the MAC algorithm. Allowed values:
| Algorithm17Code |
4 | Parameter | MAC Algorithm | Param | Optional | Parameters associated to the MAC algorithm. | Parameter7 |
5 | Initialisation Vector | Parameter | InitlstnVctr | Optional | Initialisation vector of a cipher block chaining (CBC) mode encryption. | Max500Binary |
5 | Byte Padding | Parameter | BPddg | Optional | Byte padding for a cypher block chaining mode encryption, if the padding is not implicit. | BytePadding1Code |
3 | Encapsulated Content | Authenticated Data | NcpsltdCntt | Mandatory | Data to authenticate. | EncapsulatedContent3 |
4 | Content Type | Encapsulated Content | CnttTp | Mandatory | Type of data which have been authenticated. Allowed values:
| |
4 | Content | Encapsulated Content | Cntt | Optional | Actual data to authenticate. Format: Base64 Restrictions:
| Max100KBinary |
3 | MAC | Authenticated Data | MAC | Mandatory | Message authentication code value. Format: Base64 Restrictions:
| Max140Binary |
MAC Generation
For further information on MAC generation for Nexo messages, please see the Nexo Protocol Security MUG section 5.
MAC generation and MAC verification use the same algorithms. MAC is verified by generating the MAC from the received message, and compared to the received MAC. In response messages, the gateway will generate a security trailer using the same MAC algorithm that was used in the request message.
MAC computation is carried out on the message body only. The header (and, obviously, security trailer) components of a message should not be included in the MAC computation input.
It is important that the MAC computation is carried out on the minified version of the message body, omitting all unnecessary spaces and carriage returns. This ensures that calculation and verification can be carried out using consistent representations of the message body.
Supported MAC Algorithms
The Toro Gateway supports the following MAC algorithms:
- CMAC with AES128
- SHA-256 CMAC with AES128
- SHA3-256 CMAC with AES128
- CMAC with AES192
- SHA-384 CMAC with AES192
- SHA3-384 CMAC with AES192
- CMAC with AES256
- SHA-512 CMAC with AES256
- SHA3-512 CMAC with AES256
MAC Generation Examples
The following examples are adapted from the Nexo Protocol Security MUG section 5.4.
As an example message body to compute a MAC example, we will use the AcceptorDiagnosticRequest message of the Acquirer protocol.
As input of the AcceptorDiagnosticRequest MAC, the XML encoded body DiagnosticRequest of the message is:
<DgnstcReq>
<Envt>
<AcqrrParamsVrsn>2010-01-01T08:00:00</AcqrrParamsVrsn>
<MrchntId>
<Id>EPASMER001</Id>
<Tp>MERC</Tp>
</MrchntId>
<POIId>
<Id>66000001</Id>
<Tp>OPOI</Tp>
<Issr>ACQR</Issr>
</POIId>
</Envt>
</DgnstcReq>Once unnecessary spaces and carriage returns are removed, AcceptorDiagnosticRequest is:
<DgnstcReq><Envt><AcqrrParamsVrsn>2010-01-01T08:00:00</AcqrrParamsVrsn><MrchntId><Id>EPASMER001</Id><Tp>MERC</Tp></MrchntId><POIId><Id>66000001</Id><Tp>OPOI</Tp><Issr>ACQR</Issr></POIId></Envt></DgnstcReq>CMAC examples with AES256
These examples demonstrate MAC calculation using CMAC AES-256. The examples are taken from the Nexo Protocol Security MUG.
In these examples, the following AES256 bit key is used:
34790642 381CC5FF AD606931 49B29D39 340E5482 748C37EB 8A6F4BF3 F3EF39E2
Example: CMAC with AES256
See Nexo Protocol Security MUG Section 5.4.6 for a full breakdown of the MAC generation process. Example commands using OpenSSL are shown below.
# Save the input message as message.txt
echo -n "<DgnstcReq><Envt><AcqrrParamsVrsn>2010-01-01T08:00:00</AcqrrParamsVrsn><MrchntId><Id>EPASMER001</Id><Tp>MERC</Tp></MrchntId><POIId><Id>66000001</Id><Tp>OPOI</Tp><Issr>ACQR</Issr></POIId></Envt></DgnstcReq>" > message.txt
# Save the key as hey.hex
echo -n "34790642381CC5FFAD60693149B29D39340E5482748C37EB8A6F4BF3F3EF39E2" > hey.hex
# Calculate the MAC using cipher AWS-256
openssl dgst -mac cmac -macopt cipher:aes-256-cbc -macopt hexkey:$(cat key.hex) message.txt
# Expected output:
# CMAC(message.txt)= f712286e727f3f2681d74101e0be8de3
# Convert the hex to base64
echo "f712286e727f3f2681d74101e0be8de3" | xxd -r -p | base64
# Base64 representation of the MAC: 9xIobnJ/PyaB10EB4L6N4w==The MAC of the message is: F7 12 28 6E 72 7F 3F 26 81 D7 41 01 E0 BE 8D E3
The Base64 representation of the MAC is: 9xIobnJ/PyaB10EB4L6N4w==
The resulting XML encoded structure is shown below:
<SctyTrlr>
<CnttTp>AUTH</CnttTp>
<AuthntcdData>
<Rcpt>
<KeyIdr>
<KeyId>SpecV1TestKey</KeyId>
<KeyVrsn>1</KeyVrsn>
</KeyIdr>
</Rcpt>
<MACAlgo>
<Algo>CCA3</Algo>
</MACAlgo>
<NcpsltdCntt>
<CnttTp>DATA</CnttTp>
</NcpsltdCntt>
<MAC>9xIobnJ/PyaB10EB4L6N4w==</MAC>
</AuthntcdData>
</SctyTrlr>Example: SHA-512 CMAC with AES256
See section Nexo Protocol Security MUG Section 5.4.6.3 for a full breakdown of the MAC generation process. Example commands using OpenSSL are shown below.
# Generate a SHA-512 digest of the message and save it to digest.bin
echo -n "<DgnstcReq><Envt><AcqrrParamsVrsn>2010-01-01T08:00:00</AcqrrParamsVrsn><MrchntId><Id>EPASMER001</Id><Tp>MERC</Tp></MrchntId><POIId><Id>66000001</Id><Tp>OPOI</Tp><Issr>ACQR</Issr></POIId></Envt></DgnstcReq>" | openssl dgst -sha512 -binary > digest.bin
# Check it matches the expected value
xxd digest.bin
# 00000000: 09ae 273c ac0d ef0f 4243 e9dc 9dfc b076 ..'<....BC.....v
# 00000010: 5c7e b7ee a016 b5b2 8ae1 5406 5264 ce6a \~........T.Rd.j
# 00000020: 2573 045d ee09 d8d7 1696 840a 70c8 2140 %s.]........p.!@
# 00000030: e4dc d52e 83f5 578e 9448 71b2 faef c117 ......W..Hq.....
# Save the key as hey.hex
echo -n "34790642381CC5FFAD60693149B29D39340E5482748C37EB8A6F4BF3F3EF39E2" > hey.hex
# Calculate the MAC using AES-256 cipher
openssl dgst -mac cmac -macopt cipher:aes-256-cbc -macopt hexkey:$(cat key.hex) digest.bin
# Expected output:
# CMAC(digest.bin)= 45a2bf16ea10fd137ea95eb8256ed92f
# Convert the hex to base64
echo "45a2bf16ea10fd137ea95eb8256ed92f" | xxd -r -p | base64
# Base64 representation of the MAC: RaK/FuoQ/RN+qV64JW7ZLw==The resulting XML encoded structure is shown below:
<SctyTrlr>
<CnttTp>AUTH</CnttTp>
<AuthntcdData>
<Rcpt>
<KeyIdr>
<KeyId>SpecV1TestKey</KeyId>
<KeyVrsn>1</KeyVrsn>
</KeyIdr>
</Rcpt>
<MACAlgo>
<Algo>CMA5</Algo>
</MACAlgo>
<NcpsltdCntt>
<CnttTp>DATA</CnttTp>
</NcpsltdCntt>
<MAC>RaK/FuoQ/RN+qV64JW7ZLw==</MAC>
</AuthntcdData>
</SctyTrlr>Example: SHA3-512 CMAC with AES256
See section Nexo Protocol Security MUG Section 5.4.6.4 for a full breakdown of the MAC generation process. Example commands using OpenSSL are shown below.
# Generate a SHA-512 digest of the message and save it to digest.bin
echo -n "<DgnstcReq><Envt><AcqrrParamsVrsn>2010-01-01T08:00:00</AcqrrParamsVrsn><MrchntId><Id>EPASMER001</Id><Tp>MERC</Tp></MrchntId><POIId><Id>66000001</Id><Tp>OPOI</Tp><Issr>ACQR</Issr></POIId></Envt></DgnstcReq>" | openssl dgst -sha3-512 -binary > digest.bin
# Check it matches the expected value
xxd digest.bin
# 00000000: 2b43 71a8 c795 0c12 9576 1200 eaec 3551 +Cq......v....5Q
# 00000010: 611b 1c50 8334 fc3c 5a39 78a5 57ec a37a a..P.4.<Z9x.W..z
# 00000020: 45fc f5a2 a6e3 afc3 d728 2888 cb0d 7c74 E........((...|t
# 00000030: 5e6f 7daf 0944 5f9e 5043 d820 2e69 a99c ^o}..D_.PC. .i..
# Save the key as hey.hex
echo -n "34790642381CC5FFAD60693149B29D39340E5482748C37EB8A6F4BF3F3EF39E2" > hey.hex
# Calculate the MAC using AES-256 cipher
openssl dgst -mac cmac -macopt cipher:aes-256-cbc -macopt hexkey:$(cat key.hex) digest.bin
# Expected output:
# CMAC(digest.bin)= ba35edff29f16bfe4ce7fc1bbf784aab
# Convert the hex to base64
echo "ba35edff29f16bfe4ce7fc1bbf784aab" | xxd -r -p | base64
# Base64 representation of the MAC: ujXt/ynxa/5M5/wbv3hKqw==The resulting XML encoded structure is shown below:
<SctyTrlr>
<CnttTp>AUTH</CnttTp>
<AuthntcdData>
<Rcpt>
<KeyIdr>
<KeyId>SpecV1TestKey</KeyId>
<KeyVrsn>1</KeyVrsn>
</KeyIdr>
</Rcpt>
<MACAlgo>
<Algo>CM33</Algo>
</MACAlgo>
<NcpsltdCntt>
<CnttTp>DATA</CnttTp>
</NcpsltdCntt>
<MAC>ujXt/ynxa/5M5/wbv3hKqw==</MAC>
</AuthntcdData>
</SctyTrlr>
Responses to common security trailer scenarios
- Messages received by the Gateway which do not include a security trailer will be rejected with a 400 BAD REQUEST error.
- Messages which include an invalid (e.g. incorrectly formatted) security trailer will be rejected with a 400 BAD REQUEST error.
- Messages whose security trailer cannot be verified against the sender's shared passphrase will be rejected with a 403 FORBIDDEN error.