rust-payjoin icon indicating copy to clipboard operation
rust-payjoin copied to clipboard

Ensure key rotation is accounted for in server and client implementations

Open DanGould opened this issue 1 year ago • 5 comments

Keys should be able to be expired and clients should handle fetching new ones.

Keys should be cached in payjoin-cli too rather than fetched each time, but that's a different issue.

DanGould avatar Dec 27 '24 02:12 DanGould

@DanGould I have a few questions consigning this .

  • what do you think is a reasonable expiration time , i'm thinking 2weeks to a month
  • are we okay with base64 encoding key binary data , that way we can return it inside a json and then add properties like expiration time

zealsham avatar Jul 23 '25 14:07 zealsham

what do you think is a reasonable expiration time

  • TBH I think 6 months is more like it, especially if Bitcoin Core clients cache the OhttpKeys from having sent a payjoin. I think we'd want times to overlap so that old keys gave some time for new ones to be fetched by clients that can't fetch out of band by depending on WebPKI

are we okay with base64 encoding key binary data , that way we can return it inside a json and then add properties like expiration time

Rats. In BIP 77 we support RFC 9540 key fetching which just returns data as binary in a response with content-type "application/ohttp-keys". Ideally we'd continue to comply with that spec but I'm not sure how to deliver the expiration. I feel like @nothingmuch might have an idea.

DanGould avatar Aug 05 '25 01:08 DanGould

Cache control header on RFC 9540 responses

nothingmuch avatar Aug 05 '25 10:08 nothingmuch

There we go. I knew there was some straightforward way that'd just make sense that would require encoding / json complexity

DanGould avatar Aug 05 '25 20:08 DanGould

see also:

  • https://www.ietf.org/rfc/rfc9458.html#name-signaling-key-configuration
  • https://www.ietf.org/rfc/rfc9458.html#section-7-3

for our use case, since the payloads are uniform there's not much that can be learned from partitioned key configs, short IDs still link otherwise independent requests by the same client, so partitioning can't link those any more than they already would be.

a more serious concern is that stale configs or more generally lack of OHTTP key consistency can leak that two short IDs are related to the same session, which is a minor censorship risk.

the most serious concern i can think of is that lack of key consistency can link a receiver's sessions to each other. here too i don't see much that a malicious payjoin directory can do with this information.

so this implies to me that it's OK to have multiple active configs in paralle. i think the desired behavior would be to allow for some grace period, maybe 24 hours after nominal expiry, where a new configuration is published, and the old one is still accepted.

practically this would mean serving only one key & params at a time, but keeping their IDs different, and then just switching over once the old one has expired.

receivers who see that a key config will expire soon can still use the new one, either setting EXP to the same value, or if that's too short an interval, then for up to 24 hours where the sender can still use the key config given in the URI without bootstrapping.

the key config for the next epoch could also be made available, but i don't see a very strong reason to support that. assuming we find a reason, i think this could be done by sending an If-Modified-Since header that is nominally after the expiry (i.e. far enough in the future that the current config will have expired), not sure if that's a little too silly/clever as far as interpreting standard HTTP headers' meanings, but i doubt we'll need such a thing

nothingmuch avatar Aug 05 '25 20:08 nothingmuch