Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSKv2 #351

Merged
merged 2 commits into from
Jan 25, 2018
Merged

PSKv2 #351

merged 2 commits into from
Jan 25, 2018

Conversation

emschwartz
Copy link
Member

Transport protocol that handles encryption, fulfillment and condition generation, end-to-end quoting, and chunked payments.

There are still a number of TODOs scattered throughout the spec but I figured we might as well get the discussion going.

Curious to hear if you think all the description up front is helpful or makes the protocol seem more complicated than necessary. Should we cut that down, move more to the appendix, or add more explanation?

Please leave comments inline (as opposed to in the conversation view) so we can keep discussion threads together.

---
# Pre-Shared Key V2 (PSKv2) Transport Protocol

This spec defines version 2 of the Pre-Shared Key Transport Protocol. PSKv2 implements end-to-end data encryption, condition and fulfillment generation, [End-to-End Quoting](#end-to-end-quoting) and [Chunked Payments](#chunked-payments). The approach to chunked payments is inspired by the Internet's Transmission Control Protocol (TCP).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it also be used for test payments and/or stats collection?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure it can be used for test payments, but would those be any different from normal payments or quotes? If not, I'm not sure it needs to be documented here


When a sender is sending many payments to the receiver, they need a dynamic view of the exchange rate because the rate may change during the course of the interaction. To handle these cases, every PSKv2 payment response includes the amount that arrived in the final transfer to the receiver. Similar to the informational quotes, the sender can use the chunk source amount and the receiver's response to determine and monitor the exchange rate.

**Trusting the Receiver:** With any type of Interledger quoting, whether end-to-end or using ILQP, senders SHOULD judge prices in their source units. Receivers can lie about how much arrives in a test payment for a quote or they could run a connector that happens to have a bad exchange rate to their destination units. Therefore, senders must assume that whatever amount the receiver says has arrived represents the "real" rate of the path.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strictly speaking, in ILQP the receiver cannot lie about how much arrives, unless they run the destination ledger

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"or they could run a connector that happens to have a bad exchange rate to their destination units"

Copy link
Contributor

@michielbdejong michielbdejong Dec 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

running one such connector wouldn't make a difference, the network would route around it using other connectors to reach the destination ledger

Copy link
Contributor

@michielbdejong michielbdejong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments inline.

Request and response data are encrypted using [AES-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) with a 16-byte Initialization Vector (IV).

#### Encryption Envelope

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strongly recommend using the format from RFC8188 rather than making up our own.
See: http://httpwg.org/specs/rfc8188.html#header

This would make PKS over ILP over HTTP compliant with RFC8188.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would make PKS over ILP over HTTP compliant with RFC8188.

This wouldn't be true if we bring back the ILP packet, because the HTTP body will be the ILP packet and this envelope will be inside there. I think it's more important to be consistent with the rest of our stack (using OER)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. This makes me sad 😢

  1. Put ILP Address in HTTP headers use RFC 8188 as is. Profit
    OR
  2. Put ILP Address in packet in body. Invent everything from scratch.


### Quotes

#### Quote Request
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@justmoon brought up the point: do we need purely informational quotes or should these be removed?

If on a normal payment the receiver will tell you how much arrived, the sender could just use an actual payment to determine the rate for the path. It seems like the main problem would be that the rounding error could get quite large if you send a super tiny payment first. That would also mean you need to pay a small amount for quotes, but that might not be the worst thing.

Thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If a quote is a "Payment Chunk Request" with the "Chunk Minimum" set to the max uint64, then it won't actually cost anything.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a great point. I'm going to update it to remove the quote request and response types. It's very cool that that means we can use the same request/response/error packets to handle quotes, one-off payments, and chunked payments.

| Payment Received | 8-Byte UInt (UInt64) | The amount the receiver has received thus far for the payment (the sum of fulfilled chunks) |
| Chunk Received | 8-Byte UInt (UInt64) | The amount the receiver received for the given chunk |
| Application Data | [OER Variable-Length Octet String](http://www.oss.com/asn1/resources/books-whitepapers-pubs/Overview%20of%20OER.pdf) | User data carried along with the payment |
| Extensions (E) | 1-Byte UInt (UInt8) | Always `0`. Reserved for future use to indicate the presence of extension fields |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extensions must be ignored if you don't implement the logic to handle OER extensions. "Always 0" implies that a parser should verify that the extensions byte is zero, which would actually break extensibility.

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|T| Payment ID | Seq | Payment Rcved | Chu-|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|nk Rcved | Application Data |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe include the length prefix for the application data in this diagram

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to but I'm not sure how to illustrate a variable length prefix...

| Extensions (E) | 1-Byte UInt (UInt8) | Always `0`. Reserved for future use to indicate the presence of extension fields |
| Junk Data | 0-? Bytes | _(Optional)_ Extra data included to obscure what the ILP payment is for. Receivers MUST ignore this data. Senders SHOULD include either the maximum or a random number of empty (0) bytes |

**TODO:** should we add a source address for refunds? (that should only be necessary in extreme cases)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that refunds are an uncommon enough case that psk shouldn't account for them.

### Payment Chunks

#### Payment Chunk Request
All PSKv2.0 requests and responses encode their data in the same byte format, though the meanings of the fields differ depending on whether it is a request or a response.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation for defining a binary packet format?

Copy link
Member Author

@emschwartz emschwartz Dec 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. We need some encoding format
  2. There's no reason to have the overhead of including the keys as strings, since we know what they are up front
  3. Might as well make it consistent with the rest of our stack by using OER
  4. We don't need this layer to have the "extensible by anyone" quality that a string-based key-value map would provide. You can stick whatever additional data you want in the Application Data part

What else would you propose?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think pushing OER (or any binary encoding) up to this layer is a mistake because it is passed opaquely through all intermediaries so the interoperability requirement is significantly diminished.

Only the sender and receiver need to interpret this so it's more likely to be implemented by clients than connectors and something more common at this layer (like JSON) just seems like a better natural fit.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is only the sender and receiver that need to implement this, but the sender and receiver also need to support OER to send and receive ILP packets.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of different layers of the stack is likely to come from different libraries and a sender/receiver is also likely to support multiple transport layer protocols so I don't think using OER just because it's used in ILP is a good argument.

That said, I think it makes more sense at to do this at the transport layer than the application layer.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I think many application layer protocols will use JSON or some other "friendlier" encoding. If that's the case though, that's all the more reason to have as minimal byte overhead as possible in the transport layer so the application layer can send more data.

In most cases I would expect the ILP and PSK2 modules to be implemented roughly at the same time, by the same people. If you're building a sender or receiver right now you'll need both, and I think that is a good place to get started for an implementation.


**Trusting the Receiver:** With any type of Interledger quoting, whether end-to-end or using ILQP, senders SHOULD judge prices in their source units. Receivers can lie about how much arrives in a test payment for a quote or they could run their own ledger and connector that happen to have a bad exchange rate. Therefore, senders must assume that whatever amount the receiver says has arrived represents the "real" rate of the path.

**Linear Exchange Rates and Fixed Destination Amount Quotes:** End-to-End Quoting works best with linear exchange rates. If the rate for a path is linear, it can be determined by sending a single test payment for an arbitrary amount. The source amount required to deliver a fixed destination amount can be easily determeined by dividing the source amount by the rate. If exchange rates are non-linear, gettting an informational quote for a fixed destination amount would require a binary search, which is not ideal. However, in an Interledger network primarily or exclusively suited to small payments, there should be little reason for rates to vary by payment size.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo determeined and gettting


**Fluctuating Exchange Rates:** The rate from the sender to the receiver may change due to legitimate market movements during the course of a payment. Senders SHOULD monitor the rate changes and they may not want to send payments that are significantly larger than their bandwidth (this could be compared to how downloading a 4K movie over a dial-up connection is possible but would not make for a good experience).

**Malicious Connectors:** Exchange rates can also change for illegitimate reasons, such as connectors driving up the price mid-payment. Ultimately, the main force keeping connectors' rates low is competition. If there is only one path, the connectors do not need to do anything sneaky to drive up the rate. If a sender has multiple options for the connector they send through, they may want to use [Multi-Path Chunked Payments](#multi-path-chunked-payments) to automatically switch paths if one becomes more expensive.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is only one path, the connectors do not need to do anything sneaky to drive up the rate.

What do you mean exactly with "sneaky"? If there is only one path, there are many sneaky ways how a connector could steal from a sender/receiver (e.g. increasing the rate half-way through the payment).

Copy link
Member Author

@emschwartz emschwartz Dec 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point is that there's no benefit to for them to do this in a sneaky way. They can simply charge a higher rate up front, since there's nowhere else to go. Sending a chunked payment is no worse than sending a single payment in this case.

Copy link

@dappelt dappelt Dec 13, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If such a connector sets a higher rate up front, a customer could simply not send the transfer at all. By providing a cheaper rate initially and increasing the rate half-way through the sender is trapped: Either he finishes the payment at the higher rate or he asks the receiver for a refund, for which the connector would also apply the higher rate. Either way, the sender is trapped.

I propose to drop the sentence If there is only one path, the connectors do not need to do anything sneaky to drive up the rate.


### Informational Quote

1. Sender creates a packet with a type of 1, a random Payment ID, a Sequence number of 0, a Payment Amount of 0, a Chunk Amount set to 18446744073709551615 (the maximum value) and [encrypts it](#encryption-envelope).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By payment amount and chunk amount, do you mean transfer amount and packet amount? Why would anyone forward such a payment if the transfer amount is zero?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it means the fields called Payment Amount and Chunk Amount in the PSK data packet defined below


### Single Payment

1. In the case of a fixed source amount payment, sender multiplies the source amount by the rate to determine the Payment Amount. In the case of a fixed destination amount payment, sender divides the destination amount by the rate to determine the amount for their outgoing transfer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer to use ilqp in this case

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that will realistically be an option. As I've said before, ILQP doesn't work unless every connector supports it and/or every connector pushes up to date price information to one another very frequently. This is a high burden to place on the connectors that isn't necessary if senders can get the same information in a different way that doesn't require a specific feature from the connectors.

Copy link
Contributor

@michielbdejong michielbdejong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should rename this to PSK-chunked, so that it doesn't imply deprecating current interledger. That's the nice thing about having an hourglass stack; multiple things can be built on top of Interledger's chained hashlocks.

@emschwartz
Copy link
Member Author

I do think we should deprecate the ILP packet type with the amount in it and ILQP.

We are trying to create interoperability. Having multiple different protocols at the core interoperability layer means more that everyone needs to agree on and implement and I don't think they provide enough substantive benefit to merit the cost. Someone could build another set of protocols using the same chaining idea as ILP, but it's not our concern because it wouldn't actually be interoperable.

Forwarded payments are a much simpler and more generic building block, as evidenced by the fact that you can build the three different flows described in this spec using only that one connector feature. I would strongly argue that this is the right building block to standardize on at the Inyerledger layer.

@michielbdejong
Copy link
Contributor

more that everyone needs to agree on and implement

I think that's an illusion. For ledgers and ledger plugins, it doesn't matter because they treat the packet as opaque. The question is how connectors can be interoperable with each other.

Of course connectors need to talk each other's complex language to do complex things like path finding, liquidity management, and dynamic pricing. So yes, interoperability between connectors will be hard, regardless of whether we make the interledger layer a thick layer or a thin layer.

The beauty is that the ledger requirements are low: to become interoperable, a ledger just needs to support timed sha256 hashlock escrow + messaging, and if the ledger supports both (or the messaging is added in a way that makes sense for that ledger) then someone needs to write a plugin for that ledger, done. That doesn't change by moving quoting and path finding up or down in the stack.

@adrianhopebailie
Copy link
Collaborator

I do think we should deprecate the ILP packet type with the amount in it and ILQP.

I agree that we should deprecate the ILP packet with the amount (we need one or the other in the ILP layer).

I'm not sure about ILQP. It is not a fundamental part of ILP but that doesn't mean it may not be used between connectors. i.e. It's not part of ILP but it may still be used so it's probably out of place in the ILP packet definitions but not entirely useless

@michielbdejong
Copy link
Contributor

The destination amount can easily be considered to be implied by the transfer amount, so I wouldn't be so opposed to moving it into the packet data. We could also say ILQP is part of the connector-to-connector (and/or sender-to-connector) layer, the fact that we have ilp-packet types for ILQP just makes it easy to send such messages as a payload to some other account on the same ledger

@emschwartz
Copy link
Member Author

emschwartz commented Dec 13, 2017

Ok I actually don't care if we deprecate ILQP, but it is important to agree that it is not required and you cannot expect all other connectors to have implemented it (as was the assumption before).

I'm coming at this from the perspective of someone who wants to reimplement ILP in another language. My question is what must I write to make my implementation interoperable. For a minimal connector (with configured routes) you need one or two ledger protocols and ILP forwarded payments/fulfillments/errors. For a sender or receiver you need at least one ledger layer protocol, ILP forwarded payments/fulfillments/errors, a transport protocol and an application layer protocol.

(Also, if some connectors support ILQP but you can't assume that all do, we need to deprecate and replace both IPR and PSKv1. Neither work if you cannot quote and they don't include built-in functionality to handle quoting. So while they might still work if you're a connector connected to a couple of specific connectors, it would not be advisable to implement and use those protocols because it's not likely to work in most cases.)

- A 32-byte random or pseudorandom shared secret
- The receiver's ILP address

The receiver MAY generate the shared secret from a longer-term secret and a nonce that they put into the ILP address they give to the sender, as described in [PSKv1's Appendix A: Recommended Algorithm for Generating Shared Secrets](./0016-pre-shared-key/0016-pre-shared-key.md#appendix-a-recommended-algorithm-for-generating-shared-secrets).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] longer-term secret and a nonce that they put into the ILP address [...]

Consider rewording to make it clear that only the nonce goes into the ILP address, not the long-term secret.


### Encryption

In PSKv2.0, request and response data is encrypted using [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) with a 16-byte Initialization Vector (IV) and a 16-Byte Authentication Tag.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the size of the Authentication Tag. This is an excerpt from the NIST publication on GCM:

The bit length of the tag, denoted t, is a security parameter, as discussed in Appendix B. In general, t may be any one of the following five values: 128, 120, 112, 104, or 96. For certain applications, t may be 64 or 32; guidance for the use of these two tag lengths, including requirements on the length of the input data and the lifetime of the key in these cases, is given in Appendix C.

So, a 16 byte Authentication Tag can be considered secure.

Another source recommending a 16 byte authentication tag is Cryptography Engineering: Design Principles and Practical Applications (p. 309):

“Therefore, although it is possible to reduce the size of the authenticator for GCM from 128 bits to something less, we recommend not doing so. Our recommendation is to only use GCM with the full 128-bit authentication tag.”

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, and I think the recommendation is also to use a 16-byte IV, right?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NIST publication recommends a bit length of 96 for the IV:

For IVs, it is recommended that implementations restrict support to the length of 96 bits, to promote interoperability, efficiency, and simplicity of design.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the Rust, Go, and Java implementations either require or recommend using a 96-bit IV as well.


### Condition and Fulfillment Generation

The sender and receiver generate the condition and fulfillment from the pre-shared key that gives this protocol its name and the data that is sent with each payment. The fulfillment is an HMAC (using SHA256) of a key derived from the shared secret and the encrypted data.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sender and receiver generate the condition and fulfillment from the pre-shared key that gives this protocol its name and the data that is sent with each payment.

If the reader is not already familiar with PSK, this sentence is confusing. Consider splitting this sentence up. The fulfillment is computed from the pre-shared key and payment data. The condition is the SHA256 hash of the fulfillment.


### Informational Quote

1. Sender creates a packet with a type of 1, a random Payment ID, a Sequence number of 0, a Payment Amount of 0, a Chunk Amount set to 18446744073709551615 (the maximum value) and [encrypts it](#encryption-envelope).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

18446744073709551615 -> 264-1

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(the maximum value) -> (max. unsigned 64 bit integer)

7. Receiver generates fulfillment as described [below](#fulfillment-generation) and fulfills the incoming transfer.
8. Sender is notified of the outgoing transfer being fulfilled and decrypts the attached data.
9. Sender determines the exchange rate by dividing the Chunk Amount from the receiver by the chunk's source amount.
10. Sender repeats step 1-8 until the fixed source or destination amount has been sent or delivered, respectively. Sender MAY adjust the chunk amount upwards or downwards in response to successful and unsuccessful payments. The sender SHOULD set the Type to 1 on the last chunk.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sender does not exactly repeat steps 1 to 8. At 1), the sender creates a new packet with the same Payment ID as before, increments Sequence number etc.

2. Sender generates the payment condition as described [below](#fulfillable-condition).
3. Sender sends a payment with the condition from step 2., the encrypted request from step 1. as the data, and an arbitrary source amount.
4. Receiver is notified of the incoming transfer and decrypts the data.
5. Receiver checks that the incoming transfer amount is greater than or equal to the Chunk Amount and that the total that has arrived thus far does not exceed the Payment Amount.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At step 1) a packet with Chunk Amount of 0 is created. So how can this constraint ever be violated:

Receiver checks that the incoming transfer amount is greater than or equal to the Chunk Amount [...]

The incoming transfer would need to have an amount < 0 to violate this constraint.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume for consecutive packets the chunk amount would be greater than 0.

8. Receiver generates fulfillment as described [below](#fulfillment-generation) and fulfills the incoming transfer with the fulfillment and the encrypted response data.
9. Sender is notified of the outgoing transfer being fulfilled and can use the decrypted response to determine the amount that arrived at the destination.

### Chunked Payment
Copy link

@dappelt dappelt Dec 14, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding pseudo-code would make the algorithm for sending chunked payments more clear.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could add that but I think it would be fairly long. It might be better to just read an actual implementation if you want to reimplement it

```
(Numbers represent bytes)

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If numbers represent bytes, this should start counting at 1. Also, it should not start over at 0 but should keep going 10, 11, etc.

Maybe better to use powers of 2 instead.

@michielbdejong
Copy link
Contributor

My question is what must I write to make my implementation interoperable. For a minimal connector (with configured routes) you need [...]

This simplification makes sense for an initial network where all connectors trust all other connectors through configured routes, but I would like to be more ambitious and hope that we can later open up that network, and allow random unvetted connectors to join. Then, we'll need to add back things like quote caching, maybe not on the interledger layer, but on the connector-to-connector layer.

Some of the decisions we are proposing now (e.g. relaxing the definition of ledger prefix and not including a targetPrefix in end-to-end quote responses) affect not only the initial closed network, but also the ability to open that network up later. So it's better to build in "trustlessness" from the start, even if it's not an MVP prerequisite.

Especially since we already have an implementation of it, into which years of work were invested and which does already support quote caching, liquidity routing, and route broadcasts with exchange rate information. And it would be easy to add this new experiment without throwing away all that existing work, right?

@sharafian
Copy link

...and allow random unvetted connectors to join. Then, we'll need to add back things like quote caching

Can you explain why allowing unvetted connectors to join the network requires quote caching?

@michielbdejong
Copy link
Contributor

When information about alternative routes arrives, you want to compare it to information you received earlier. That comparison cannot happen if you didn't cache the information.

SPSP may be used by end-user applications, such as a digital wallet with a user interface for the sender to initiate payments. SPSP clients and receivers use ILP modules to send and receive Interledger payments.

SPSP messages MUST be exchanged over HTTPS.
SPSP is a high-level protocol for applications to build in support for basic Interledger payments. It uses HTTPS to communicate the details necessary to set up an ILP/PSKv2 payment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "basic interledger payments" is confusing now, because an ordinary Interledger payment is a micropayment. This should specify that SPSP is for large payments, especially large peer-to-peer payments like the kind one would use Paypal for.

@@ -33,27 +23,20 @@ Any SPSP receiver will run an SPSP server and expose an HTTPS endpoint called th
* **SPSP Client** - The sender application that uses SPSP to interact with the SPSP Server
* **SPSP Server** - The server used on the receiver's side to handle SPSP requests
* **SPSP Endpoint** - The specific HTTPS endpoint on the SPSP server used for setting up a payment
* **PSK Module** - Software included in the SPSP Client and Server that implements the [Pre-Shared Key](../0016-pre-shared-key/0016-pre-shared-key.md) protocol
* **PSK Module** - Software included in the SPSP Client and Server that implements the [Pre-Shared Key](../0024-pre-shared-key-v2/0024-pre-shared-key-v2.md) protocol

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should say implements "version 2 of the Pre-Shared Key" protocol. Someone reading over the new spec might not notice that this has changed.

9. If the receiver executed the transfer, the sender's SPSP client receives a notification from its ILP module that the transfer has been executed, including the condition fulfillment from the receiver, and notifies the sender that the payment is completed. If the receiver rejected the transfer, the sender's SPSP client receives a notification with the receiver-specified error message detailing why the payment was rejected.
2. The SPSP Endpoint responds with the receiver info, including the PSK version, the receiver's ILP address and the shared secret to be used in PSK.
3. The sender uses PSKv2 with the specified version, destination address and shared secret to pay the receiver. In many cases the payment will be sent in chunks by the PSKv2 module. Senders MAY quote before paying.
4. When the receiver is notified of the first incoming payment, they MAY submit the details to an exteranl system for review to ensure the funds are wanted.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exteranl -> external

3. The sender uses PSKv2 with the specified version, destination address and shared secret to pay the receiver. In many cases the payment will be sent in chunks by the PSKv2 module. Senders MAY quote before paying.
4. When the receiver is notified of the first incoming payment, they MAY submit the details to an exteranl system for review to ensure the funds are wanted.
5. The receiver's PSKv2 module fulfills the incoming payments.
6. The sender's PSKv2 module notifies the SPSP module when the payment is finished or if there is an error.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if there is an error partway through?

@@ -119,8 +103,9 @@ The response body is a JSON object that includes basic account details necessary

| Field | Type | Description |
|---|---|---|
| `psk_versions` | Array of Strings | PSK versions supported by the server (written as `2.0`, indicating the major and minor version) |

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already use webfinger lookup for SPSP, which means we're given a list of URIs. Couldn't we say that each SPSP version uses a single version of PSK, and you just choose the appropriate SPSP version from the webfinger lookup?

For the current spec of this field, how does the sender tell the receiver which protocol they're using?

I don't think it's a good idea to add a second protocol negotiation on top of the one already used for SPSP (the webfinger lookup)

Copy link
Member Author

@emschwartz emschwartz Dec 20, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could. The main argument I see against that would be: what if we add a PSKv2.1 that supports ciphers other than AES-GCM? Would we really need to do a major version bump for SPSP just to support that?

That said, the thing that's weird about including the versions here is that you might want completely different parameters for the other versions. It would be pretty ugly to mash all of the parameters into one JSON object (and make sure the key names don't conflict).


**Malicious Connectors:** Exchange rates can also change for illegitimate reasons, such as connectors driving up the price mid-payment. Ultimately, the main force keeping connectors' rates low is competition. If there is only one path, the connectors do not need to do anything sneaky to drive up the rate. If a sender has multiple options for the connector they send through, they may want to use [Multi-Path Chunked Payments](#multi-path-chunked-payments) to automatically switch paths if one becomes more expensive.

**Trusting the Receiver:** When doing any Interledger payment aside from an atomic exchange of money for goods or services, the sender must trust the receiver to deliver the goods once they receive the payment. With chunked payments, the sender must trust the receiver to deliver the goods and accurately report how much money has arrived on their incoming transfer. A malicious receiver could keep reporting less money having arrived on every transaction in an attempt to extract more money from the sender, but this is no worse a threat than a receiver demanding more money after a non-chunked payment arrives but before they release the goods or services. Since senders will judge merchants' prices by the total source amount they pay, receivers that pull such tricks should develop negative reputations and be avoided.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth pointing out that if the receiver colludes with a connector to have them raise their rates mid-payment, it's no different from the receiver deciding to raise the prices for their goods mid payment. If we don't point that out, then the Malicious Connector attack sounds a lot worse than it is.


## Overview

Like [PSKv1](./0016-pre-shared-key/0016-pre-shared-key.md), PSKv2 uses a secret shared between the sender and receiver to generate the conditions and fulfillments for payments without repeated end-to-end communication. PSKv2 additionally includes support for End-to-End Quoting and Chunked Payments.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kinds of payments will PSK2 be used for? Will we use it for everything, or will micropayments (which we expect to be under the MTU) use a different protocol? A lot of the points in this document are very different depending on whether we're thinking about a large payment or a small payment. Most of the time, it seems like this spec is describing retail-sized payments that require lots of chunking.

If it's also being used for micropayments, I think that deserves more mentioning. For instance, many of the attacks described really don't work against micropayments. And many of the quoting mechanisms aren't needed when you're just sending a 1-chunk micropayment.

8. Sender is notified of the outgoing transfer being fulfilled and decrypts the attached data.
9. Sender determines the exchange rate by dividing the Chunk Amount from the receiver by the chunk's source amount.
10. Sender repeats step 1-8 with the following modifications, until the fixed source or destination amount has been sent or delivered, respectively:
- Sender MUST create a new PSK data packet for each chunk with the same Payment ID and an incremented Sequence number

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the purpose of the sequence number? What should the receiver do if they miss a sequence number? If one of the payments fails, should the sender increment the sequence number, or retry it with the same sequence number? Will an informational quote always have a sequence number of 0, even if it occurs partway through a payment (for example to refresh the exchange rate)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main purpose of the sequence number right now is to allow the sender to ensure that responses match the requests they sent out. Without the sequence, a connector could attach an old response (that would say a lower amount had arrived) to a later fulfillment and the sender wouldn't know the difference.

Originally I thought that receivers should keep track of the sequence numbers to avoid accepting two with the same sequence. @justmoon argued that wasn't necessary because unlike with data, money is fungible so it doesn't matter if the chunks arrive out of order as long as you get the total amount.

I don't think informational quotes need to have a sequence of 0. From the receiver's perspective, there is no such thing as an informational quote, it's just a chunk that arrived with too little money (and potentially an unfulfillable condition).

- Sender MUST create a new PSK data packet for each chunk with the same Payment ID and an incremented Sequence number
- Sender MAY specify the minimum Chunk Amount the receiver should accept for each transfer based on the calculated path exchange rate
- Sender MAY adjust the transfer amount upwards or downwards in response to successful and unsuccessful payments, or to deliver a fixed destination amount exactly
- Sender SHOULD set the Type to 1 on the last chunk

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the sender didn't set the type to 1, but the chunk they sent completes the payment's destination amount. Would the receiver reject subsequent payments? Do they have a way of telling the sender that they've completed the payment already?

What if the sender sets the type to 1, but the destination amount isn't complete yet? Does the receiver just take it to mean that the sender won't pay the rest of what they owe?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the sender didn't set the type to 1, but the chunk they sent completes the payment's destination amount. Would the receiver reject subsequent payments?

Yes, the receiver should reject further chunks once the destination amount specified has arrived

What if the sender sets the type to 1, but the destination amount isn't complete yet?

The receiver should assume that means the sender won't send any more. I think the PSK implementation would need to turn it over to the Application Layer protocol at that point to figure out what to do


### Encryption

Request and response data are encrypted using [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) with a random 12-byte Initialization Vector (IV).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and 16 byte auth tag

@emschwartz
Copy link
Member Author

When information about alternative routes arrives, you want to compare it to information you received earlier. That comparison cannot happen if you didn't cache the information.

@michielbdejong this seems orthogonal to PSKv2, no?

@emschwartz
Copy link
Member Author

(@justmoon PSKv2 is currently slated to use IL-RFC number 24 but I can change that to 25)

@emschwartz emschwartz merged commit bae5e28 into master Jan 25, 2018
@emschwartz emschwartz deleted the es-psk-v2 branch January 25, 2018 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants