erlang-punch / nostr Goto Github PK
View Code? Open in Web Editor NEWA pure Erlang implementation of the nostr protocol
Home Page: https://github.com/erlang-punch/nostr
License: MIT License
A pure Erlang implementation of the nostr protocol
Home Page: https://github.com/erlang-punch/nostr
License: MIT License
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:
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
MethodgetSharedSecret
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
functionnostrlib_secp256k1:get_public_key/1
: extract the public key from the value returned generate_key/0
functionnostrlib_secp256k1:get_coordinates/1
: extract the coordinates of the keypros:
cons:
pros:
cons:
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
.
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
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.
nostr_client.erl
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 |
Work in progress.
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.
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.
nostr_sigwip.erl
using specification defined below;SUITE_nostr_sigwip.erl
using examples from BIP173
and NIP19
;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 |
% 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().
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.
eunit
moduleeunit
module can be included in any modules if requiredcommon_test
modulecommon_test
modulemain
branchnostr
is following semantic versioning0.X.Y
releases are using jazz band/artist names (hard bop if possible) with a song1.X.Y
release will use metal band/artist names with a songA declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.