logo IPR declaration

Saturn - Payment Authorization Wallet

Disclaimer: this is a system in development, subject to change without notice.

Copyright Notice: this document is furnished under an MIT license.

Table of Contents

n/a1.  Introduction
n/a2.  Detailed Operation
n/a2.1.  Sequence Diagram
n/a2.2.  Wallet Initiation
n/a2.3.  Wallet Request UI
n/a2.4.  Payer Authorization
n/a2.5.  Payee Processing
n/a2.6.  Calling the Payment Network
n/a2.7.  Wallet Termination
n/a3.  Message Reference
n/a3.1.  Authorization Request
n/a3.2.  Payment Request
n/a3.3.  Authorization Response
n/a3.4.  Key Encryption
n/a3.5.  Unencrypted Data
n/a3.6.  Provider Info
n/a3.7.  Response Encryption
n/a3.8.  Signed Authorization
n/a4.  Credential Database
n/a5.  Credential Enrollment
n/a6.  Authorization Processing
n/a6.1.  Decryption
n/a6.2.  Signature Validation
n/a7.  Non-direct Payments (NDP)
n/a7.1.  NDP Objects
n/a7.2.  Gas Station Payments
n/a8.  Algorithm Support
n/a9.  Security Considerations
n/a10.  Test Vectors
n/a11.  Version

1.  Introduction

This document describes the core components of the Saturn wallet. Note that Saturn is a payment authorization system for end-users, not a payment system. Saturn is heavily influenced by the current "gold standard" for consumer payments, EMVemv.

Compared to EMV, Saturn introduces several enhancements:

Saturn builds on the idea that different payment networks should not need unique user authorizations solutions; only identifiers related to accounts and payment networks need to be adapted. This data is provided in the associated payment credentials (virtual payment cards), potentially making the wallet software universal.

Saturn is also intended to serve as a candidate for the payment authorization part of the EU Digital Identity (EUDI) Wallet [EUDIWeudiw].

2.  Detailed Operation

To guide the reader, this document is based on an example which in turn provides links to the formal definitions.

The Saturn protocol is based on CBOR [RFC8949rfc8949] which is a binary interchange format. However, for documentation purposes, messages are shown in diagnostic notation.

To support cryptographic operations requiring secure transformations of CBOR map objects, Saturn relies on an IETF standard currently in development [CDEcde], which defines deterministic encoding of CBOR.
2.1.  Sequence Diagram
The sequence diagram below outlines the Saturn protocol:
Payer icon provided through the courtesy of wikimedia.orgpayer.
2.2.  Wallet Initiation
The payment process is initiated when the Payer hits a "Pay" button on the Web or scans a QR-code, returning a Wallet activation URL. The Wallet should then use the received URL for performing an HTTP GET (step #1 in the Sequence Diagram) to the Payee service. This operation should return an Authorization Request object (step #2 in the Sequence Diagram) like the following:
1010(["https://cyberphone.github.io/saturn/areq/v1", {
  1: {
    1: "Space Shop",
    2: "600.00",
    3: "EUR",
    4: "20250114.00079"
  },
  2: ["https://cardnetwork.com", "https://banknet2.org"],
  3: "https://spaceshop.com/receipts/20250114.00079.MNloPyPahXxr43flXzufdQ"
}])
Note that the Payee service must set the HTTP Content-Type header parameter to application/cbor.
2.3.  Wallet Request UI
After receival of the Authorization Request, the Wallet should display a UI like the following:
If there are multiple Credential Database entries matching the Authorization Request, the Payer needs to select (step #3 in the Sequence Diagram) a suitable credential, unless the default (or last used) credential already meets the preferences of the Payer.

In the non-normative sample UI, credential selection is performed through swiping the card images.

If there are no matching payment credentials, the Wallet must provide the Payer with a suitable error message and a cancel button.

If the requested currency differs from the default (as defined by the locale settings of the operating system), it is recommended displaying ISO three-letter abbreviations (USD, EUR, SEK, etc.) rather than short-hand versions like '$' and '€'. See also Payment Request.

2.4.  Payer Authorization
Assuming that the Payer accepts and subsequently authorizes the request (step #4 in the Sequence Diagram) using a biometric operation or PIN, the Wallet should perform an HTTP POST (step #5 in the Sequence Diagram) to the Payee service containing an Authorization Response object like the following:
1010(["https://cyberphone.github.io/saturn/ares/v1", {
  0: 1010(["https://cyberphone.github.io/saturn/sig/v1", {
    1: {
      1: {
        1: "Space Shop",
        2: "600.00",
        3: "EUR",
        4: "20250114.00079"
      },
      2: {
        1: "https://banknet2.org",
        2: "mybank.com"
      },
      3: "spaceshop.com",
      4: "2025-01-14T13:28:02-01:00"
    }
  }]),
  1: 3,
  2: {
    1: -29,
    4: {
      1: 2,
      -1: 1,
      -2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
      -3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8'
    },
    7: {
      1: 2,
      -1: 1,
      -2: h'b7b091914e4156d8003534bd4784a3a891dadb491ec70d3920aea42abc87931a',
      -3: h'bba3903327062bca0f6ec87e2d2b55be2676678223bff41e1a54e0cea28deb17'
    },
    10: h'b3b85ffa8b53735f07d365cbf2a7b76430f53c57e28c8281e9c6a14c7ac347fea02067aef11510e1'
  },
  8: h'172ec3e612e7a13e5655e21e8f7205ee',
  9: h'09553821a9c38becdd28766a',
  10: h'cd8a9ccc07b722dc0be06d9e459453304780a702ced562d82fed32d02a041a49427c39120181c5232050be44faa136a02328645fcaa97c09362f46ead431d20560ccb0d292246e6c1144297966a72721263604f197d5a66b8ffd608922f1212a19a19034d8b54db31617dd8da332cfb5b2061ff0465972654414e3dfb12ca0d00c71c4d2c666756573710a36216b77109b284420f3703124680c6fe5961e76da022e9af7cb3e56c237a622f6b7d2a70cb85ae2cd5fc5ec297be830c6d8e3600d415747121597e2fade0dd0d73ca082c75d387dc9f1062fe2173915dd2625906b2154d0f543616ec00146c905a10f02d89a1b'
}])
Note that the POST must be directed to the same URL as used by the GET in Wallet Initiation. In addition, the HTTP Content-Type header parameter must be set to application/cbor.
2.5.  Payee Processing
After receiving the Authorization Response, the Payee must perform a number of checks on the Unencrypted Data object including: If any validation step fails, the authorization process must be aborted including providing the Payer with a suitable error message,
2.6.  Calling the Payment Network
After successful validation of the Authorization Response, the Payee includes this object in a payment-network-specific request to the designated PaymentNetwork (provided by the selected payment credential). Note that this phase may constitute of multiple request-response pairs.
2.7.  Wallet Termination
After successful (or failed) authorization, the Payee must return a Wallet termination message (step #6 in the Sequence Diagram). This message depends on how the payment process was initiated, and is currently TBD.

3.  Message Reference

This section contains a reference to the components underpinning Saturn messages. The components are based on CBOR map objects.

In the tables outlining the map objects, "Name" refers to symblic name used for documentation purposes only, "Label" refers to the actual CBOR integer value used as key, while "Type" refers to the type of the value component of an entry.

Note that the "https://cyberphone.github.io/saturn/..." identifiers represent temporary name allocations.
3.1.  Authorization Request
An Authorization Request consists of a CBOR map object, embedded by a COTXcotx wrapper:
1010(["https://cyberphone.github.io/saturn/areq/v1", {
CBOR map...
}])
NameLabelTypeDescription
paymentRequest1mapPayment Request is the core Payee request object.
supportedNetworks2arrayNon-empty list of payment network/method identifiers that the Payee supports. Network identifiers are expressed as CBOR strings (tstr).
See also networkId in the Credential Database.
receiptUrl3tstrOptional: URL to a Payee receipt service.
See also RECEIPTSreceipts.
The Authorization Request represents the primary Payee to Wallet message. In same-device Web contexts this message is also associated with the invocation of the Wallet.
3.2.  Payment Request
NameLabelTypeDescription
commonName1tstrPayee common name to be shown in the Wallet Request UI
Note that common and legal names often differ.
amount2tstrMonetary amount compatible with the regular expression: ^(0|[1-9][0-9]*)(\.[0-9]+)?$.
Amounts must not use more decimals than is custom for prices for the specific currency.
currency3tstrCurrency expressed in the ISO4217iso4217 alphabetical format.
referenceId4tstrPayee reference Id.
Reference Ids must be unique with respect to the Payee.
nonDirectPayment5cotxOptional: See also Non-direct Payments (NDP).
3.3.  Authorization Response
An Authorization Response consists of a single CEFcef object, where the outermost element is a COTXcotx wrapper as follows:
1010(["https://cyberphone.github.io/saturn/ares/v1", {
CEF container...
}])
Note that the COTX wrapper is included in the encryption process by constituting a part of the Additional Authentication Data (AAD).
The CEF container map keys are as follows:
NameLabelTypeDescription
customData0mapCEF custom (unencrypted) data in the form of a copy of the Signed Authorization object where all map objects except for the Unencrypted Data object have been removed.
Also see Decryption.
algorithm1intCopy of the encContentAlg attribute of the selected payment credential in the Credential Database.
keyEncryption2mapHolds the CEF Key Encryption object.
tag8bstrEncryption algorithm tag.
iv9bstrEncryption algorithm initialization vector (IV).
cipherText10bstrEncrypted version of the outermost map object of the Signed Authorization object where the Unencrypted Data object has been removed.
Also see Decryption.
See also Authorization Processing.
3.4.  Key Encryption
NameLabelTypeDescription
algorithm1intCopy of the encKeyAlg attribute of the selected payment credential in the Credential Database.
keyId3"Any"Optional: Copy of the encKeyId attribute of the selected payment credential in the Credential Database.
publicKey4mapOptional: Copy of the encPublicKey object of the selected payment credential in the Credential Database.
Note that keyId and publicKey are mutually exclusive.
ephemeralKey7mapEphemeral ECDH public key.
cipherText10bstrOptional: Encrypted key for key wrapping algorithms.
3.5.  Unencrypted Data
NameLabelTypeDescription
paymentRequest1mapThrough the inclusion of a copy of the Payment Request in the Payer authorization, this object remains authoritative throughout the payment process (except for interbank operations).
providerInfo2mapHolds the Provider Info required by the Payee for deriving which payment network to use and how to initiate a compatible payment transaction request.
payeeHost3tstrHost name or IP address of the invoking Payee, derived from the URL obtained in step #1 in the sequence diagram.
The purpose of the payeeHost attribute is to provide a means for a Payment Network to verify that the origin of a received Authorization Response matches that of the Payee. This is essentially an inverted version of the phishing protection method used by WebAuthn [WEBAUTHNwebauthn].
The security of this arrangement also depends on that forwarded Payee requests are properly authenticated.
timeStamp4tstrISO date-time string [RFC3339rfc3339] using UTC (T) or local time (Z) format.
The purpose of the timeStamp attribute is to provide a means for an Issuer to verify the "freshness" of a received Authorization Response. The recommended method is using a cache holding a hash of the associated Signed Authorization and its timeStamp, where the latter is used to automatically remove a cache entry when the authorization is considered to be expired. This arrangement is either used for protection against replay, or for supporting idempotent operation.
Note that authorizations that already have expired or are too new must be rejected.
Tentative lower limit: timeStamp > currentTime - 600s
Tentative higher limit: timeStamp < currentTime + 60s
3.6.  Provider Info
NameLabelTypeDescription
networkId1tstrCopy of the same attribute of the selected payment credential in the Credential Database.
serviceLocator2tstrCopy of the same attribute of the selected payment credential in the Credential Database.
3.7.  Response Encryption
The Response Encryption object provides a means for an Issuer to return messages to the Payer like "Out of funds" through the regular transaction channel, and that without revealing any personal information to intermediaries. In addition, Response Encryption also serves as a strong nonce, ensuring that Signed Authorization objects become unique, irrespective of Authorization Request data and time-stamps.
This concept can also be used to force the Payer to provide additional information which could be needed for high-value or "suspicious" transaction requests.
NameLabelTypeDescription
algorithm1intCopy of the encKeyAlg attribute of the selected payment credential in the Credential Database.
instanceKey2bstrRandom encryption key, initialized for each authorization request.
The length of the instanceKey attribute must match the algorithm.
3.8.  Signed Authorization
A Signed Authorization consists of a CBOR map wrapped in a COTXcotx container as follows:
1010(["https://cyberphone.github.io/saturn/sig/v1", {
CBOR map...
}])
Note that the COTX wrapper is included in the signature process as well.
The CBOR map keys are as follows:
NameLabelTypeDescription
unencryptedData1mapHolds the Unencrypted Data object.
responseEncryption2mapHolds the Response Encryption object.
accountId3tstrCopy of the same attribute of the selected payment credential in the Credential Database.
serialNumber4tstrCopy of the same attribute of the selected payment credential in the Credential Database.
platformData5arrayArray holding the name and version of the operating system in [0] and [1] respectively, expressed as CBOR strings (tstr).
walletData6arrayArray holding the name and version of the Wallet software in [0] and [1] respectively, expressed as CBOR strings (tstr).
location7arrayOptional: Array holding the current latitude [0] and longitude [1] of the Wallet device, expressed as CBOR floating point values.
This option depends on Payer privacy settings.
authzSignature-1mapAuthorization signature using a CSFcsf object.
For an example, see Signature Validation.

4.  Credential Database

A fundamental part of the Wallet is a local database holding enrolled payment credentials. Note that although credential data types listed here are mainly expressed as CBOR, other representations may be used. The only requirement is that credential data types can be securely mapped back and forth to CBOR.

The data type "ps" denotes a platform-specific solution.

Each entry in the database contains a payment credential according to the following definition:

NameTypeDescription
versiontstrSince credential data may evolve over time, versioning is necessary. This specification covers version: https://cyberphone.github.io/saturn/cred/v1.
networkIdtstrPayment network/method identifier. Since payment networks are likely to continue having unique message solutions, the Payee needs to identify the specific network before making a transaction request.
Payment network identifiers may be expressed as URLs or as simple names like "VISA". Note that this concept does not make a distinction between payment methods or "schemes".
See also Provider Info.
serviceLocatortstrPayment service URL or host name. This attribute enables the Payee to find the end-point of the specific payment service (like a bank), associated with the payment credential.
How to interpret this attribute is dictated by the networkId identifier. If serviceLocator is expressed as a host-name only, a "/.well-known/" [RFC8615rfc8615] URL extension would typically be used.
See also Provider Info.
accountIdtstrAccount identifier associated with the payment credential.
serialNumbertstrSerial number of the payment credential.
cardImagebstrCard image associated with the payment credential. Card images are used for aiding Payer administration of payment credentials as well as being featured in the Wallet Request UI.
Card images must be in SVGsvg format and tentatively having a size of 300×180 pixels.
authzAlgintCOSE signature algorithm to use for creating Signed Authorization objects.
authzKeyHandle"ps"Local handle to the private key to use for creating Signed Authorization objects.
authzPublicKey"ps"Authorization public key for inclusion in Signed Authorization objects.
encContentAlgintCOSE content encryption algorithm to use for creating Authorization Response objects.
encKeyAlgintCOSE key encryption algorithm to use for creating Authorization Response objects.
encPublicKey"ps"Encryption public key to use for creating Authorization Response objects.
Note that encPublicKey objects are provided by credential issuers. In order to serve their primary purpose, preserving privacy, encPublicKey objects must be shared by multiple clients.
encKeyId"Any"Optional: If the encKeyId attribute is defined, it must be featured in Key Encryption objects instead of encPublicKey.

5.  Credential Enrollment

TBD.

6.  Authorization Processing

This section describes how Authorization Response messages should be processed, using the Payer Authorization sample and associated Test Vectors as model data.
6.1.  Decryption
Firstly, the Authorization Response object needs to be decrypted using a private key associated with the supplied CEFcef publicKey or keyId attribute. In the sample, a publicKey attribute was used.

Note that enclosing COTXcotx object must be included in the decryption process.

The decryption process should return two CBOR objects.

1. The Signed Authorization object where all map objects except for the Unencrypted Data object have been removed. Note that this item is already supplied in clear:
1010(["https://cyberphone.github.io/saturn/sig/v1", {
  1: {
    1: {
      1: "Space Shop",
      2: "600.00",
      3: "EUR",
      4: "20250114.00079"
    },
    2: {
      1: "https://banknet2.org",
      2: "mybank.com"
    },
    3: "spaceshop.com",
    4: "2025-01-14T13:28:02-01:00"
  }
}])
2. The remaining part of the Signed Authorization object which after decryption should read like the following:
{
  2: {
    1: 3,
    2: h'7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a'
  },
  3: "FR7630002111110020050014382",
  4: "010049255",
  5: ["Android", "15"],
  6: ["Saturn", "1.0.0"],
  7: [38.8882, -77.01988],
  -1: {
    1: -50,
    4: {
      1: 1,
      -1: 6,
      -2: h'fe49acf5b92b6e923594f2e83368f680ac924be93cf533aecaf802e37757f8c9'
    },
    6: h'efe04dba72f599b91217e557bb9f6e9049094a51ab9abe9349c4fe76038be27850cb9ad7ecf4c7456519344989cea329d5b8df5a1e0ae81520b662ad08a5bc01'
  }
}
6.2.  Signature Validation
Now combine the objects retrieved during the decryption phase by merging the second map with the first map (Unencrypted Data). Note that the length of the resulting map object must be updated to reflect the addition of an item. This operation effectively recreates the Signed Authorization object:
1010(["https://cyberphone.github.io/saturn/sig/v1", {
  1: {
    1: {
      1: "Space Shop",
      2: "600.00",
      3: "EUR",
      4: "20250114.00079"
    },
    2: {
      1: "https://banknet2.org",
      2: "mybank.com"
    },
    3: "spaceshop.com",
    4: "2025-01-14T13:28:02-01:00"
  },
  2: {
    1: 3,
    2: h'7fdd851a3b9d2dafc5f0d00030e22b9343900cd42ede4948568a4a2ee655291a'
  },
  3: "FR7630002111110020050014382",
  4: "010049255",
  5: ["Android", "15"],
  6: ["Saturn", "1.0.0"],
  7: [38.8882, -77.01988],
  -1: {
    1: -50,
    4: {
      1: 1,
      -1: 6,
      -2: h'fe49acf5b92b6e923594f2e83368f680ac924be93cf533aecaf802e37757f8c9'
    },
    6: h'efe04dba72f599b91217e557bb9f6e9049094a51ab9abe9349c4fe76038be27850cb9ad7ecf4c7456519344989cea329d5b8df5a1e0ae81520b662ad08a5bc01'
  }
}])
Since this object contains a CSFcsf publicKey attribute, it can be validated using "as is".

Note that the authenticity of received public keys must be verified before authorization objects are acted upon!

Authorization objects must also be checked for alignment with the specification. Missing, additional, or malformed elements must be rejected.

See also timeStamp in Unencrypted Data.

7.  Non-direct Payments (NDP)

Non-direct payments represent a group of payment-related scenarios where an initial Payer authorization is followed by one or more operations performed by the Payee, usually without further intervention by the Payer. Here we find gas-station payments, subscriptions, BNPL (Buy Now Pay Later), and deposits.

Non-direct payments typically involve reservation of funds. Subscriptions and BNPL may also be subject to Payer credit considerations.

Although this specification only defines a single non-direct payment scenario, this is intended to serve as a model for additional variants as well.

7.1.  NDP Objects
NDP objects are denoted by a COTXcotx object as follows:
1010([ndpObjectId,
Additional Parameters
])
Note that ndpObjectId URLs must be unique and expressed as CBOR strings (tstr).
7.2.  Gas Station Payments
A gas station payment consists of two operations: 1) Reservation of a maximum amount of money to be used. 2) Resolving the reservation by debiting the Payer for the actual cost of the fill-up. Note that a valid receipt (step #7 in the Sequence Diagram) can only be made available after the second phase has been executed.

For gas station payments, ndpObjectId must be set to: "https://cyberphone.github.io/saturn/ndp/gas", while the Additional Parameters consist of single CBOR integer (int) holding the number of hours (1-24) the reservation will remain valid before being automatically revoked by the account-holding entity. It is recommended to have a margin of at least 15 minutes.

The CBOR object
1010(["https://cyberphone.github.io/saturn/ndp/gas", 2])
thus represents an argument to a Payment Request, for a gas station payment with a validity of 2 hours.

Since non-direct payments differ from one-off payments, the Wallet UI should also reflect such requests in a meaningful way.

For gas station payments, the following appears like a suitable solution:

8.  Algorithm Support

The following table shows the minimum algorithm support required:
NameDescription
Ed25519 Authorization (signature) algorithm.
ESP256
ECDH-ES+A128KW Key encryption algorithm for usage with P-256 and X25519 keys.
ECDH-ES+A256KW
A128GCM Content encryption algorithm.
A256GCM

9.  Security Considerations

TBD.

10.  Test Vectors

Equipped with an appropriate diagnostic notation parser like https://cyberphone.github.io/CBOR.js/doc/playground.html and CEFcef/CSFcsf support, the Payer Authorization sample should be possible to decrypt and validate, using the following sample keys.
Authorization key in JWK format:
{
  "kty": "OKP",
  "crv": "Ed25519",
  "x": "_kms9bkrbpI1lPLoM2j2gKySS-k89TOuyvgC43dX-Mk",
  "d": "0flr-6bXs459f9qwAq20Zs3NizTGIEH5_rTDFoumFV4"
}
Authorization key in COSE format:
{
  1: 1,
  -1: 6,
  -2: h'fe49acf5b92b6e923594f2e83368f680ac924be93cf533aecaf802e37757f8c9',
  -4: h'd1f96bfba6d7b38e7d7fdab002adb466cdcd8b34c62041f9feb4c3168ba6155e'
}
Encryption key in JWK format:
{
  "kty": "EC",
  "crv": "P-256",
  "x": "6BKxpty8cI-exDzCkh-goU6dXq3MbcY0cd1LaAxiNrU",
  "y": "mCbcvUzm44j3Lt2b5BPyQloQ91tf2D2V-gzeUxWaUdg",
  "d": "6XxMFXhcYT5QN9w5TIg2aSKsbcj-pj4BnZkK7ZOt4B8"
}
Encryption key in COSE format:
{
  1: 2,
  -1: 1,
  -2: h'e812b1a6dcbc708f9ec43cc2921fa0a14e9d5eadcc6dc63471dd4b680c6236b5',
  -3: h'9826dcbd4ce6e388f72edd9be413f2425a10f75b5fd83d95fa0cde53159a51d8',
  -4: h'e97c4c15785c613e5037dc394c88366922ac6dc8fea63e019d990aed93ade01f'
}

11.  Version

API version: 0.58
Document version: 2025-01-14
Author: Anders Rundgren (anders.rundgren.net@gmail.com)