Skip to content

Commit

Permalink
docs(0011): use PSK in IPR
Browse files Browse the repository at this point in the history
depends on #166
* change the IPR format to only include a packet and condition
* recommend using a PSK implementation to generate the payment request

resolves #138
  • Loading branch information
emschwartz committed Mar 22, 2017
1 parent 589dd96 commit ab8ef4c
Showing 1 changed file with 26 additions and 90 deletions.
116 changes: 26 additions & 90 deletions 0011-interledger-payment-request/0011-interledger-payment-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,23 @@

## Preface

This document specifies the Interledger Payment Request (IPR) data format. It is intended for use in protocols in which the recipient of an Interledger payment first communicates a request for payment to the sender.

This document provides an example flow to illustrate how IPR could be used in a higher level protocol. This document also details a method for generating Interledger transfer conditions that enable recipients to use the condition as an integrity check on incoming transfers and packets, without maintaining state about all outstanding payment requests.
This document specifies the Interledger Payment Request (IPR) transport protocol. It is an end-to-end protocol in which the recipient of an Interledger payment first communicates a request for payment to the sender. This document also details how the recipient can verify that incoming payments match previously created requests without storing a history of all outstanding requests.

## Introduction

### Motivation

The Interledger Protocol uses holds based on Crypto Conditions and expiries to ensure a sender's funds are delivered to the destination or returned to the sender's account.
The Interledger Protocol uses holds based on cryptographic conditions and expiries to ensure a sender's funds are delivered to the destination or returned to the sender's account.

One approach is for conditions to be generated and fulfilled by the receivers of Interledger payments. When the sender of the payment receives the fulfillment, they can be certain that the recipient received the payment.

Receiver-generated conditions are also useful in building non-repudiable application layer protocols. The recipient would generate the condition and cryptographically sign a statement that the preimage of the specified hash indicates the sender has settled a particular debt. The recipient would only disclose the hash preimage upon receipt of payment. Once the sender gets the preimage, they would be able to demonstrate to 3rd parties that they paid the recipient using the preimage and the original signed statement.

A common approach is for conditions to be generated and fulfilled by the receivers of Interledger transfers. This document specifies one such approach that enables receivers to verify that an incoming transfer matches a payment request they previously created, without the receiver needing to keep a record of all outstanding payment requests.
In cases where non-repudiability is unnecessary, the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) transport protocol may be preferrable because it does not require end-to-end communication for each payment.

### Scope

This document defines a suggested format for representing payment requests and an approach for receiver-generated conditions. Protocols built on top of Interledger MAY use the format provided here, though they can use any format they choose instead.
This document defines a suggested format for representing payment requests and an approach for receiver-generated conditions. Protocols built on top of IPR MAY use the format provided here, though they can use any format they choose instead.

While this document also provides a recommendation for generating transfer conditions, conditions are often opaque to all parties aside from the receiver. Receivers MAY use any condition they choose, however this specification includes best practices that receivers SHOULD take into consideration.

Expand All @@ -26,70 +28,31 @@ This document does **not** specify how payment requests are communicated from a

Interledger Payment Requests are intended to be used in higher-level protocols as follows:

1. Recipient creates the payment request and generates the condition using one of algorithms outlined in [Condition Generation](#condition-generation).
2. Recipient communicates the payment request to the sender.
3. Sender generates an ILP Packet from the payment request.
4. Sender prepares a local ledger transfer to a connector, held pending the execution condition given in the payment request and with the ILP Packet attached.
5. Connectors forward the payment until it reaches the recipient.
6. Recipient receives a notification of a held transfer with an ILP Packet attached.
7. Recipient checks the transfer amount against the ILP Packet and checks the payment request has not expired.
8. Recipient regenerates the condition and fulfillment from the ILP Packet attached to the incoming transfer, using the same algorithm as before, and fulfills the transfer's condition. If the condition generated does not match the one in the transfer it means that the ILP Packet has been tampered with and the transfer goes unfulfilled.
1. Recipient creates the ILP Packet and generates the condition using one of algorithms outlined in [Condition Generation](#condition-generation).
2. Recipient communicates the ILP Packet and condition to the sender.
3. Sender prepares a local ledger transfer to a connector, held pending the execution condition provided by the recipient and with the ILP Packet provided by the recipient attached.
4. Connectors forward the payment until it reaches the recipient.
5. Recipient receives a notification of a held transfer with an ILP Packet attached.
6. Recipient checks the transfer amount against the ILP Packet and checks the payment request has not expired.
7. Recipient regenerates the condition and fulfillment from the ILP Packet attached to the incoming transfer, using the same algorithm as before, and fulfills the transfer's condition. If the condition generated does not match the one in the transfer it means that the ILP Packet has been tampered with and the transfer goes unfulfilled.

## Payment Request Specification

### JSON

```json
{
"address": "ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af",
"amount": "10.25",
"condition": "cc:0:3:hd5x8kpaDDLQu-KqMyCrlsg5QJ9g9qaFr9ytTwqyCsw:32",
"expires_at": "2016-08-16T12:00:00Z",
"data": {
"re": "dinner the other night"
},
"additional_headers": "asdf98zxcvlknannasdpfi09qwoijasdfk09xcv009as7zxcv"
"packet": "AYGyAAAAAAAAAAo6ZXhhbXBsZS5pbHBkZW1vLmJsdWUuYm9iLkR0azB1Y3U4OHFJQm1iRFEwelhxa2lkTWZhOHRQakJZUW1QU0svMS4wCk5vbmNlOiBvdHotT25NbThURHo3NThGYWY4cXRRCkVuY3J5cHRpb246IGFlcy0yNTYtZ2NtIEdFRUR0MXk4NmlkSVFMa2xWbnpiRWcKCuXPle38YB02KVAHPtVNqRgP7Ok6D-QRAA",
"condition": "oHeVy7WAEhrJF3U4NeLt5QT6Fr3L2YeBCmB88Xu9sEY"
}
```

| Field | Type | Short Description |
|:--|:--|:--|
| `address` | ILP Address | Request-specific address |
| `amount` | Decimal String | Amount requested by the recipient |
| `condition` | Crypto Condition | Execution condition for the payment |
| `expires_at` | [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) Timestamp | Expiry of the request |
| `data` | Object | Data to be included in the ILP Packet |
| `additional_headers` | Base64 String | Additional headers for the ILP Packet |

#### address

The ILP Address the payment is to be routed to. This SHOULD be a request-specific address in the form `<account address>.<request ID>`, such as `ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af`.

When using the recommended [Condition Generation](#condition-generation) method, account addresses MUST include a request-specific portion to ensure that conditions are unique per-request.

#### amount

The amount requested by the recipient, such as `10.25`, denoted in the assets of the recipient's ledger.

The amount MUST be rounded based on the precision supported by the recipient's ledger.

Recipients SHOULD ignore incoming transfers whose amounts are less than the amount specified in the payment request. Recipients MAY ignore incoming transfers whose amounts are greater than requested.

#### condition
| `packet` | [Base64-Url](https://en.wikipedia.org/wiki/Base64#URL_applications) Encoded ILP Packet | ILP Payment packet including the destination address and amount |
| `condition` | 32 Bytes, Base64-Url Encoded | Execution condition for the payment |

The Crypto Condition to be used as the execution condition for the payment. See [Condition Generation](#condition-generation) for recommendations the recipient MAY use for creating the condition.

#### expires_at

The timestamp when the request expires. Recipients SHOULD NOT fulfill the conditions of incoming transfers that arrive after the expiry has passed. Senders MAY use the request expiry to determine the expiry of the transfer they put on hold for the first connector.

#### data

An arbitrary JSON object that should be included in the ILP Packet's user data header.

#### additional_headers

Additional binary headers, encoded as a base64 string, that should be included in the ILP Packet. The sender SHOULD treat these as opaque.
In IPR the sender only uses the `account` and `amount` from the ILP packet and MUST treat the `data` as opaque. The sender MUST NOT modify the ILP packet at all, or the recipient will reject the payment.

### Binary

Expand All @@ -99,42 +62,15 @@ Additional binary headers, encoded as a base64 string, that should be included i

It is RECOMMENDED that recipients use a Message Authentication Code (MAC) of the details of their payment request in the condition so that the condition can be used as an integrity check on incoming transfers. This allows a recipient to ensure that a transfer matches a request they previously generated, without the recipient needing to maintain a log of all outstanding payment requests.

Recipients MUST include a payment request-specific component in the account ILP Address to ensure that conditions are unique per-request.

### Preimage Condition from JSON

This is how a [PREIMAGE-SHA-256 Crypto Condition](../0002-crypto-conditions) MAY be generated using an HMAC of a JSON-encoded Interledger Payment Request:
Recipients MUST include a payment request-specific nonce in the ILP packet to ensure that conditions are unique per-request.

1. The recipient takes the `address`, `amount`, `expires_at`, and `data` fields from the payment request. Any fields that are used for routing are probably not important to perform an integrity check upon, because they may be modified in transit and there is no further use for them once the ILP packet is received attached to an incoming transfer.
2. The recipient uses a canonical encoding, such as [Canonical JSON](https://www.npmjs.com/package/canonical-json) to create a stringified version of `1.`
3. The recipient uses a secret key to create an HMAC of `2.`
4. The recipient uses the digest of `3.` as the preimage of a [PREIMAGE-SHA-256 Crypto Condition](../0002-crypto-conditions).
### Using a PSK Implementation

The following snippet shows how this can be done in JavaScript:
It is RECOMMENDED that recipients use an implementation of the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) protocol to generate the packet and condition for IPR.

```js
const crypto = require('crypto') // Node.js crypto
const stringify = require('canonical-json') // https://www.npmjs.com/package/canonical-json
const cc = require('five-bells-condition') // https://www.npmjs.com/package/five-bells-condition
The recipient should follow BOTH the steps normally followed by the recipient AND sender. The recipient should generate the secret in the normal fashion but MUST NOT share it. The recipient then follows the steps the sender would do in a PSK payment to create the packet and condition from the secret and the payment parameters.

const RECIPIENT_HMAC_KEY = Buffer.from('/4t65RDqth7/rxI0j+MqtQyv04Y8mzUCMhAAofhDQIY=', 'base64')
const requestString = stringify({
"address": "ilpdemo.red.bob.b9c4ceba-51e4-4a80-b1a7-2972383e98af",
"amount": "10.25",
"expires_at": "2016-08-16T12:00:00Z",
"data": {
"re": "dinner the other night"
}
})
For details, see the [Pre-Shared Key Pseudocode](../0016-pre-shared-key/0016-pre-shared-key.md#pseudocode).

const hmac = crypto.createHmac('sha256', RECIPIENT_HMAC_KEY)
hmac.update(requestString)
const cryptoCondition = new cc.PreimageSha256()
cryptoCondition.setPreimage(hmac.digest())
Note the recipient's use of a PSK implementation does not matter to the sender, because the sender treats the `data` in the ILP packet as opaque and uses the condition from the recipient without needing to know how the fulfillment is generated.

const condition = cryptoCondition.getConditionUri()
console.log(condition) // Prints cc:0:3:BC-mUTFsZYXd89QQDiocToUWbQ-XYTh5H5lFQNwhDWE:32

const fulfillment = cryptoCondition.serializeUri()
console.log(fulfillment) // Prints cf:0:aW9EFFuRmltvYcs2ESExvf-0pbzBhUGgpqKT-oUBuuU
```

0 comments on commit ab8ef4c

Please sign in to comment.