-
Notifications
You must be signed in to change notification settings - Fork 132
[group key addrs 2/5]: internal/ecies: add encrypt/decrypt with ECIES #1512
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
Conversation
Pull Request Test Coverage Report for Build 15734776865Details
💛 - Coveralls |
internal/ecies/ecies.go
Outdated
var result bytes.Buffer | ||
result.Write(nonce) | ||
|
||
gcm, err := cipher.NewGCMWithNonceSize(block, GCMNonceSize) |
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.
Any particular reason to use this over NewGCM
? 12-byte nonces are standardized, it's also what the underlying Go packages uses with the default constructor.
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.
See section 5.2.1 of this PDF: https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38d.pdf
For IVs, it is recommended that implementations restrict support to the length of 96 bits, to
promote interoperability, efficiency, and simplicity of design.
IV here is basically a nonce.
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.
One alternative crypto system to consider is chacha20poly1305
: https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305#New. This is what we use for encryption in LN. It also natively supports adding associative data (plaintext data included in the MAC).
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.
Hmm, I just used an example I found somewhere.
But I like ChaCha20Poly1305 better, changed the code to use that. We'll use the sender's ephemeral public key as the additional data, to authenticate the ciphertext. It will be part of the outer (authmailbox message) envelope, not the ciphertext itself. Does that make sense?
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.
We'll use the sender's ephemeral public key as the additional data, to authenticate the ciphertext
Makes sense, this is exactly what I had in mind!
When it comes to the question of if we should have the key be a "part" of the cipher text (sent as a single blob), or split out into a single key, I lean towards making it part of the cipher text. That way it's a logical unit during transport.
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.
Made some small comments throughout.
internal/ecies/ecies.go
Outdated
// SHA256. | ||
func HkdfSha256(secret []byte) ([32]byte, error) { | ||
var key [32]byte | ||
kdf := hkdf.New(sha256.New, secret, nil, nil) |
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.
Maybe add a comment here explaining why it's ok to not use a salt here.
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.
Actually, adding some salt here wouldn't be too bad of an idea. Added the protocol name to make the derived key unique to this application.
internal/ecies/ecies.go
Outdated
func EncryptSha256ChaCha20Poly1305(sharedSecret [32]byte, msg []byte, | ||
additionalData []byte) ([]byte, error) { | ||
|
||
// We begin by stretching the shared secret using HKDF with SHA256. |
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.
Not a lot of stretching involved if it goes from 32 bytes to 32 bytes. 😄
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.
Yeah, you're right. Re-formulated to "hardening against brute forcing".
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.
Looking pretty good!
} | ||
|
||
// Select a random nonce, and leave capacity for the ciphertext. | ||
nonce := make( |
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.
As we're using a random nonce here, we should use NewX
, which is meant for cases where a counter-like nonce isn't used. Basically some extra security margin for when nonces are generated randomly.
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.
Ah, yes, makes sense. Changed to X.
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.
Reviewed 3 of 3 files at r1, all commit messages.
Reviewable status: all files reviewed, 5 unresolved discussions (waiting on @guggero)
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.
LGTM!
Should we be worried about the TestLightningTerminal/custom_channels_multi_rfq (119.50s)
litd itest failing?
No, that looks like a flake. Re-triggered it. |
This commit creates a simple encryption and decryption function that uses ChaCha20Poly1305 for tne symmetric encryption and HKDF with SHA256 for the key derivation. The shared key generation is not part of these functions, because we'll need to use lnd RPCs in some cases to be able to derive it.
@Roasbeef: review reminder |
@Roasbeef friendly review ping. |
// We begin by hardening the shared secret against brute forcing by | ||
// using HKDF with SHA256. | ||
stretchedKey, err := HkdfSha256(sharedSecret[:], []byte(protocolName)) | ||
if err != nil { | ||
return nil, fmt.Errorf("cannot derive hkdf key: %w", err) | ||
} |
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.
Does this actually add "hardening"? I think that HKDF with a constant salt doesn’t add brute-force hardening.
I wander if we can't just use the nonce below as the salt here. And then protocolName
can be the info
arg in HkdfSha256
's hkdf.New
call.
If we use the nonce as the salt then Encrypt...
and Decrypt...
will then have access to the same (random) salt and the serialization format doesn't change.
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.
At a high level, you can view it as just binding the shared secret we create to our particular context (eg: if we change the protocol name, for the same shared secret we get a diff stretched key). The Noise Protocol does something similar to create an initial hash accumulator value which gets mixed into the initial shared secrets.
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.
Yes, I see this as just a form of binding as well. I think we should update the comments in ecies.go
to clarify that this isn't providing hardening, but rather serving as a binding mechanism.
Thanks for the link!
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.
IMO it does harden against brute force because you need to use more CPU cycles per shared secret you want to try. But I guess in this context that's not really relevant as you'd attack the encryption in different manners than doing brute force.
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.
LGTM 🎊
Implements the ECIES encryption scheme as mentioned here: #874 (comment)
This commit creates a simple encryption and decryption function that uses ChaCha20Poly1305 for tne symmetric encryption and HKDF with SHA256 for the key derivation.
The shared key generation is not part of these functions, because we'll need to use lnd RPCs in some cases to be able to derive it.
The idea is that we'd transport the sender's ephemeral public key outside of the cipher text and use it as the additional data to authenticate the ciphertext.
This change is