NIP 44 Update

By this time, many of you may have either given up on a new standard for encrypting notes to emerge, or entirely forgotten that there was one. Well, I have good news — we've received the audit report from Cure53 on the NIP 44 encryption spec, and the results are good! This post will be a summary of the findings, and some hints on what lies ahead.

The results

The assets in scope for the audit can be found on Paul Miller's NIP 44 repository. Included is the full text of the spec, as well as implementations in Typescript, Rust, and Go.

Overall, here's what Cure53 had to say:

The Cure53 team succeeded in achieving very good coverage of the WP1-WP4 targets. All
ten findings spotted by the testers were classified as general weaknesses with limited
exploitation potential. In other words, no vulnerabilities were detected within the inspected
components.

In other words, no vulnerabilities were found, but there are things we can do to harden the implementations. Cure53 elaborates:

The fact that Cure53 was not able to identify any exploitable vulnerabilities can be
interpreted as a positive sign in regard to the security of the NIP44 specification and
implementations. Nevertheless, even though the spotted problem can all be seen as general
weaknesses and hardening advice, they should not be ignored. It is known that weaknesses
may serve as entry points for more severe vulnerabilities in the future. Cure53 strongly
recommends swift resolution of all reported flaws.

These weaknesses and recommendations each have their own code, prefixed by "NOS-01", which is our audit number. Here's a quick summary of the individual points:

  • NOS-01-001 describes a weakness related to naive secp256k1 implementations, which may accept public keys with both x and y coordinates instead of just x and a sign-bit. This can result in the compromise of a sender's private key if the sender can be tricked into encrypting a message with an invalid public key. This is known as a "twist attack". Cure53 recommends some additional test vectors to make sure that uncompressed keys are not accepted.
  • NOS-01-002 includes a handful of minor recommendations, including specifying the initial counter for ChaCha20, nailing down key formats, and a few other things which they go into more depth on elsewhere.
  • NOS-01-003 provides some additional test vectors to prevent twist attacks and out of bound errors.
  • NOS-01-004 suggests using a constant time equality function to prevent timing side-channel attacks, along with some code samples.
  • NOS-01-005 identifies missing range checks in the Go implementation which should be addressed in order to avoid crashes.
  • NOS-01-006 addresses the lack of provisions for forward secrecy in the NIP 44 spec. They suggest introducing sender-side forward secrecy by deriving the session key using ephemeral keys and a KDF.
  • NOS-01-007 explains that using the same key for multiple purposes can lead to unexpected compromises when taken together. Best practice is to use a different key for signing and for encryption, which nostr violates. While the particular curves we use aren't vulnerable to this exploit, Cure53 recommends deriving an encryption key from a user's main key using a KDF in order to reduce the impact of a leaked encryption key.
  • NOS-01-008 identifies an incorrect use of the salt parameter in calls to HKDF (an HMAC-based Key Derivation Function allows us to prove that an encrypted payload was not forged). Currently, the salt is generated randomly every time, but the security definition of HKDF specifies that it be static. This is likely not exploitable, but should be fixed. Cure53 suggests switching the nonce and salt when calling the HKDF, and deriving a static salt using something like SHA256(pubA, pubB, "nip-44-v2").
  • NOS-01-009 demonstrates that the Message Authentication Code (MAC) tag is currently computed without a nonce. This doesn't compromise the encryption, and authentication is covered by event signatures as specified in NIP 01, but it does prevent the MAC from being useful on its own.
  • NOS-01-010 suggests using clearer boundaries when constructing the HMAC payload.

The takeaway here is that with a few minor changes, the spec itself is sufficient to "provide a simple way for the users to communicate privately". There are, however, a number of edge cases which can compromise implementations, so for any developers out there porting the spec to new languages, be sure to take a look at Paul's test vectors.

In the end, the NIP44 specification and implementations appear to have already achieved the security goal of providing users with a way to communicate privately. Miscellaneous issues identified by Cure53 during NOS-01 correspond to further hardening of the current version and/or suggestions for future versions. Following these suggestions could provide more advanced security guarantees.

They also clarify that:

Importantly, the lack of certain security guarantees, such as forward secrecy, post-compromise security, deniability, and post-quantum, had been known to the development team prior to NOS-01.

Since this is the case, it's important that client developers be absolutely clear with their users about what NIP 44 is good for, and what it's not. This is a "simple" way to communicate privately — users with more advanced privacy requirements should use something like MLS or SimpleX.

What's next?

NIP 44 isn't quite ready for widespread use just yet, but it shouldn't take long to incorporate all the adjustments mentioned in the audit. Then there are several NIPs which rely on the encryption in NIP 04 which should eventually be migrated individually. Beyond that, a whole world of affordances for encrypted data becomes available on nostr.

First and most anticipated, we can finally deprecate NIP 04 and stop leaking DM metadata. Vitor's Sealed Gift Wraps are an excellent alternative to NIP 04 DMs, and add support for small group chats, which will be a big improvement to client UX.

Other more ambitious specifications targeting larger groups exist as well, including my Closed Communities draft spec. This NIP makes it possible to extend NIP 72 communities with a private component, which can be useful for publishers or real-life communities.

There are lots of other things NIP 44 might be used for as well, for example:

  • The ability to share your location only with certain people
  • Private calendar events, product listings, or streams
  • Private tags — instead of encrypting an entire event, it should be possible to encrypt only a single tag's value. This allows senders to attach private data to public events.

Conclusion

There's lots of work still to be done, but I think this marks a huge milestone for nostr. So thank you to Paul Miller for his work on the spec and the audit, and to OpenSats for funding the audit!