joule-labs / joule-extension

Lightning payments extension for Chrome
MIT License
353 stars 62 forks source link

Sign and Verify messages #146

Closed jamaljsr closed 5 years ago

jamaljsr commented 5 years ago

Description of the Feature or Idea

Add the ability for websites to request the visitor's LN node to sign or verify a message according to the WebLN provider spec https://github.com/wbobeirne/webln#provider-spec.

  /* Prompts the user to sign a message with their private key */
  signMessage(message: string): Promise<SignMessageResponse>;

  /* Shows the user a view that verifies a signed message */
  verifyMessage(signedMessage: string, rawMessage: string): Promise<void>;

Limitations

LND requires nodes be routable between each other in order to verify a message. In other words, given the lightning networks A -> B -> C & D <- E. If A signs a message, then B and C are able to verify that message but D and E are not.

The lncli verifymessage command returns valid: false in these two cases:

Joule will not be able to inform the website if the failure is one or the other.

Use Cases

Sign The obvious use case is for authentication. The website can give the user a message to sign using Joule then take the signature and verify it on the backend to confirm the identity of the user's node.

Verify ??? 🤔

I've started implementing this functionality. I'm creating this PR to open the floor for suggestions or ideas for improvements

wbobeirne commented 5 years ago

This is great to see that it's feasible. I hadn't had time to look into it, but was under the impression it would require a direct channel with a node, not just routable, so that's great to hear.


Just to clarify what I'd expect the UX of signMessage to look like, here's an example of what signing a message looks like with Metamask. You get a preview of the message you're signing, and are asked to accept or reject the signature.

We could also draw from having a standard data-rich format ala Ethereum's EIP-712 for the signatures Joule generates. It could be a standard JSON blob that, in addition to the whatever the site asks a signature for, attaches some metadata about the site to prevent phishing signatures (As seen with the domain key in EIP-712.) However, I think this might require some more planning, so we can stick with raw signatures for now, and if people pass in JSON, that's fine too.


In the case of verifyMessage, the purpose for this would be so that the user's client could independendently verify any signature that a site serves up. Take for instance if users who posted articles on yalls.org also signed a message for authenticity. Other users could verify that the user did in fact post the message, and that yalls.org wasn't compromised.

The way I envision this is there'd be a "Verify" button you'd press, that would call webln.verifyMessage(signature, raw) which would pop up a window from Joule that shows the contents of the raw message, and confirms which pubkey it came from.

I'll see if I can dig in more on your point about a lack of granularity in the verifymessage errors, hopefully that's something that can be fixed easily on the LND side. It should probably just return an error if there is no route, rather than valid: false, but there may be a bigger issue at play here.

jamaljsr commented 5 years ago

@wbobeirne Thanks for the great feedback.

I skimmed through EIP-712 and now I have a much better understanding of the motivation to create a standard for the signing data structure. This clearly does require more thought and discussion, so I agree with your suggestion to stick with raw signatures for now, and improve on structure later.

Also thanks for the example use case for verifyMessage. This would be a great feature to have available for web apps.

Regarding the valid: false response, I did review LND PR 192 which was the introduction of the sign/verify RPC methods. I don't think it would be difficult to add an additional fail_reason field to the VerifyMessageResponse. I'd be willing to create a PR myself as I see the problem is in rpcserver.go. I'd have to get some insight from the LND team on whether or not there's valid security reasons to be vague with the invalid response first. I'll try to ping someone tomorrow to get some feedback.

jamaljsr commented 5 years ago

I opened an issue (2510) over in the LND repo with a lot more details on what I've discovered about the verifymessage failure response cases.

TLDR Your node doesn't even need to be routable to the signing node to get valid:true. It just needs to have once connected (add peer) to the network that the signing node was connected to. You can simply spin up a brand new node, add a peer to any other node, then you will be able to validate the signature.

As of right now, the ability to distinguish between the 2 invalid cases is blocked by this LND issue. I'll continue working on the update but we'll have to decide if we want to wait for it to be fixed in LND or merge as is.

wbobeirne commented 5 years ago

Good find. I would say for the vast majority of people, they probably won't run into that issue, and verification will most frequently be done on the server side, not the client side. Full steam ahead IMO. Feel free to split the tasks out (sign vs verify) if you want some early feedback. And feel free to adjust the WebLN interfaces as needed, that's all still in flux.

jamaljsr commented 5 years ago

I’ve implemented both prompts already. I’m not 100% satisfied with the UI for verify. I’ll create a PR tomorrow with what i have so far to get your feedback.

ianpatton commented 5 years ago

As noted in https://github.com/BlueWallet/BlueWallet/issues/578, for the authentication use-case, it would be ideal to use a purely JS implementation of verifyMessage.

There is an implementation available at https://github.com/nerddan/ln-crypto for reference.

wbobeirne commented 5 years ago

WebLN's verifyMessage is only for the user's sake, not for websites or services to call to verify a message. You're more than welcome to use ln-crypto in your app or backend to verify messages that come from signMessage.

Dropped some other notes in that thread related to what you're working on, hope it helps clarify.