There is no description of the protocol or of its security goals, so I am making some guesses based on a cursory look at the source and what I imagine this might be for.
A single symmetric key is derived for both directions, and there is no checking of nonces, so as far as I can tell any message can be dropped, reordered, or replayed in both directions. (Including replaying message from A to B as if they were from B to A.) This is a bit like using ECB and likely to lead to fun application-specific attacks like [0].
This is very much rolling your own crypto, in a dangerous way. I am on the record as being "against" the "don't roll your own crypto" refrain [1], but mostly because it doesn't work: it should discourage people from publishing hand-rolled protocols such as this, but instead people think it means "don't roll your own primitives" and accept any use of "Ed25519/X25519" as probably secure.
Please read about the Noise framework [2] to get an idea of how much nuance there is to this, and consider using a Go implementation of it [3] instead.
P.S. This kind of issue is also why I maintain that NaCl is not a high-level scheme [4]: this could have used NaCl and have the exact same issues. libsodium has a couple slightly higher-level APIs that could have helped, secretstream [5] and kx [6], but again please use Noise.
I mean this looks like standard ECIES to me. I think it could even count as a Noise protocol, as in my understanding that protocol family subsumes most of the classic Diffie Hellman based key exchanges. Reading the source the author seems to use a long-lived ECDSA key pair to sign and exchange an ephemeral ECDH key pair, then derives a symmetric AES key from that and uses that for AES-GCM. Not sure how you would do message reordering here as GCM takes care of authenticating your data and keys are not reused. If you want replay protection it's probably sufficient to either remember used keys or add a timestamp. It's a well-done example of writing such a simple protocol, I don't think the author plans to replace TLS with it and he even mentions that in the README.
Can you express this with Noise framework tokens? I don't think you can. Noise is fiddlier than it looks! It's not just an ordering of DH exchanges; it's transcript hashes, cipher state tracking (and reinitializing), key derivation, the whole 9. The temptation (at least for me) is to just skip to the table of handshakes and skim, but the actual protocol framework is in Section 5, where they define precisely what each of those tokens really entails.
(To say nothing of: this uses signatures, and Noise does not).
Gotta say that I generally like the Noise framework (or rather the protocols that result), but it is one of the most impenetrable specifications I've ever read
I don't remember what it is specifically about it, I just remember the document being a pain to read; a bit like the original Paxos paper in that regard.
I don't know about that. As an implementor, it is probably one of the easiest-to-follow specs I've ever worked from. You can pretty much code it from the top of the spec to the bottom; when you get to the handshake patterns section and realize that each is just a different ordering of things in an array or whatnot, it's pretty slick.
ECIES is quite fine though for most asynchronous applications, where having a signing key also makes sense as you often want to publish long-lived, signed data and build a trust chain (e.g. generate and sign session keys from a master key). I built several real-world systems based on that (e.g. [1]) and they all made it through the audits fine. I was exploring Noise-based protocols but I find it's best to rely on primitives that are supported by the Web Crypto API.
ECIES is a hybrid encryption construction; Noise is a protocol. They're two different levels of abstraction. This thing we're commenting on has a protocol; it's just an accidental one, which is usually not what you want. WebCrypto doesn't provide a protocol framework, just a bunch of primitives.
> If you want replay protection it's probably sufficient to either remember used keys or add a timestamp.
The replay Filippo is talking about is in the context of a single key. The remote side simply reads the nonce || message off the wire and aead.Opens it, without regard to whether or not that nonce has been used before to decrypt a previous message.
I'm not going to make any security claims as of now (it's almost certainly broken) but it uses the Noise IK handshake.
Long term I think NoiseSocket would be a good way to go https://noisesocket.org/post/1/ but perhaps in service-to-service use cases ciphersuite negotiation can be simplified.
I once heard something along the lines of "if you're writing the words RSA, you're doing it wrong." I guess that goes for ED25519 also.
If you want to secure a stream with asymmetric cryptography and don't need all the bells and whistles of TLS, what's the correct way to do it? Noise? Is there nothing simpler?
IIUC the correct answer, according to the 2009 blog post that popularized the saying you're referring to, is to just use TLS anyway. In most cases it's better to accept some extra complexity than to get crypto wrong.
A single symmetric key is derived for both directions, and there is no checking of nonces, so as far as I can tell any message can be dropped, reordered, or replayed in both directions. (Including replaying message from A to B as if they were from B to A.) This is a bit like using ECB and likely to lead to fun application-specific attacks like [0].
This is very much rolling your own crypto, in a dangerous way. I am on the record as being "against" the "don't roll your own crypto" refrain [1], but mostly because it doesn't work: it should discourage people from publishing hand-rolled protocols such as this, but instead people think it means "don't roll your own primitives" and accept any use of "Ed25519/X25519" as probably secure.
Please read about the Noise framework [2] to get an idea of how much nuance there is to this, and consider using a Go implementation of it [3] instead.
P.S. This kind of issue is also why I maintain that NaCl is not a high-level scheme [4]: this could have used NaCl and have the exact same issues. libsodium has a couple slightly higher-level APIs that could have helped, secretstream [5] and kx [6], but again please use Noise.
[0] https://cryptopals.com/sets/2/challenges/13
[1] https://securitycryptographywhatever.buzzsprout.com/1822302/...
[2] https://noiseprotocol.org/noise.html
[3] https://github.com/flynn/noise
[4] https://words.filippo.io/dispatches/nacl-api/
[5] https://libsodium.gitbook.io/doc/secret-key_cryptography/sec...
[6] https://libsodium.gitbook.io/doc/key_exchange