[webauthn] FIDO Payment Support (#1570)

cyberphone has just created a new issue for https://github.com/w3c/webauthn:

== FIDO Payment Support ==
Since the SPC specification is to be discussed at https://github.com/w3c/webpayments/wiki/Agenda-FTF2021, I took the liberty outlining an enhanced scheme that may be used as "inspiration".  It is not complete, but the core is hopefully understandable.  The proposal mainly deals with security constructs and data flows. 

### Enhancements

- _Privacy_: User-unique information (PII) is neither required nor provided to external actors.
- _Security_: Reduced attack space since no sensitive data is given to external actors. Encryption replaces tokenization.
- _Accounts_: Applicable to any account based payment system.
- _Communication_:  In similarity to EMV the scheme does not not depend on external challenge data. Possible "replays" can be dealt with by maintaining a list of [deliberately] short-lived received authorizations.

Although not a part of this specification, the underpinning authorization scheme also opens the door to non-direct payments, including secure (tokenized) versions of Card-on-File. 

### Prerequisite - Payment Credentials
The scheme outlined here depends on that the **User** already has received a payment credential from the **Issuer** in an _enrollment process_.   A payment credential is supposed to at least contain the following elements:
- A **User** specific (unique) FIDO key for _signing_ payment authorization data.
- An `accountId` associated with the FIDO signature key.
- An **Issuer** specific (shared) public key for _encrypting_ payment authorization data.
- An `issuerId` in the form of a BIN number or a URL associated with the **Issuer**.

### Detailed Operation
The different steps in following state diagram are described in this section.
![state-diagram](https://user-images.githubusercontent.com/8044211/107975497-7dca2f00-6fb8-11eb-9e3d-08d1fff16ddf.png)
Note: this specification does not elaborate on the return data from the **Issuer** etc.
 
**1.** The **Merchant** creates payment request data (PREQ) and invokes the payment request API with it:
```javascript
{
  "paymentMethod": "SPC",
  "id": "7040566321",
  "payee": "Rocket Shop",
  "amount": "435.00",
  "currency": "USD"
}
```
Note: the JSON object above outlines the conceptual data before being translated into a compliant Payment Request call.

**2.** The **Browser** responds with PREQ rendered in a user interpretable fashion:
![browser-display](https://user-images.githubusercontent.com/8044211/107975556-92a6c280-6fb8-11eb-97fa-aa4337722d20.png)
_Non-normative UI_.

**3.** The **User** authorizes the request with a fingerprint, PIN, or similar.

**4.** After successful user authorization, the **Browser** _MUST_ perform the following _internal steps_.

**4.1** Calculate a Base64Url-encoded SHA256 hash of PREQ, canonicalized according to [RFC8785](https://tools.ietf.org/html/rfc8785).  Expressed as a formula: BASE64URL(SHA256(RFC8785(PREQ))). Applied on sample data, the result of this operation should be `"APznrD_LxOZGsN0kPnEKODbaxqDVMdPQkIuBWozGqWM"`.

**4.2** Create an authorization data object (AD) with data needed for processing by the **Issuer**:
```javascript
{
  "requestHash": {
     "algorithm": "S256",
     "value": "APznrD_LxOZGsN0kPnEKODbaxqDVMdPQkIuBWozGqWM"
  },
  "payeeHost": "spaceshop.com",
  "accountId": "FR7630002111110020050014382",
  "timeStamp": "2021-02-15T10:32:35+01:00"
}
```
- `requestHash` cryptographically binds PREQ to AD. 
- `payeeHost` is picked-up by the **Browser**.
- `accountId` denotes the actual account number to be debited and is retrieved from the payment credential.  The  sample uses a French IBAN number but it could equally well be a credit card PAN.
- `timeStamp` is generated internally.

**4.3** Using the FIDO signature key, sign AD using [JWS/CT](https://tools.ietf.org/html/draft-jordan-jws-ct-02) resulting in the following completed AD object:
```javascript
{
  "requestHash": {
    "algorithm": "S256",
    "value": "APznrD_LxOZGsN0kPnEKODbaxqDVMdPQkIuBWozGqWM"
  },
  "payeeHost": "spaceshop.com",
  "accountId": "FR7630002111110020050014382",
  "timeStamp": "2021-02-15T10:32:35+01:00",
  "signature": "eyJhbGciOiJFUzI1NiIsImp3ayI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IjZCS3hwdHk4Y0ktZXhEekNraC1nb1U2ZFhxM01iY1kwY2QxTGFBeGlOclUiLCJ5IjoibUNiY3ZVem00NGozTHQyYjVCUHlRbG9ROTF0ZjJEMlYtZ3plVXhXYVVkZyJ9fQ..tZ9bany1FCd7Be9VsJuPcnr-bbGVB-v97Glq5qSkFotielHn2AY6c9mwEMg60F9tRC7bDEvy8LJhKSKRA2tFzA"
}
```
Although not visible in the listing above (due to JWS' Base64Url-encoding requirements on JWS header data), AD signatures _MUST_ provide a key identifier.  In the sample, the following JWK holding the public key was used:
```javascript
{
  "kty": "EC",
  "crv": "P-256",
  "x": "6BKxpty8cI-exDzCkh-goU6dXq3MbcY0cd1LaAxiNrU",
  "y": "mCbcvUzm44j3Lt2b5BPyQloQ91tf2D2V-gzeUxWaUdg"
}
```

**4.4** For privacy and security reasons, the completed AD object _MUST_ be encrypted by a public key provided by the **Issuer** through the payment credential.  This specification builds on JWE ([RFC7516](https://tools.ietf.org/html/rfc7516)) using the compact serialization mode.  The selection between an RSA or ECDH encryption scheme is at the discretion of the **Issuer**.  AD objects _MUST_ be JSON-serialized to UTF-8 before being encrypted.  The resulting JWE string would look something like this (_with line breaks for display purposes only_):
```code
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.OKOawDo13gRp2oja
HV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGeipsEdY3mx_etLbb
WSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDbSv04uVuxIp5Zms
1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaVmqgfwX7XWRxv2
322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8860ppamavo35
UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi6UklfCpIMfIj
f7iGdXKHzg.48V1_ALb6US04U3b.5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu
6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A.XFBoMYUZodetZ
dvTiFvSkQ
```
To simplify documentation, the JWE string is subsequently referred to as `"JWE-Encrypted-AD"`.

_Note that due to the end-2-end encryption scheme, the domain constraints usually associated with FIDO, do not apply_.

**4.5** Put the encrypted AD into a payment response object (PRES) together with clear text data needed for payment "backend" processing:
```javascript
{
  "encryptedAuthorization": "JWE-Encrypted-AD",
  "issuerId": "https://somebank.fr/payment"
}
```

**5.** The **Browser** returns PRES to the **Merchant**.

**6.** The **Merchant** puts its original PREQ and the received PRES into an authorization request object (AREQ):
```javascript
{
  "paymentRequest": {
    "paymentMethod": "SPC",
    "id": "7040566321",
    "payee": "Rocket Shop",
    "amount": "435.00",
    "currency": "USD"
  },
  "encryptedAuthorization": "JWE-Encrypted-AD",
  "issuerId": "https://somebank.fr/payment"
}
```

**7.**  The **Merchant** sends AREQ to its **Acquirer** service.  _Note that authentication of AREQ is out of scope for this specification_.

**8.** The **Acquirer** validates the authenticity and syntax of the received AREQ as well as looking up the **Issuer** through the supplied `issuerId`.

**9.** The **Acquirer** augments the received AREQ with additional data (like **Merchant** collection account), and forwards the completed object to the designated **Issuer**.  _Note that authentication of AREQ is out of scope for this specification_.

**10.** The **Issuer** _MUST_ now perform a number of steps to verify the correctness of the received AREQ. With respect to the **User** the following steps are the most significant:

**10.1** Decrypt the `encryptedAuthorization` object in AREQ using the **Issuer** specific (but shared) decryption key.  Set result as AD.

**10.2** Retrieve the signature key declared in the JWS header of the AD `signature` element.  Abort if unknown.

**10.3** Verify that the retrieved signature key is associated with the AD `accountId` element.  Abort on failure.

**10.4** Validate the AD signature using the retrieved signature key.  Abort on failure.

**10.5** Perform the same calculation as specified in **4.1** on the received `paymentRequest` object and compare the result with the AD `requestHash`.  Abort on failure.

**10.6** Check that the AD `timeStamp` element is within limits. Tentative: _CurrentTime_ - 600s < `timeStamp` < _CurrentTime_ + 60s.  Abort on failure.

**10.7** To cope with possible replays, check that the received AD has not already been utilized.  Ideally, the **Issuer** should support idempotent operation to facilitate benign transaction retries.

Available funds also needs to be validated but that is out of scope for this specification.
@equalsJeffH @nadalin @rlin1 

Please view or discuss this issue at https://github.com/w3c/webauthn/issues/1570 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 15 February 2021 17:10:53 UTC