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

Simplify heartbeat signature scheme #1537

Merged
merged 6 commits into from
Nov 29, 2022

Conversation

aquarat
Copy link
Contributor

@aquarat aquarat commented Nov 15, 2022

Closes #1536

ethers packing is not well supported in the Elixir environment and even message signing has quirks associated with it that don't translate 1:1. This PR simplifies the heartbeat scheme by removing the packing component in favour of moving the timestamp into the JSON payload. This makes Airnode signatures easier to support in other languages.

The JSON is signed and sent as serialised JSON to prevent differences in JSON marshaling/unmarshaling across languages causing signatures to fail.

@aquarat aquarat self-assigned this Nov 15, 2022
@aquarat
Copy link
Contributor Author

aquarat commented Nov 22, 2022

Currently waiting on feedback from @andreogle if he's happy with this scheme (I suspect he is, but will wait for confirmation).

@andreogle
Copy link
Member

ChainAPI supports the new heartbeat signature authentication. I'm happy with this. Thanks for the help getting the address recovery to work @aquarat 💙

@aquarat aquarat marked this pull request as ready for review November 23, 2022 09:55
const heartbeatPayload = {
const timestamp = Date.now();

const heartbeatPayload = JSON.stringify({
Copy link
Member

@andreogle andreogle Nov 24, 2022

Choose a reason for hiding this comment

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

I think it's worth adding a comment explaining why this is stringified. i.e. to avoid any minor differences that might occur when encoding/decoding in other environments. The payload/message needs to be exactly the same everywhere

Copy link
Contributor Author

@aquarat aquarat Nov 28, 2022

Choose a reason for hiding this comment

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

I've added the following comment, LMK if you disagree:

  /*
  The heartbeat payload is serialised as JSON and then serialised again in JSON as the value of the payload field.
  The reason this is done is to avoid any inconsistencies between different JSON serialisation implementations.
   */

const timestamp = Date.now();

const heartbeatPayload = JSON.stringify({
timestamp,
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: I'd suggest using the UNIX timestamp (in seconds) and not the JS timestamp which is in ms... But this is a breaking change so we might just not do it.

Copy link
Member

@andreogle andreogle Nov 26, 2022

Choose a reason for hiding this comment

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

Good point. Unix time would be much better imo. And just to confirm, is this in UTC or is it the server timezone?

Why is it a breaking change?

Copy link
Contributor

@Siegrift Siegrift Nov 27, 2022

Choose a reason for hiding this comment

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

Don't you rely on the heartbeat timestamp being in ms? (That's how it worked until now). Changing that to seconds would mean the old values would need to be migrated.

EDIT: But tou will be implementing new handling for this anyway, so maybe it's not a big problem. But I am not sure if there aren't any other users of heartbeat that would be affected. (But they will be affected either way by the packing removal).

Copy link
Member

Choose a reason for hiding this comment

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

ChainAPI hasn't been using the timestamp until now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made the change by using Math.round(Date.now()) 👌

const heartbeatPayload = {
const timestamp = Date.now();

const heartbeatPayload = JSON.stringify({
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. Maybe it would be wise to make sure that the payload content fields are always sorted. JSON.stringify doesn't do that. Maybe something like [json-stable-stringify](https://github.com/ljharb/json-stable-stringify] would be better. It's not like JSON.stringify will stringify it randomly, the fields will be in the order they are presented in when creating the object. But just to be on the safe side?

Copy link
Contributor Author

@aquarat aquarat Nov 28, 2022

Choose a reason for hiding this comment

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

If we serialise it like this we will always get a consistent result.
Serialising a serialised payload gets rid of the unstable-ordering-related issue and any other potential issues as you're comparing the signature with a byte array effectively. Adding an additional library to ensure serialisation ordering consistency seems unnecessary to me and introduces a new dependency 🤷

@aquarat aquarat merged commit 403a120 into master Nov 29, 2022
@aquarat aquarat deleted the simplify-heartbeat-signature-scheme branch November 29, 2022 10:47
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.

Simplify Heartbeat packing/signing
4 participants