nostr icon indicating copy to clipboard operation
nostr copied to clipboard

Implement nip/04: Encrypted Direct Message

Open niamtokik opened this issue 2 years ago • 0 comments

Summary

  • [ ] Ensure the generate secp256k1 is correct
  • [ ] Find or implement the new functions required to generate encrypted messages
  • [ ] Create the interfaces required to encrypt, decrypt, sign and validate encrypted messages

What we need

  1. A simple and light way to create encrypted message
  2. A function to get a shared secret between a private and a public key
  3. A comprehensible way to do it

Notes

The following javascript code is coming from nip/04 standard.

import crypto from 'crypto'

//--- 8< This part of the code is probably not supported by Erlang
import * as secp from 'noble-secp256k1'
let sharedPoint = secp.getSharedSecret(ourPrivateKey, '02' + theirPublicKey)
let sharedX = sharedPoint.substr(2, 64)
//--- 8< This part of the code is probably not supported by Erlang

let iv = crypto.randomFillSync(new Uint8Array(16))
var cipher = crypto.createCipheriv(
  'aes-256-cbc',
  Buffer.from(sharedX, 'hex'),
  iv
)
let encryptedMessage = cipher.update(text, 'utf8', 'base64')
encryptedMessage += cipher.final('base64')
let ivBase64 = Buffer.from(iv.buffer).toString('base64')

let event = {
  pubkey: ourPubKey,
  created_at: Math.floor(Date.now() / 1000),
  kind: 4,
  tags: [['p', theirPublicKey]],
  content: encryptedMessage + '?iv=' + ivBase64
}

It seems some of the functions used in this snippet are not available in Erlang (or at least in default libraries).

getSharedSecret Method

getSharedSecret method create a common secret between a public and a private key. This function is defined in index.ts in the noble-secp256k1 project.

In pure Elixir, the function get_shared_secret/3 defined in the Curvey module can do the same action.

In both case, others non-implemented functions will be required, like extracting the coordinate of the public/private key and implement Jacobian multiplication (noble-secp256k1 implementation.

The functions used are very small subset of all the feature available on secp256k1 bitcoin library. It could be interesting to create a function called nostrlib_secp256k1:get_shared_secret/3 functions. The same module could also integrated all other functions related to secp256k1 encryption like:

  • nostrlib_secp256k1:generate_key/0: generate a new key with default options based on crypto:generate_key/2 module.
  • nostrlib_secp256k1:generate_key/1: generate a new key with extra options.
  • nostrlib_secp256k1:get_private_key/1: extract the private key from the value returned by generate_key/0 function
  • nostrlib_secp256k1:get_public_key/1: extract the public key from the value returned generate_key/0 function
  • nostrlib_secp256k1:get_coordinates/1: extract the coordinates of the key
  • ...

Using External Implementation

Using our own secp256k1 partial implementation in Erlang

pros:

  • no dependencies at all
  • only what we need
  • could be a good introduction to elliptic curve encryption

cons:

  • less secure
  • require a lot of work (it should be secure enough to be used)
  • require external audit
  • using Erlang will be less performant than a C lib
  • we should avoid re-implement cryptographic libraries

Using secp256k1 C NIF Elixir Implementation

pros:

  • NIF: performance
  • Use official bitcoin library
  • Used by other projects

cons:

  • Elixir: this project wants to use only pure Erlang code with only few external deps
  • NIF: if not well implemented can do nasty thing
  • NIF: requires to maintain an external C library and compile it for every releases
  • old version of the library used by default: a really old version from 2018, based on the build_deps.sh script.
  • seems to be not maintained since January 2020 (latest commit)

References and Resources

niamtokik avatar Feb 18 '23 12:02 niamtokik