Giter Site home page Giter Site logo

erlang-punch / nostr Goto Github PK

View Code? Open in Web Editor NEW
15.0 15.0 3.0 3.41 MB

A pure Erlang implementation of the nostr protocol

Home Page: https://github.com/erlang-punch/nostr

License: MIT License

Erlang 99.13% Makefile 0.64% Python 0.23%
client distributed erlang implementation library minimalist nip nostr otp protocol relay server

nostr's People

Contributors

erlangpunch avatar niamtokik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

maartz 22388o

nostr's Issues

Implement Schnorr algorithm signature algorithm in pure Erlang

All messages are signed using Schnorr signature, the same protocol used by Bitcoin and defined in BIP-340. That's one of the most important requirement here. Unfortunately, at this time, it seems no version of this algorithm is present in OTP and no one implemented it in pure Erlang.

Implementing an algorithm from the cryptography world is never a good idea, but, it could be great to learn a little bit more. Here the list of specifications and implementation I found.

The test vectors can be found there: https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv with the implementation: https://github.com/bitcoin/bips/blob/master/bip-0340/reference.py

Other resources:

Implement nip/04: Encrypted Direct Message

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

Implement NIP/01: Basic Events and Kinds

At this time of writing, NIP/01 is partially implemented but there is no clean way to encode or decode the different events and kinds. nostrlib_decoder has been created to deal with this issue but it will probably necessary to create at least another one called nostrlib_encoder.

  • create a common interface to encode message
  • create a common interface to decode message
  • create interface to encode/decode a kind 0 (metadata)
  • create interface to encode/decode a kind 1 (text_note)
  • create interface to encode/decode a kind 2 (recommend message)
  • update handle_cast/2 callback in nostr_client_router module
% reference to nostrlib_encode:encode/1,2
nostrlib:encode/1
nostrlib:encode/2

% reference to nostrlib_decode:decode/1,2
nostrlib:decode/1
nostrlib:decode/2

see https://github.com/nostr-protocol/nips/blob/master/01.md

test an open relay server to understand how the protocol is working

nostr is an open protocol used on open relays. The goal of this issue is to create a small usable client using Erlang/OTP functions and few external modules like Thoas and Gun.

  • Try to connect to an open-relay using only the Erlang shell and external modules. In parallel document how the protocol is working using a modern browser like Firefox, Chromium or Brave with javascript client.
    • Create a connection
    • Request for events
    • Create a key
    • Stop a connection
  • Create module nostr_client.erl
  • Document the implementation
  • Create tests when needed

Summary

At this time, these functions were created to design the client. They will probably change during the development process based on the nostr protocol requirements.

Functions Examples Comments
nostr_client:connect/1 nostr_client:connect(Host) Connect to a remote server with default options
nostr_client:connect/2 nostr_client:connect(Host, Options) Connect to a remote server with specific options
nostr_client:request/2 nostr_client:request(Reference, EventsId) Request an event or a list of events
nostr_client:publish/2 nostr_client:publish(Reference, Event) Publish a new event
nostr_client:key/0 nostr_client:key() Generate a new key pair
nostr_client:disconnect/1 nostr_client:disconnect(Reference) Disconnect from a server

Specification

Work in progress.

References and Resources

Create an internal routing system for nostr

The latest versions of nostr specifications have removed asynchronous way to communicate with a relay. That means every connection are now synchronous and will receive an answer from the relay. The first model was not planned with that in mind and a full rewrite of this part of the code is required. Here the issue.

Implement bech32 format in pure erlang

Bech32/sigwip is currently used by nostr protocol to share different kind of information between clients and relays. The goal of this issue is to create a fully compliant, easy to use, tested and documented version of this data format in Erlang.

  • Create different version of the algorithm and take the most efficient/usable one based on nostr usage;
  • Design the module nostr_sigwip.erl using specification defined below;
  • Design this module using TDD to be sure we are using the corrects patterns;
  • Create a test suite called SUITE_nostr_sigwip.erl using examples from BIP173 and NIP19;
  • Create the documentation explaining how to use the module and how it has been implemented;
  • Be 100% compatible with use cases from nostr and bitcoin;
  • Be proud of your work and have fun!

Summary

Functions Examples Comments
nostr_sigwip:encode/1 encode(Data) Encode a Data element
nostr_sigwip:encode/2 encode(Data, HumanReadablePart) Encode data and set an HRP header
nostr_sigwip:encode/3 encode(Data, HumanReadableParts, Options) Encode data, with HRP and options
nostr_sigwip:decode/1 decode(Data) Decode data
nostr_sigwip:decode/2 decode(Data, Options) Decode data, with specific options

Specification

% main types used to decode
-type sigwip_data() :: iodata() | pos_integer() | bitstring() | binary() | string().
-type sigwip_human_readable_part() :: data().
-type sigwip_option() :: bech32 | bech32m | uppercase | lowercase | mixedcase.
-type sigwip_options() :: [sigwip_option(), ...].

% the types when decoded, using a map()
-type sigwip_struct() :: #{
   human_readable_part => <<>> :: bitstring(),
   data => <<>> :: bitstring(), 
   checksum => <<>> :: bitstring(), 
   valid => false :: boolean(),
   options => [] :: sigwip_options()
}
-type sigwip_ok() :: {ok, sigwip_struct()}.

% error messages must contain enough information
% to debug a message in case of problem.
-type sigwip_error_proplist() :: {message, bitstring()} | 
   {offset, post_integer()} | 
   {parsed, bitstring()} | 
   {rest, bitstring()}.
-type sigwip_error_proplists() :: [sigwip_error_proplist(), ...].
-type sigwip_error() :: {error, sigwip_error_proplists()}.

-spec encode(Data) -> Return when
   Data :: sigwip_data(),
   Return :: sigwip_ok() | sigwip_error().

-spec encode(Data, HRP) -> Return when
   Data :: sigwip_data(),
   HRP :: sigwip_human_readabe_part(),
   Return :: sigwip_ok() | sigwip_error().

-spec encode(Data, HRP, Options) -> Return when
   Data :: sigwip_data(),
   HRP :: sigwip_human_readable_part(),
   Options :: sigwip_options(),
   Return :: sigwip_ok() | sigwip_error().

References and Resources

project: create rules for editing, testing and documenting.

nostr project has been created as an academic/educational project. The main goal is to teach Erlang programming by using a recent and modern protocol from scratch, and delivering a high quality level application, highly tested and documented. This project is trying to follow the KISS principles.

Documentation

  • user documentation must include examples
  • user documentation must be accessible and correct
  • development documentation should be available
  • exported interfaces must be documented
  • private interfaces should be documented internally to explain the "why" or point to an issue on github
  • if literate programming can be used, use it.
  • if a schema or a diagram is needed, creates one (using draw.io)

Testing

  • functions and modules should be designed using TDD principle with eunit module
  • eunit module can be included in any modules if required
  • new features must be tested with common_test module
  • regression testing must use common_test module
  • all tests must succeed before merging in main branch

Benchmarking

  • before merging, a benchmark should be available
  • the benchmark should be done automatically on a pipeline (github action workflow)

Packaging

  • must work on all major operating systems available on the market
  • virtual machines should be available to help developers
  • docker image should be available to help deployment

Versions, Releases and Milestones

  • nostr is following semantic versioning
  • releases are following milestones
  • releases are named with artists names and one of their song
  • the 0.X.Y releases are using jazz band/artist names (hard bop if possible) with a song
  • the 1.X.Y release will use metal band/artist names with a song

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.