gnunet-go
gnunet-go copied to clipboard
GNUnet in Go
gnunet-go: GNUnet implementation in Go
Copyright (C) 2019-2022 Bernd Fix >Y<
gnunet-go is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
gnunet-go is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see http://www.gnu.org/licenses/.
SPDX-License-Identifier: AGPL3.0-or-later
Caveat
THIS IS WORK-IN-PROGRESS AT A VERY EARLY STATE. DON'T EXPECT ANY COMPLETE DOCUMENTATION OR COMPILABLE, RUNNABLE OR EVEN OPERATIONAL SOURCE CODE.
TL;DR
Go v1.18+ is required to compile the code.
git clone https://github.com/bfix/gnunet-go
cd gnunet-go/src/gnunet
go mod tidy
go generate ./...
go install ./...
go test ./...
The binaries are stored in ${GOPATH}/bin
.
Source code
All source code is written for Go v1.18+.
3rd party libraries are managed by the Go module framework. After downloading
the source code, make sure you run go mod tidy
in the src/gnunet
folder
to install all dependencies.
./src/gnunet
The folder src/gnunet
contains a Go implementation of GNUnet: It is WIP
and only provides a very limited coverage of GNUnet. The goal is to have
a complete, functionally equivalent implementation of the GNUnet protocol
in Go. Currently only some aspects of Transport, GNS, Revocation, Namecache
and DHT are implemented.
Use ./build.sh withgen
to build the executables (services and utilities, see
below). The resulting programs are stored in ${GOPATH}/bin
.
The withgen
argument is optional (only needed if GANA registry files have
changed and been updated in this repository by the user). The go generate
step requires the stringer
tool to be available; you can install it
by running go install golang.org/x/tools/cmd/stringer@latest
.
To run the unit tests, use ./test.sh
.
./src/gnunet/enums
Changes in GANA definitions for block types, GNS record types and signature purpose values can be imported by copying the recfiles (GNU recutils) from GANA into this folder:
- gnunet-dht.rec
- gnunet-gns.rec
- gnunet-signature.rec
After updating the recfiles, you need to run go generate ./...
to generate
the new source files.
./src/gnunet/cmd
gnunet-service-dht-test-go
: Implementation of the DHT core service (testbed).
gnunet-service-gns-go
: Implementation of the GNS core service.
Stand-alone GNS service that could be used with other GNUnet utilities and services.
gnunet-service-revocation-go
: Implementation of the GNS revocation service.
Stand-alone Revocation service that could be used with other GNUnet utilities and services.
revoke-zonekey
: Implementation of a stand-alone program to calculate revocations.
This program creates a zone key revocation block. Depending on the parameters
the calculation can take days or even weeks. The program can be interrupted
at any time using ^C
; restarting the program with the exact same parameters
continues the calculation.
The following command-line options are available:
-
-b
: Number of leading zero bits (difficulty, default: 24). The minimum difficultyD
is fixed at 23. The expiration of a revocation is derived using(b-D+1)*(1.1*EPOCH)
, whereEPOCH
is 365 days and it is extended by 10% in order to deal with unsynchronized clocks.
The default difficulty will create a revocation valid for ~2 years.
-
-z
: Zone key to be revoked (zone ID) -
-f
: Name of file to store revocation data -
-t
: testing mode: allow small difficulties for test runs. -
-v
: verbose output
peer_mockup
: test message exchange on the lowest level (transport).
vanityid
: Compute GNUnet vanity peer id for a given regexp pattern.
N.B.: Key generation is slow at the moment, so be patient! To generate a single matching key some 1,000,000 keys need to be generated for a four letter prefix; this can take more than 30 minutes on average (depending on your CPU).
$ vanityid "^TST[0-9]"
Keys matching the pattern are printed to the console in the following format:
<vanity_id> [<hex.seed>][<hex.scalar>] (<count> tries, <time> elapsed)
The value of count
tells how many key had been generated before a match was
found; time
is the time needed to find a match.
To generate the key files, make sure GNUnet is not running and do:
echo "<hex.seed>" | xxd -r -p > /var/lib/gnunet/.local/share/gnunet/private_key.ecc
chown gnunet:gnunet /var/lib/gnunet/.local/share/gnunet/private_key.ecc
chmod 600 /var/lib/gnunet/.local/share/gnunet/private_key.ecc
For gnunet-go
configuration files you need to paste the result of
echo "<hex.seed>" | xxd -r -p | base64
into the PrivateSeed
field in the
NodeConfig
section.
Testing gnunet-go
To test the current gnunet-go
implementation in a local GNUnet environment,
you should follow the detailed instructions below.
N.B.: Testing requires an up-to-date GNUnet build from source. You can either use your local machine (please follow the GNUnet documentation for setup) or you can simply use a Docker image like gnunet-docker for this.
Testing R5N DHT
gnunet-go
implements the DHT protocol specified in
lsd0004 and uses a custom (unencrypted)
transport protocol not supported by the standard GNUnet. Luckily there is a
testbed in GNUnet that allows to run the new protocol over UDP/IP.
Starting the DHTU testbed
Make sure you stopped (or have not started) all GNUnet services; the testbed will take care of everything required.
Change into ./src/dht
in the gnunet
-Repository and start any number of
DHTU nodes for testing:
./dhtu_testbed_deploy.sh 10
will start ten DHTU nodes. Nodes will listen to all available network addresses on port 10000+ (one node, one port).
Log and configuration files can be found in /tmp/deployment/
; they are
named by index (starting at 0).
Running the gnunet-go
node in the testbed
Setting up the configuration file
Copy the example gnunet-config.json
to dhtu-config.json
and modify the
network
and local
sections to our local setup. In this example
172.17.0.5
is the network address for GNUnet DHTU nodes and 172.17.0.1
is the network address for gnunet-go
:
{
"network": {
"bootstrap": [
"ip+udp://127.17.0.5:10000"
],
"numPeers": 10
},
"local": {
"privateSeed": "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=",
"endpoints": [
{
"id": "r5n",
"network": "ip+udp",
"address": "172.17.0.1",
"port": 2086,
"ttl": 86400
}
]
}
:
}
The above configuration will expect a network of 10 nodes and has a single
bootstrap node (the first DHTU node in the testbed). gnunet-go
will listen
on port 2086.
Check the configuration for path definitions (especially /var/libg/gnunet/...
)
and make sure the folders exist and have R/W permissions for the user running
the test.
Running the gnunet-go
node
Run the following commands to start the gnunet-go
node:
rm -f /tmp/gnunet-system-runtime/*-go.sock
${GOPATH}/bin/gnunet-service-dht-go -c dhtu-config.json 2>&1 | tee run.log
Testing GNS
N.B.: The GNS service is currently not up-to-date. To test it, you need to check out version v0.1.23 (the latest tested version) and a matching GNUnet version as well (latest as of May 2020) to be on a safe side. You also need to have (all) GNUnet services up and running.
Setting up a modified configuration for GNUnet
You need to tell the GNUnet client which GNS service to use (either the default
or the gnunet-go
version) by modifying the GNS service socket. Copy your
configuration file to gns-go.comf
and modify the [gns]
sectiom:
UNIXPATH = $GNUNET_RUNTIME_DIR/gnunet-service-gns-go.sock
This will ensure that clients (and other services) talk to the gnunet-go
GNS service.
Setting up the configuration file (gnunet-go)
Copy the example gnunet-config.json
to gns-config.json
and modify the
network
and local
sections:
{
"network": {
"bootstrap": [],
"numPeers": 10
},
"local": {
"privateSeed": "YGoe6XFH3XdvFRl+agx9gIzPTvxA229WFdkazEMdcOs=",
"endpoints": []
},
:
}
Preparing and running the tests
For test purposes you need to start the gnunet-go
DNS service, generate
zones and resource records for testing and run the actual test cases.
You can use the follwing script to do it all in one go:
#!/bin/bash
GNS_SOCK=/tmp/gnunet-system-runtime/gnunet-service-gns-go.sock
[ -e ${GNS_SOCK} ] && sudo rm -f ${GNS_SOCK}
sudo -u gnunet ../bin/gnunet-service-gns-go -L 5 &
GOGNS=$!
function get_pkey() {
gnunet-identity -d -e $1 | sed 's/.* - //'
}
CERT=$(openssl x509 -in <(openssl s_client -connect gnunet.org:443 </dev/null 2>/dev/null) -outform der \
| od -t x1 -A n \
| tr "\n" " " \
| sed "s/ //g")
VPN="ZH7W4PR933913VA45AH45GH9QNQVP3TEM89J18549Q6RNDV75A4G secret"
for x in zone9 private; do
gnunet-identity -D $x
done
gnunet-identity -C zone9
gnunet-identity -C private
gnunet-namestore -a -z zone9 -n "@" -t NICK -V "zone9" -e never
gnunet-namestore -a -z zone9 -n web -t A -V 131.159.74.67 -e never
gnunet-namestore -a -z zone9 -n web -t BOX -V "6 443 52 3 0 0 ${CERT}" -e never
gnunet-namestore -a -z zone9 -n gn -t CNAME -V gnunet.org -e never
gnunet-namestore -a -z zone9 -n sec -t VPN -V "6 ${VPN}" -e never
gnunet-namestore -a -z zone9 -n prv -t PKEY -V "$(get_pkey private)" -e never
# gnunet-namestore -a -z zone9 -n prv -t A -V "14.15.16.17" -e never
gnunet-namestore -a -z zone9 -n old -t LEHO -V "old.gnunet.org" -e never
gnunet-namestore -a -z zone9 -n old -t A -V 5.6.7.8 -e never
# gnunet-namestore -a -z zone9 -n old -t A -V 10.11.12.13 -e never
# gnunet-namestore -a -z zone9 -n old -t TXT -V "Old version" -e never
gnunet-namestore -a -z private -n "@" -t NICK -V "nexus9" -e never
gnunet-namestore -a -z private -n name -t TXT -V "GNUnet test" -e never
gnunet-namestore -d -z private -n host
gnunet-namestore -a -z private -n host -t GNS2DNS -V "[email protected]" -e never
# gnunet-namestore -a -z private -n host -t A -V 1.2.3.4 -e never
function test_gns() {
echo "========================"
echo -n "Testing '$2' for type '$1': "
gnunet-gns -t $1 -u $2 > plain.out
gnunet-gns -c gns-go.conf -t $1 -u $2 > go.out
rc=$(diff plain.out go.out)
if [ -z "$rc" ]; then
echo "O.K."
else
echo "FAILED!"
echo "---------------- GNS-C"
cat plain.out
echo "---------------- GNS-Go"
cat go.out
fi
}
# (1)
test_gns any web.zone9
# (2)
test_gns any _443._tcp.web.zone9
# (3)
test_gns nick zone9
# (4)
test_gns any gn.zone9
# (5)
test_gns any sec.zone9
# (6)
test_gns pkey prv.zone9
# (7)
test_gns nick prv.zone9
# (8)
test_gns any name.prv.zone9
# (9)
test_gns any host.prv.zone9
# (10)
test_gns a host.prv.zone9
kill ${GOGNS}
Using gnunet-go in your own projects
gnunet-go
is not a standard Go module for direct use (via go.mod) in other
packages, but designed as a stand-alone application. The rationale behind was
to not hard link the code to a single Git provider.
If you are interested in using (parts of) gnunet-go
in your own projects, the
following step-by-step instructions show the easiest route.
-
git clone https://github.com/bfix/gnunet-go
into folder/home/user/gnunet-go
(or any other folder if you adjust the instructions accordingly). -
create project folder and change into it
-
run
go mod init test
(replace test with the name of your project) -
create a simple test
main.go
package main
import (
"crypto/rand"
"fmt"
"gnunet/util"
)
func main() {
a := make([]byte, 32)
rand.Read(a)
fmt.Println(util.EncodeBinaryToString(a))
}
- edit
go.mod
and add at end of file:
require gnunet v0.1.34
replace gnunet v0.1.34 => /home/user/gnunet-go/src/gnunet
- run
go mod tidy
- build test program:
go build
- run test program:
./test
The only disadvantage of this approach is that you have to update the source
code for gnunet-go
yourself. From time to time or on demand, do a git pull
followed by a go mod tidy
described above. No version control is supported
either because the dependency for gnunet-go
is redirected to a local folder.