-
Notifications
You must be signed in to change notification settings - Fork 135
router: abstract node key behind ECDH interface #47
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,6 +22,55 @@ const ( | |
| // the output of a SHA256 hash. | ||
| type Hash256 [sha256.Size]byte | ||
|
|
||
| // SingleKeyECDH is an abstraction interface that hides the implementation of an | ||
| // ECDH operation against a specific private key. We use this abstraction for | ||
| // the long term keys which we eventually want to be able to keep in a hardware | ||
| // wallet or HSM. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Out of curiosity, which hardware wallets currently support ECDH?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have no idea, to be honest. HW wallets are my blind spot. But I imagine implementing that is pretty easy as it's not much different from signing a message.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Roasbeef do they exist?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ledger and trezor have support in their APIs which can be leveraged by custom applications. With this though, I think we're more so targeting remote/restricted signers which can emulate these calls by using the existing |
||
| type SingleKeyECDH interface { | ||
| // PubKey returns the public key of the private key that is abstracted | ||
| // away by the interface. | ||
| PubKey() *btcec.PublicKey | ||
|
|
||
| // ECDH performs a scalar multiplication (ECDH-like operation) between | ||
| // the abstracted private key and a remote public key. The output | ||
| // returned will be the sha256 of the resulting shared point serialized | ||
| // in compressed format. | ||
| ECDH(pubKey *btcec.PublicKey) ([32]byte, error) | ||
| } | ||
|
|
||
| // PrivKeyECDH is an implementation of the SingleKeyECDH in which we do have the | ||
| // full private key. This can be used to wrap a temporary key to conform to the | ||
| // SingleKeyECDH interface. | ||
| type PrivKeyECDH struct { | ||
| // PrivKey is the private key that is used for the ECDH operation. | ||
| PrivKey *btcec.PrivateKey | ||
| } | ||
|
|
||
| // PubKey returns the public key of the private key that is abstracted away by | ||
| // the interface. | ||
| // | ||
| // NOTE: This is part of the SingleKeyECDH interface. | ||
| func (p *PrivKeyECDH) PubKey() *btcec.PublicKey { | ||
| return p.PrivKey.PubKey() | ||
| } | ||
|
|
||
| // ECDH performs a scalar multiplication (ECDH-like operation) between the | ||
| // abstracted private key and a remote public key. The output returned will be | ||
| // the sha256 of the resulting shared point serialized in compressed format. If | ||
| // k is our private key, and P is the public key, we perform the following | ||
| // operation: | ||
| // | ||
| // sx := k*P | ||
| // s := sha256(sx.SerializeCompressed()) | ||
| // | ||
| // NOTE: This is part of the SingleKeyECDH interface. | ||
| func (p *PrivKeyECDH) ECDH(pub *btcec.PublicKey) ([32]byte, error) { | ||
| s := &btcec.PublicKey{} | ||
Roasbeef marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| s.X, s.Y = btcec.S256().ScalarMult(pub.X, pub.Y, p.PrivKey.D.Bytes()) | ||
|
|
||
| return sha256.Sum256(s.SerializeCompressed()), nil | ||
| } | ||
|
|
||
| // DecryptedError contains the decrypted error message and its sender. | ||
| type DecryptedError struct { | ||
| // Sender is the node that sent the error. Note that a node may occur in | ||
|
|
@@ -149,21 +198,7 @@ func (r *Router) generateSharedSecret(dhKey *btcec.PublicKey) (Hash256, error) { | |
| } | ||
|
|
||
| // Compute our shared secret. | ||
| sharedSecret = generateSharedSecret(dhKey, r.onionKey) | ||
| return sharedSecret, nil | ||
| } | ||
|
|
||
| // generateSharedSecret generates the shared secret for a particular hop. The | ||
| // shared secret is generated by taking the group element contained in the | ||
| // mix-header, and performing an ECDH operation with the node's long term onion | ||
| // key. We then take the _entire_ point generated by the ECDH operation, | ||
| // serialize that using a compressed format, then feed the raw bytes through a | ||
| // single SHA256 invocation. The resulting value is the shared secret. | ||
| func generateSharedSecret(pub *btcec.PublicKey, priv *btcec.PrivateKey) Hash256 { | ||
| s := &btcec.PublicKey{} | ||
| s.X, s.Y = btcec.S256().ScalarMult(pub.X, pub.Y, priv.D.Bytes()) | ||
|
|
||
| return sha256.Sum256(s.SerializeCompressed()) | ||
| return r.onionKey.ECDH(dhKey) | ||
| } | ||
|
|
||
| // onionEncrypt obfuscates the data with compliance with BOLT#4. As we use a | ||
|
|
@@ -200,10 +235,14 @@ func (o *OnionErrorDecrypter) DecryptError(encryptedData []byte) ( | |
| len(encryptedData)) | ||
| } | ||
|
|
||
| sharedSecrets := generateSharedSecrets( | ||
| sharedSecrets, err := generateSharedSecrets( | ||
| o.circuit.PaymentPath, | ||
| o.circuit.SessionKey, | ||
| ) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("error generating shared secret: %v", | ||
| err) | ||
| } | ||
|
|
||
| var ( | ||
| sender int | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wondering still if
SingleKeyECDHis the right name. It also suggests some fancy algorithm in which you exchange keys with yourself. Thinking about alternatives. MaybeExternalECDHor justSigner(even though it isn't really signing)