[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnunet-go] branch master updated: Milestone #3 (RC1)
From: |
gnunet |
Subject: |
[gnunet-go] branch master updated: Milestone #3 (RC1) |
Date: |
Sun, 24 May 2020 13:40:05 +0200 |
This is an automated email from the git hooks/post-receive script.
bernd-fix pushed a commit to branch master
in repository gnunet-go.
The following commit(s) were added to refs/heads/master by this push:
new fdf2318 Milestone #3 (RC1)
fdf2318 is described below
commit fdf23180919ec42422694f1f1801eead1ea576e8
Author: Bernd Fix <address@hidden>
AuthorDate: Sun May 24 13:34:05 2020 +0200
Milestone #3 (RC1)
---
src/cmd/gnunet-service-revocation-go/main.go | 105 ++++++++
src/cmd/pow-test/main.go | 51 ----
src/cmd/revoke-zonekey/main.go | 192 +++++++++++++++
src/gnunet/config/config.go | 18 +-
src/gnunet/message/factory.go | 2 +-
src/gnunet/message/msg_revocation.go | 35 ++-
src/gnunet/modules.go | 19 +-
src/gnunet/service/gns/module.go | 21 +-
src/gnunet/service/gns/service.go | 59 +++++
src/gnunet/service/revocation/module.go | 124 ++++++++++
src/gnunet/service/revocation/pow.go | 346 ++++++++++++++++-----------
src/gnunet/service/revocation/service.go | 160 +++++++++++++
src/gnunet/util/database.go | 72 ++++++
src/gnunet/util/key_value_store.go | 188 +++++++++++++++
src/gnunet/util/time.go | 12 +
15 files changed, 1172 insertions(+), 232 deletions(-)
diff --git a/src/cmd/gnunet-service-revocation-go/main.go
b/src/cmd/gnunet-service-revocation-go/main.go
new file mode 100644
index 0000000..c829aea
--- /dev/null
+++ b/src/cmd/gnunet-service-revocation-go/main.go
@@ -0,0 +1,105 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package main
+
+import (
+ "flag"
+ "os"
+ "os/signal"
+ "syscall"
+ "time"
+
+ "github.com/bfix/gospel/logger"
+ "gnunet/config"
+ "gnunet/service"
+ "gnunet/service/revocation"
+)
+
+func main() {
+ defer func() {
+ logger.Println(logger.INFO, "[revocation] Bye.")
+ // flush last messages
+ logger.Flush()
+ }()
+ logger.Println(logger.INFO, "[revocation] Starting service...")
+
+ var (
+ cfgFile string
+ srvEndp string
+ err error
+ logLevel int
+ )
+ // handle command line arguments
+ flag.StringVar(&cfgFile, "c", "gnunet-config.json", "GNUnet
configuration file")
+ flag.StringVar(&srvEndp, "s", "", "REVOCATION service end-point")
+ flag.IntVar(&logLevel, "L", logger.INFO, "REVOCATION log level
(default: INFO)")
+ flag.Parse()
+
+ // read configuration file and set missing arguments.
+ if err = config.ParseConfig(cfgFile); err != nil {
+ logger.Printf(logger.ERROR, "[revocation] Invalid configuration
file: %s\n", err.Error())
+ return
+ }
+
+ // apply configuration
+ logger.SetLogLevel(logLevel)
+ if len(srvEndp) == 0 {
+ srvEndp = config.Cfg.GNS.Endpoint
+ }
+
+ // start a new REVOCATION service
+ rvc := revocation.NewRevocationService()
+ srv := service.NewServiceImpl("revocation", rvc)
+ if err = srv.Start(srvEndp); err != nil {
+ logger.Printf(logger.ERROR, "[revocation] Error: '%s'\n",
err.Error())
+ return
+ }
+
+ // handle OS signals
+ sigCh := make(chan os.Signal, 5)
+ signal.Notify(sigCh)
+
+ // heart beat
+ tick := time.NewTicker(5 * time.Minute)
+
+loop:
+ for {
+ select {
+ // handle OS signals
+ case sig := <-sigCh:
+ switch sig {
+ case syscall.SIGKILL, syscall.SIGINT, syscall.SIGTERM:
+ logger.Printf(logger.INFO, "[revocation]
Terminating service (on signal '%s')\n", sig)
+ break loop
+ case syscall.SIGHUP:
+ logger.Println(logger.INFO, "[revocation]
SIGHUP")
+ case syscall.SIGURG:
+ // TODO:
https://github.com/golang/go/issues/37942
+ default:
+ logger.Println(logger.INFO, "[revocation]
Unhandled signal: "+sig.String())
+ }
+ // handle heart beat
+ case now := <-tick.C:
+ logger.Println(logger.INFO, "[revocation] Heart beat at
"+now.String())
+ }
+ }
+
+ // terminating service
+ srv.Stop()
+}
diff --git a/src/cmd/pow-test/main.go b/src/cmd/pow-test/main.go
deleted file mode 100644
index 247f442..0000000
--- a/src/cmd/pow-test/main.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package main
-
-import (
- "encoding/hex"
- "flag"
- "fmt"
- "log"
-
- "gnunet/service/revocation"
-
- "github.com/bfix/gospel/crypto/ed25519"
- "github.com/bfix/gospel/math"
-)
-
-func main() {
- var (
- quiet bool
- bits int
- )
- flag.IntVar(&bits, "b", 25, "Number of leading zero bits")
- flag.BoolVar(&quiet, "q", false, "Be quiet")
- flag.Parse()
-
- // pre-set difficulty
- fmt.Printf("Leading zeros required: %d\n", bits)
- difficulty := math.TWO.Pow(512 - bits).Sub(math.ONE)
- fmt.Printf("==> Difficulty: %v\n", difficulty)
-
- // generate a random key pair
- pkey, _ := ed25519.NewKeypair()
-
- // initialize RevData structure
- rd := revocation.NewRevData(0, pkey)
-
- var count uint64 = 0
- for {
- result, err := rd.Compute()
- if err != nil {
- log.Fatal(err)
- }
- //fmt.Printf("Nonce=%d, Result=(%d) %v\n", rd.GetNonce(),
result.BitLen(), result)
- if result.Cmp(difficulty) < 0 {
- break
- }
- count++
- rd.Next()
- }
- fmt.Printf("PoW found after %d iterations:\n", count)
- fmt.Printf("--> Nonce=%d\n", rd.GetNonce())
- fmt.Printf(" REV = %s\n", hex.EncodeToString(rd.GetBlob()))
-}
diff --git a/src/cmd/revoke-zonekey/main.go b/src/cmd/revoke-zonekey/main.go
new file mode 100644
index 0000000..2bbd90f
--- /dev/null
+++ b/src/cmd/revoke-zonekey/main.go
@@ -0,0 +1,192 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package main
+
+import (
+ "context"
+ "encoding/hex"
+ "flag"
+ "log"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+
+ "gnunet/service/revocation"
+ "gnunet/util"
+
+ "github.com/bfix/gospel/crypto/ed25519"
+ "github.com/bfix/gospel/data"
+)
+
+func main() {
+ log.Println("*** Compute revocation data for a zone key")
+ log.Println("*** Copyright (c) 2020, Bernd Fix >Y<")
+ log.Println("*** This is free software distributed under the Affero GPL
v3.")
+
+ // handle command line arguments
+ var (
+ verbose bool // be verbose with messages
+ bits int // number of leading zero-bit requested
+ zonekey string // zonekey to be revoked
+ filename string // name of file for persistance
+ )
+ flag.IntVar(&bits, "b", 25, "Number of leading zero bits")
+ flag.BoolVar(&verbose, "v", false, "verbose output")
+ flag.StringVar(&zonekey, "z", "", "Zone key to be revoked")
+ flag.StringVar(&filename, "f", "", "Name of file to store revocation")
+ flag.Parse()
+
+ // define layout of persistant data
+ var revData struct {
+ Rd *revocation.RevData // Revocation data
+ T util.RelativeTime // time spend in calculations
+ Last uint64 // last value used for PoW test
+ Numbits uint8 // number of leading zero-bits
+ }
+ dataBuf := make([]byte, 377)
+
+ // read revocation object from file
+ file, err := os.Open(filename)
+ cont := true
+ if err != nil {
+ if len(zonekey) != 52 {
+ log.Fatal("Missing or invalid zonekey and no file
specified -- aborting")
+ }
+ keyData, err := util.DecodeStringToBinary(zonekey, 32)
+ if err != nil {
+ log.Fatal("Invalid zonekey: " + err.Error())
+ }
+ pkey := ed25519.NewPublicKeyFromBytes(keyData)
+ revData.Rd = revocation.NewRevData(util.AbsoluteTimeNow(), pkey)
+ revData.Numbits = uint8(bits)
+ revData.T = util.NewRelativeTime(0)
+ cont = false
+ } else {
+ n, err := file.Read(dataBuf)
+ if err != nil {
+ log.Fatal("Error reading file: " + err.Error())
+ }
+ if n != len(dataBuf) {
+ log.Fatal("File corrupted -- aborting")
+ }
+ if err = data.Unmarshal(&revData, dataBuf); err != nil {
+ log.Fatal("File corrupted: " + err.Error())
+ }
+ bits = int(revData.Numbits)
+ if err = file.Close(); err != nil {
+ log.Fatal("Error closing file: " + err.Error())
+ }
+ }
+
+ if cont {
+ log.Printf("Revocation calculation started at %s\n",
revData.Rd.Timestamp.String())
+ log.Printf("Time spent on calculation: %s\n",
revData.T.String())
+ log.Printf("Last tested PoW value: %d\n", revData.Last)
+ log.Println("Continuing...")
+ } else {
+ log.Println("Starting new revocation calculation...")
+ }
+ log.Println("Press ^C to abort...")
+
+ // pre-set difficulty
+ log.Printf("Difficulty: %d\n", bits)
+ if bits < 25 {
+ log.Println("WARNING: difficulty is less than 25!")
+ }
+
+ // Start or continue calculation
+ startTime := util.AbsoluteTimeNow()
+ ctx, cancelFcn := context.WithCancel(context.Background())
+ wg := new(sync.WaitGroup)
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ if result, last := revData.Rd.Compute(ctx, bits, revData.Last);
result != 32 {
+ log.Printf("Incomplete revocation: Only %d of 32 PoWs
available!\n", result)
+ revData.Last = last
+ revData.T = util.AbsoluteTimeNow().Diff(startTime)
+ log.Println("Writing revocation data to file...")
+ file, err := os.Create(filename)
+ if err != nil {
+ log.Fatal("Can't write to output file: " +
err.Error())
+ }
+ buf, err := data.Marshal(&revData)
+ if err != nil {
+ log.Fatal("Internal error: " + err.Error())
+ }
+ if len(buf) != len(dataBuf) {
+ log.Fatal("Internal error: Buffer mismatch")
+ }
+ n, err := file.Write(buf)
+ if err != nil {
+ log.Fatal("Can't write to output file: " +
err.Error())
+ }
+ if n != len(dataBuf) {
+ log.Fatal("Can't write data to output file!")
+ }
+ if err = file.Close(); err != nil {
+ log.Fatal("Error closing file: " + err.Error())
+ }
+ } else {
+ log.Println("Revocation data object:")
+ log.Println(" 0x" +
hex.EncodeToString(revData.Rd.Blob()))
+ log.Println("Status:")
+ rc := revData.Rd.Verify()
+ switch {
+ case rc == -1:
+ log.Println(" Missing/invalid signature")
+ case rc == -2:
+ log.Println(" Expired revocation")
+ case rc == -3:
+ log.Println(" Wrong PoW sequence order")
+ case rc < 25:
+ log.Println(" Difficulty to small")
+ default:
+ log.Printf(" Difficulty: %d\n", rc)
+ }
+ }
+ }()
+
+ go func() {
+ // handle OS signals
+ sigCh := make(chan os.Signal, 5)
+ signal.Notify(sigCh)
+ loop:
+ for {
+ select {
+ // handle OS signals
+ case sig := <-sigCh:
+ switch sig {
+ case syscall.SIGKILL, syscall.SIGINT,
syscall.SIGTERM:
+ log.Printf("Terminating (on signal
'%s')\n", sig)
+ cancelFcn()
+ break loop
+ case syscall.SIGHUP:
+ log.Println("SIGHUP")
+ case syscall.SIGURG:
+ // TODO:
https://github.com/golang/go/issues/37942
+ default:
+ log.Println("Unhandled signal: " +
sig.String())
+ }
+ }
+ }
+ }()
+ wg.Wait()
+}
diff --git a/src/gnunet/config/config.go b/src/gnunet/config/config.go
index 01dadea..690b186 100644
--- a/src/gnunet/config/config.go
+++ b/src/gnunet/config/config.go
@@ -54,6 +54,15 @@ type NamecacheConfig struct {
Endpoint string `json:"endpoint"` // end-point of Namecache service
}
+///////////////////////////////////////////////////////////////////////
+// Revocation configuration
+
+// RevocationConfig
+type RevocationConfig struct {
+ Endpoint string `json:"endpoint"` // end-point of Revocation service
+ Storage string `json:"storage"` // persistance mechanism for
revocation data
+}
+
///////////////////////////////////////////////////////////////////////
// Environment settings
@@ -61,10 +70,11 @@ type Environ map[string]string
// Config is the aggregated configuration for GNUnet.
type Config struct {
- Env Environ `json:"environ"`
- DHT *DHTConfig `json:"dht"`
- GNS *GNSConfig `json:"gns"`
- Namecache *NamecacheConfig `json:"namecache"`
+ Env Environ `json:"environ"`
+ DHT *DHTConfig `json:"dht"`
+ GNS *GNSConfig `json:"gns"`
+ Namecache *NamecacheConfig `json:"namecache"`
+ Revocation *RevocationConfig `json:"revocation"`
}
var (
diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go
index 33e806b..68a24d5 100644
--- a/src/gnunet/message/factory.go
+++ b/src/gnunet/message/factory.go
@@ -92,7 +92,7 @@ func NewEmptyMessage(msgType uint16) (Message, error) {
case REVOCATION_QUERY_RESPONSE:
return NewRevocationQueryResponseMsg(true), nil
case REVOCATION_REVOKE:
- return NewRevocationRevokeMsg(0, nil, nil), nil
+ return NewRevocationRevokeMsg(nil, nil), nil
case REVOCATION_REVOKE_RESPONSE:
return NewRevocationRevokeResponseMsg(false), nil
}
diff --git a/src/gnunet/message/msg_revocation.go
b/src/gnunet/message/msg_revocation.go
index fea727c..8c7db06 100644
--- a/src/gnunet/message/msg_revocation.go
+++ b/src/gnunet/message/msg_revocation.go
@@ -21,8 +21,6 @@ package message
import (
"fmt"
- "gnunet/crypto"
- "gnunet/enums"
"gnunet/util"
"github.com/bfix/gospel/crypto/ed25519"
@@ -104,28 +102,23 @@ func (msg *RevocationQueryResponseMsg) Header()
*MessageHeader {
// RevocationRevokeMsg
type RevocationRevokeMsg struct {
- MsgSize uint16 `order:"big"` // total size of
message
- MsgType uint16 `order:"big"` // REVOCATION_QUERY
(636)
- Reserved uint32 `order:"big"` // Reserved for future
use
- PoW uint64 `order:"big"` // Proof-of-work:
nonce that satisfy condition
- Signature []byte `size:"64"` // Signature of the
revocation.
- Purpose *crypto.SignaturePurpose // Size and purpose of signature (8
bytes)
- ZoneKey []byte `size:"32"` // Zone key to be revoked
+ MsgSize uint16 `order:"big"` // total size of message
+ MsgType uint16 `order:"big"` // REVOCATION_REVOKE (638)
+ Timestamp util.AbsoluteTime // Timestamp of revocation creation
+ PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of
PoW values
+ Signature []byte `size:"64"` // Signature
(Proof-of-ownership).
+ ZoneKey []byte `size:"32"` // public zone key
to be revoked
}
// NewRevocationRevokeMsg creates a new message for a given zone.
-func NewRevocationRevokeMsg(pow uint64, zoneKey *ed25519.PublicKey, sig
*ed25519.EcSignature) *RevocationRevokeMsg {
+func NewRevocationRevokeMsg(zoneKey *ed25519.PublicKey, sig
*ed25519.EcSignature) *RevocationRevokeMsg {
msg := &RevocationRevokeMsg{
- MsgSize: 120,
+ MsgSize: 364,
MsgType: REVOCATION_REVOKE,
- Reserved: 0,
- PoW: pow,
+ Timestamp: util.AbsoluteTimeNow(),
+ PoWs: make([]uint64, 32),
Signature: make([]byte, 64),
- Purpose: &crypto.SignaturePurpose{
- Size: 40,
- Purpose: enums.SIG_REVOCATION,
- },
- ZoneKey: make([]byte, 32),
+ ZoneKey: make([]byte, 32),
}
if zoneKey != nil {
copy(msg.ZoneKey, zoneKey.Bytes())
@@ -138,7 +131,7 @@ func NewRevocationRevokeMsg(pow uint64, zoneKey
*ed25519.PublicKey, sig *ed25519
// String returns a human-readable representation of the message.
func (m *RevocationRevokeMsg) String() string {
- return fmt.Sprintf("RevocationRevokeMsg{pow=%d,zone=%s}", m.PoW,
util.EncodeBinaryToString(m.ZoneKey))
+ return fmt.Sprintf("RevocationRevokeMsg{zone=%s}",
util.EncodeBinaryToString(m.ZoneKey))
}
// Header returns the message header in a separate instance.
@@ -153,8 +146,8 @@ func (msg *RevocationRevokeMsg) Header() *MessageHeader {
// RevocationRevokeResponseMsg
type RevocationRevokeResponseMsg struct {
MsgSize uint16 `order:"big"` // total size of message
- MsgType uint16 `order:"big"` // REVOCATION_QUERY_RESPONSE (637)
- Success uint32 `order:"big"` // Revoke successful?
+ MsgType uint16 `order:"big"` // REVOCATION_REVOKE_RESPONSE (639)
+ Success uint32 `order:"big"` // Revoke successful? (0=no, 1=yes)
}
// NewRevocationRevokeResponseMsg creates a new response for a query.
diff --git a/src/gnunet/modules.go b/src/gnunet/modules.go
index 063b914..24dc92b 100644
--- a/src/gnunet/modules.go
+++ b/src/gnunet/modules.go
@@ -32,13 +32,15 @@ import (
"gnunet/service/dht"
"gnunet/service/gns"
"gnunet/service/namecache"
+ "gnunet/service/revocation"
)
// List of all GNUnet service module instances
type Instances struct {
- GNS *gns.GNSModule
- Namecache *namecache.NamecacheModule
- DHT *dht.DHTModule
+ GNS *gns.GNSModule
+ Namecache *namecache.NamecacheModule
+ DHT *dht.DHTModule
+ Revocation *revocation.RevocationModule
}
// Local reference to instance list
@@ -55,10 +57,15 @@ func init() {
// DHT (no calls to other modules)
Modules.DHT = new(dht.DHTModule)
+ // Revocation (no calls to other modules)
+ Modules.Revocation = revocation.NewRevocationModule()
+
// GNS (calls Namecache, DHT and Identity)
Modules.GNS = &gns.GNSModule{
- LookupLocal: Modules.Namecache.Get,
- StoreLocal: Modules.Namecache.Put,
- LookupRemote: Modules.DHT.Get,
+ LookupLocal: Modules.Namecache.Get,
+ StoreLocal: Modules.Namecache.Put,
+ LookupRemote: Modules.DHT.Get,
+ RevocationQuery: Modules.Revocation.Query,
+ RevocationRevoke: Modules.Revocation.Revoke,
}
}
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
index e885d2d..5e787d5 100644
--- a/src/gnunet/service/gns/module.go
+++ b/src/gnunet/service/gns/module.go
@@ -27,6 +27,7 @@ import (
"gnunet/enums"
"gnunet/message"
"gnunet/service"
+ "gnunet/service/revocation"
"gnunet/util"
"github.com/bfix/gospel/crypto/ed25519"
@@ -111,9 +112,11 @@ func NewQuery(pkey *ed25519.PublicKey, label string)
*Query {
// GNSModule handles the resolution of GNS names to RRs bundled in a block.
type GNSModule struct {
// Use function references for calls to methods in other modules:
- LookupLocal func(ctx *service.SessionContext, query *Query)
(*message.GNSBlock, error)
- StoreLocal func(ctx *service.SessionContext, block *message.GNSBlock)
error
- LookupRemote func(ctx *service.SessionContext, query *Query)
(*message.GNSBlock, error)
+ LookupLocal func(ctx *service.SessionContext, query *Query)
(*message.GNSBlock, error)
+ StoreLocal func(ctx *service.SessionContext, block
*message.GNSBlock) error
+ LookupRemote func(ctx *service.SessionContext, query *Query)
(*message.GNSBlock, error)
+ RevocationQuery func(ctx *service.SessionContext, pkey
*ed25519.PublicKey) (valid bool, err error)
+ RevocationRevoke func(ctx *service.SessionContext, rd
*revocation.RevData) (success bool, err error)
}
// Resolve a GNS name with multiple labels. If pkey is not nil, the name
@@ -158,6 +161,12 @@ func (gns *GNSModule) ResolveAbsolute(
err = ErrUnknownTLD
return
}
+ // check if zone key has been revoked
+ var valid bool
+ set = message.NewGNSRecordSet()
+ if valid, err = gns.RevocationQuery(ctx, pkey); err != nil || !valid {
+ return
+ }
// continue with resolution relative to a zone.
return gns.ResolveRelative(ctx, labels[1:], pkey, kind, mode, depth)
}
@@ -229,6 +238,12 @@ func (gns *GNSModule) ResolveRelative(
if len(labels) == 1 &&
!kind.HasType(enums.GNS_TYPE_PKEY) {
labels = append(labels, "@")
}
+ // check if zone key has been revoked
+ if valid, err := gns.RevocationQuery(ctx, pkey); err !=
nil || !valid {
+ // revoked key -> no results!
+ records = make([]*message.GNSResourceRecord, 0)
+ break
+ }
} else if hdlr := hdlrs.GetHandler(enums.GNS_TYPE_GNS2DNS);
hdlr != nil {
// (2) GNS2DNS records
inst := hdlr.(*Gns2DnsHandler)
diff --git a/src/gnunet/service/gns/service.go
b/src/gnunet/service/gns/service.go
index 464e622..f6310de 100644
--- a/src/gnunet/service/gns/service.go
+++ b/src/gnunet/service/gns/service.go
@@ -28,6 +28,7 @@ import (
"gnunet/enums"
"gnunet/message"
"gnunet/service"
+ "gnunet/service/revocation"
"gnunet/transport"
"gnunet/util"
@@ -59,6 +60,8 @@ func NewGNSService() service.Service {
inst.LookupLocal = inst.LookupNamecache
inst.StoreLocal = inst.StoreNamecache
inst.LookupRemote = inst.LookupDHT
+ inst.RevocationQuery = inst.QueryKeyRevocation
+ inst.RevocationRevoke = inst.RevokeKey
return inst
}
@@ -167,6 +170,60 @@ loop:
ctx.Cancel()
}
+//======================================================================
+
+//
+func (s *GNSService) QueryKeyRevocation(ctx *service.SessionContext, pkey
*ed25519.PublicKey) (valid bool, err error) {
+ logger.Printf(logger.DBG, "[gns] QueryKeyRev(%s)...\n",
util.EncodeBinaryToString(pkey.Bytes()))
+
+ // assemble request
+ req := message.NewRevocationQueryMsg(pkey)
+
+ // get response from Revocation service
+ var resp message.Message
+ if resp, err = service.ServiceRequestResponse(ctx, "gns", "Revocation",
config.Cfg.Revocation.Endpoint, req); err != nil {
+ return
+ }
+
+ // handle message depending on its type
+ logger.Println(logger.DBG, "[gns] Handling response from Revocation
service")
+ valid = false
+ switch m := resp.(type) {
+ case *message.RevocationQueryResponseMsg:
+ valid = (m.Valid == 1)
+ }
+ return
+}
+
+//
+func (s *GNSService) RevokeKey(ctx *service.SessionContext, rd
*revocation.RevData) (success bool, err error) {
+ logger.Printf(logger.DBG, "[gns] RevokeKey(%s)...\n",
util.EncodeBinaryToString(rd.ZoneKey))
+
+ // assemble request
+ req := message.NewRevocationRevokeMsg(nil, nil)
+ req.Timestamp = rd.Timestamp
+ copy(req.PoWs, rd.PoWs)
+ copy(req.Signature, rd.Signature)
+ copy(req.ZoneKey, rd.ZoneKey)
+
+ // get response from Revocation service
+ var resp message.Message
+ if resp, err = service.ServiceRequestResponse(ctx, "gns", "Revocation",
config.Cfg.Revocation.Endpoint, req); err != nil {
+ return
+ }
+
+ // handle message depending on its type
+ logger.Println(logger.DBG, "[gns] Handling response from Revocation
service")
+ success = false
+ switch m := resp.(type) {
+ case *message.RevocationRevokeResponseMsg:
+ success = (m.Success == 1)
+ }
+ return
+}
+
+//======================================================================
+
// LookupNamecache
func (s *GNSService) LookupNamecache(ctx *service.SessionContext, query
*Query) (block *message.GNSBlock, err error) {
logger.Printf(logger.DBG, "[gns] LookupNamecache(%s)...\n",
hex.EncodeToString(query.Key.Bits))
@@ -266,6 +323,8 @@ func (s *GNSService) StoreNamecache(ctx
*service.SessionContext, block *message.
return
}
+//======================================================================
+
// LookupDHT
func (s *GNSService) LookupDHT(ctx *service.SessionContext, query *Query)
(block *message.GNSBlock, err error) {
logger.Printf(logger.DBG, "[gns] LookupDHT(%s)...\n",
hex.EncodeToString(query.Key.Bits))
diff --git a/src/gnunet/service/revocation/module.go
b/src/gnunet/service/revocation/module.go
new file mode 100644
index 0000000..b5c8a16
--- /dev/null
+++ b/src/gnunet/service/revocation/module.go
@@ -0,0 +1,124 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package revocation
+
+import (
+ "gnunet/config"
+ "gnunet/service"
+ "gnunet/util"
+
+ "github.com/bfix/gospel/crypto/ed25519"
+ "github.com/bfix/gospel/data"
+ "github.com/bfix/gospel/logger"
+)
+
+//======================================================================
+// "GNUnet Revocation" implementation
+//======================================================================
+
+// RevocationModule handles the revocation-related calls to other modules.
+type RevocationModule struct {
+ bloomf *data.BloomFilter // bloomfilter for fast revocation check
+ kvs util.KeyValueStore // storage for known revocations
+}
+
+// Init a revocation module
+func (m *RevocationModule) Init() error {
+ // Initialize access to revocation data storage
+ var err error
+ if m.kvs, err = util.OpenKVStore(config.Cfg.Revocation.Storage); err !=
nil {
+ return err
+ }
+ // traverse the storage and build bloomfilter for all keys
+ m.bloomf = data.NewBloomFilter(1000000, 1e-8)
+ keys, err := m.kvs.List()
+ if err != nil {
+ return err
+ }
+ for _, key := range keys {
+ buf, err := util.DecodeStringToBinary(key, 32)
+ if err != nil {
+ return err
+ }
+ m.bloomf.Add(buf)
+ }
+ return nil
+}
+
+// NewRevocationModule returns an initialized revocation module
+func NewRevocationModule() *RevocationModule {
+ m := new(RevocationModule)
+ if err := m.Init(); err != nil {
+ logger.Printf(logger.ERROR, "[revocation] Failed to initialize
module: %s\n", err.Error())
+ return nil
+ }
+ return m
+}
+
+// Query return true if the pkey is valid (not revoked) and false
+// if the pkey has been revoked.
+func (s *RevocationModule) Query(ctx *service.SessionContext, pkey
*ed25519.PublicKey) (valid bool, err error) {
+ // fast check first: is the key in the bloomfilter?
+ data := pkey.Bytes()
+ if !s.bloomf.Contains(data) {
+ // no: it is valid (not revoked)
+ return true, nil
+ }
+ // check in store to detect false-positives
+ key := util.EncodeBinaryToString(data)
+ if _, err = s.kvs.Get(key); err != nil {
+ logger.Printf(logger.ERROR, "[revocation] Failed to locate key
'%s' in store: %s\n", key, err.Error())
+ // assume not revoked...
+ return true, err
+ }
+ // key seems to be revoked
+ return false, nil
+}
+
+// Revoke
+func (s *RevocationModule) Revoke(ctx *service.SessionContext, rd *RevData)
(success bool, err error) {
+ // verify the revocation data
+ rc := rd.Verify()
+ switch {
+ case rc == -1:
+ logger.Println(logger.WARN, "[revocation] Revoke:
Missing/invalid signature")
+ return false, nil
+ case rc == -2:
+ logger.Println(logger.WARN, "[revocation] Revoke: Expired
revocation")
+ return false, nil
+ case rc == -3:
+ logger.Println(logger.WARN, "[revocation] Revoke: Wrong PoW
sequence order")
+ return false, nil
+ case rc < 25:
+ logger.Println(logger.WARN, "[revocation] Revoke: Difficulty to
small")
+ return false, nil
+ }
+ // store the revocation data
+ // (1) add it to the bloomfilter
+ s.bloomf.Add(rd.ZoneKey)
+ // (2) add it to the store
+ var buf []byte
+ key := util.EncodeBinaryToString(rd.ZoneKey)
+ if buf, err = data.Marshal(rd); err != nil {
+ return false, err
+ }
+ value := util.EncodeBinaryToString(buf)
+ err = s.kvs.Put(key, value)
+ return true, err
+}
diff --git a/src/gnunet/service/revocation/pow.go
b/src/gnunet/service/revocation/pow.go
index 07c6241..f4b6b9d 100644
--- a/src/gnunet/service/revocation/pow.go
+++ b/src/gnunet/service/revocation/pow.go
@@ -20,71 +20,74 @@ package revocation
import (
"bytes"
- "crypto/cipher"
- "crypto/sha256"
- "crypto/sha512"
+ "context"
"encoding/binary"
- "sync"
+ "time"
+ "gnunet/crypto"
+ "gnunet/enums"
+ "gnunet/message"
"gnunet/util"
"github.com/bfix/gospel/crypto/ed25519"
"github.com/bfix/gospel/data"
"github.com/bfix/gospel/math"
- "golang.org/x/crypto/hkdf"
- "golang.org/x/crypto/scrypt"
- "golang.org/x/crypto/twofish"
+ "golang.org/x/crypto/argon2"
)
//----------------------------------------------------------------------
-// Revocation data
+// Proof-of-Work data
//----------------------------------------------------------------------
-// RevData is the revocation data structure (wire format)
-type RevData struct {
- Nonce uint64 `order:"big"` // start with this nonce value
- ZoneKey []byte `size:"32"` // public zone key to be revoked
+// PoWData is the proof-of-work data
+type PoWData struct {
+ PoW uint64 `order:"big"` // start with this PoW value
+ Timestamp util.AbsoluteTime // Timestamp of creation
+ ZoneKey []byte `size:"32"` // public zone key to be revoked
// transient attributes (not serialized)
blob []byte // binary representation of serialized data
}
-// NewRevData creates a RevData instance for the given arguments.
-func NewRevData(nonce uint64, zoneKey *ed25519.PublicKey) *RevData {
- rd := &RevData{
- Nonce: nonce,
- ZoneKey: make([]byte, 32),
+// NewPoWData creates a PoWData instance for the given arguments.
+func NewPoWData(pow uint64, ts util.AbsoluteTime, zoneKey []byte) *PoWData {
+ rd := &PoWData{
+ PoW: 0,
+ Timestamp: ts,
+ ZoneKey: zoneKey,
}
- copy(rd.ZoneKey, zoneKey.Bytes())
- blob, err := data.Marshal(rd)
- if err != nil {
+ if rd.SetPoW(pow) != nil {
return nil
}
- rd.blob = blob
return rd
}
-// GetNonce returns the last checked nonce value
-func (r *RevData) GetNonce() uint64 {
- if r.blob != nil {
- var val uint64
- binary.Read(bytes.NewReader(r.blob[:8]), binary.BigEndian, &val)
- r.Nonce = val
+func (p *PoWData) SetPoW(pow uint64) error {
+ p.PoW = pow
+ blob, err := data.Marshal(p)
+ if err != nil {
+ return err
}
- return r.Nonce
+ p.blob = blob
+ return nil
}
-// GetBlob returns the binary representation of RevData
-func (r *RevData) GetBlob() []byte {
- return r.blob
+// GetPoW returns the last checked PoW value
+func (p *PoWData) GetPoW() uint64 {
+ if p.blob != nil {
+ var val uint64
+ binary.Read(bytes.NewReader(p.blob[:8]), binary.BigEndian, &val)
+ p.PoW = val
+ }
+ return p.PoW
}
-// Next selects the next nonce to be tested.
-func (r *RevData) Next() {
+// Next selects the next PoW to be tested.
+func (p *PoWData) Next() {
var incr func(pos int)
incr = func(pos int) {
- r.blob[pos]++
- if r.blob[pos] != 0 || pos == 0 {
+ p.blob[pos]++
+ if p.blob[pos] != 0 || pos == 0 {
return
}
incr(pos - 1)
@@ -92,140 +95,191 @@ func (r *RevData) Next() {
incr(7)
}
-// Compute calculates the current result for a RevData content.
+// Compute calculates the current result for a PoWData content.
// The result is returned as a big integer value.
-func (r *RevData) Compute() (*math.Int, error) {
-
- // generate key material
- k, err := scrypt.Key(r.blob, []byte("gnunet-revocation-proof-of-work"),
2, 8, 2, 32)
- if err != nil {
- return nil, err
- }
-
- // generate initialization vector
- iv := make([]byte, 16)
- prk := hkdf.Extract(sha512.New, k, []byte("gnunet-proof-of-work-iv"))
- rdr := hkdf.Expand(sha256.New, prk,
[]byte("gnunet-revocation-proof-of-work"))
- rdr.Read(iv)
-
- // Encrypt with Twofish CFB stream cipher
- out := make([]byte, len(r.blob))
- tf, err := twofish.NewCipher(k)
- if err != nil {
- return nil, err
- }
- cipher.NewCFBEncrypter(tf, iv).XORKeyStream(out, r.blob)
-
- // compute result
- result, err := scrypt.Key(out,
[]byte("gnunet-revocation-proof-of-work"), 2, 8, 2, 64)
- return math.NewIntFromBytes(result), nil
+func (p *PoWData) Compute() *math.Int {
+ key := argon2.Key(p.blob, []byte("gnunet-revocation-proof-of-work"), 3,
1024, 1, 64)
+ return math.NewIntFromBytes(key)
}
//----------------------------------------------------------------------
-// Command types for Worker
+// Revocation data
//----------------------------------------------------------------------
-// StartCmd starts the PoW calculation beginng at given nonce. If a
-// revocation is initiated the first time, the nonce is 0. If the computation
-// was interrupted (because the revocation service was shutting down), the
-// computation can resume for the next unchecked nonce value.
-// see: StartResponse
-type StartCmd struct {
- ID int // Command identifier (to relate responses)
- task *RevData // RevData instance to be started
-}
-
-// PauseCmd temporarily pauses the calculation of a PoW.
-// see: PauseResponse
-type PauseCmd struct {
- ID int // Command identifier (to relate responses)
- taskID int // identifier for PoW task
-}
-
-// ResumeCmd resumes a paused PoW calculation.
-// see: ResumeResponse
-type ResumeCmd struct {
- ID int // Command identifier (to relate responses)
- taskID int // identifier for PoW task
-}
-
-// BreakCmd interrupts a running PoW calculation
-type BreakCmd struct {
- ID int // Command identifier (to relate responses)
- taskID int // identifier for PoW task
+// RevData is the revocation data (wire format)
+type RevData struct {
+ Timestamp util.AbsoluteTime // Timestamp of creation
+ PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of
PoW values
+ Signature []byte `size:"64"` // Signature
(Proof-of-ownership).
+ ZoneKey []byte `size:"32"` // public zone key
to be revoked
}
-//----------------------------------------------------------------------
-// Response types for Worker
-//----------------------------------------------------------------------
-
-// StartResponse is a reply to the StartCmd message
-type StartResponse struct {
- ID int // Command identifier (to relate responses)
- taskID int // identifier for PoW task
- err error // error code (nil on success)
+// SignedRevData is the block of data signed for a RevData instance.
+type SignedRevData struct {
+ Purpose *crypto.SignaturePurpose
+ ZoneKey []byte `size:"32"` // public zone key to be revoked
+ Timestamp util.AbsoluteTime // Timestamp of creation
}
-// PauseResponse is a reply to the PauseCmd message
-type PauseResponse struct {
- ID int // Command identifier (to relate responses)
- err error // error code (nil on success)
+// NewRevData initializes a new RevData instance
+func NewRevData(ts util.AbsoluteTime, pkey *ed25519.PublicKey) *RevData {
+ rd := &RevData{
+ Timestamp: ts,
+ PoWs: make([]uint64, 32),
+ Signature: make([]byte, 64),
+ ZoneKey: make([]byte, 32),
+ }
+ copy(rd.ZoneKey, pkey.Bytes())
+ return rd
}
-// ResumeResponse is a reply to the ResumeCmd message
-type ResumeResponse struct {
- ID int // Command identifier (to relate responses)
- err error // error code (nil on success)
+// NewRevDataFromMsg initializes a new RevData instance from a GNUnet message
+func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData {
+ rd := &RevData{
+ Timestamp: m.Timestamp,
+ Signature: util.Clone(m.Signature),
+ ZoneKey: util.Clone(m.ZoneKey),
+ }
+ for i, pow := range m.PoWs {
+ rd.PoWs[i] = pow
+ }
+ return rd
}
-// BreakResponse is a reply to the BreakCmd message
-type BreakResponse struct {
- ID int // Command identifier (to relate responses)
- Nonce uint64 // last checked nonce value
+// Sign the revocation data
+func (rd *RevData) Sign(skey *ed25519.PrivateKey) error {
+ sigBlock := &SignedRevData{
+ Purpose: &crypto.SignaturePurpose{
+ Size: 48,
+ Purpose: enums.SIG_REVOCATION,
+ },
+ ZoneKey: rd.ZoneKey,
+ Timestamp: rd.Timestamp,
+ }
+ sigData, err := data.Marshal(sigBlock)
+ if err != nil {
+ return err
+ }
+ sig, err := skey.EcSign(sigData)
+ if err != nil {
+ return err
+ }
+ copy(rd.Signature, sig.Bytes())
+ return nil
}
-//----------------------------------------------------------------------
-// Worker instance
-//----------------------------------------------------------------------
-
-// Task represents a currently active PoW calculation
-type Task struct {
- ID int
- rev *RevData
- active bool
-}
+// Verify a revocation object: returns the (smallest) number of leading
+// zero-bits in the PoWs of this revocation; a number > 0, but smaller
+// than the minimum (25) indicates invalid PoWs; a value of -1 indicates
+// a failed signature; -2 indicates an expired revocation and -3 for a
+// "out-of-order" PoW sequence.
+func (rd *RevData) Verify() int {
+
+ // (1) check signature
+ sigBlock := &SignedRevData{
+ Purpose: &crypto.SignaturePurpose{
+ Size: 48,
+ Purpose: enums.SIG_REVOCATION,
+ },
+ ZoneKey: rd.ZoneKey,
+ Timestamp: rd.Timestamp,
+ }
+ sigData, err := data.Marshal(sigBlock)
+ if err != nil {
+ return -1
+ }
+ pkey := ed25519.NewPublicKeyFromBytes(rd.ZoneKey)
+ sig, err := ed25519.NewEcSignatureFromBytes(rd.Signature)
+ if err != nil {
+ return -1
+ }
+ valid, err := pkey.EcVerify(sigData, sig)
+ if err != nil || !valid {
+ return -1
+ }
-// Worker is the revocation worker. It is responsible to manage ad schedule
-// the proof-of-work tasks for revocations.
-type Worker struct {
- tasks map[int]*Task
- wg *sync.WaitGroup
-}
+ // (2) check PoWs
+ var (
+ zbits int = 512
+ last uint64 = 0
+ )
+ for _, pow := range rd.PoWs {
+ // check sequence order
+ if pow <= last {
+ return -3
+ }
+ last = pow
+ // compute number of leading zero-bits
+ work := NewPoWData(pow, rd.Timestamp, rd.ZoneKey)
+ lzb := 512 - work.Compute().BitLen()
+ if lzb < zbits {
+ zbits = lzb
+ }
+ }
-func NewWorker() *Worker {
- return &Worker{
- tasks: make(map[int]*Task),
- wg: new(sync.WaitGroup),
+ // (3) check expiration
+ ttl := time.Duration((zbits-24)*365*24) * time.Hour
+ if util.AbsoluteTimeNow().Add(ttl).Expired() {
+ return -2
}
+ return zbits
}
-func (w *Worker) Run(wg *sync.WaitGroup, cmdCh chan interface{}, responseCh
chan interface{}) {
- defer wg.Done()
- for {
- select {
- case cmd := <-cmdCh:
- switch x := cmd.(type) {
- case *StartCmd:
- task := &Task{
- ID: util.NextID(),
- rev: x.task,
- active: true,
+// Compute tries to compute a valid Revocation; it returns the number of
+// solved PoWs. The computation is complete if 32 PoWs have been found.
+func (rd *RevData) Compute(ctx context.Context, bits int, last uint64) (int,
uint64) {
+ // set difficulty based on requested number of leading zero-bits
+ difficulty := math.TWO.Pow(512 - bits).Sub(math.ONE)
+
+ // initialize a new work record (single PoW computation)
+ work := NewPoWData(0, rd.Timestamp, rd.ZoneKey)
+
+ // work on all PoWs in a revocation data structure; make sure all PoWs
+ // are set to a valid value (that results in a valid compute() result
+ // below a given threshold)
+ for i, pow := range rd.PoWs {
+ // handle "new" pow value: set it to last_pow+1
+ // this ensures a correctly sorted pow list by design.
+ if pow == 0 {
+ pow = last
+ }
+ if pow == 0 && i > 0 {
+ pow = rd.PoWs[i-1] + 1
+ }
+ // prepare for PoW_i
+ work.SetPoW(pow)
+
+ // Find PoW value in an (interruptable) loop
+ out := make(chan bool)
+ go func() {
+ for {
+ res := work.Compute()
+ if res.Cmp(difficulty) < 0 {
+ break
}
- w.tasks[task.ID] = task
+ work.Next()
+ }
+ out <- true
+ }()
+ loop:
+ for {
+ select {
+ case <-out:
+ rd.PoWs[i] = work.GetPoW()
+ break loop
+ case <-ctx.Done():
+ return i, work.GetPoW() + 1
}
-
- default:
- // compute a single round of currently active tasks
}
}
+ // we have found all valid PoW values.
+ return 32, 0
+}
+
+func (rd *RevData) Blob() []byte {
+ blob, err := data.Marshal(rd)
+ if err != nil {
+ return nil
+ }
+ return blob
}
diff --git a/src/gnunet/service/revocation/service.go
b/src/gnunet/service/revocation/service.go
new file mode 100644
index 0000000..f2a6ec3
--- /dev/null
+++ b/src/gnunet/service/revocation/service.go
@@ -0,0 +1,160 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package revocation
+
+import (
+ "io"
+
+ "gnunet/message"
+ "gnunet/service"
+ "gnunet/transport"
+
+ "github.com/bfix/gospel/crypto/ed25519"
+ "github.com/bfix/gospel/logger"
+)
+
+//----------------------------------------------------------------------
+// "GNUnet Revocation" service implementation
+//----------------------------------------------------------------------
+
+// RevocationService
+type RevocationService struct {
+ RevocationModule
+}
+
+// NewRevocationService
+func NewRevocationService() service.Service {
+ // instantiate service and assemble a new Revocation handler.
+ inst := new(RevocationService)
+ return inst
+}
+
+// Start the Revocation service
+func (s *RevocationService) Start(spec string) error {
+ return nil
+}
+
+// Stop the Revocation service
+func (s *RevocationService) Stop() error {
+ return nil
+}
+
+// Serve a client channel.
+func (s *RevocationService) ServeClient(ctx *service.SessionContext, mc
*transport.MsgChannel) {
+
+ reqId := 0
+loop:
+ for {
+ // receive next message from client
+ reqId++
+ logger.Printf(logger.DBG, "[revocation:%d:%d] Waiting for
client request...\n", ctx.Id, reqId)
+ msg, err := mc.Receive(ctx.Signaller())
+ if err != nil {
+ if err == io.EOF {
+ logger.Printf(logger.INFO, "[revocation:%d:%d]
Client channel closed.\n", ctx.Id, reqId)
+ } else if err == transport.ErrChannelInterrupted {
+ logger.Printf(logger.INFO, "[revocation:%d:%d]
Service operation interrupted.\n", ctx.Id, reqId)
+ } else {
+ logger.Printf(logger.ERROR, "[revocation:%d:%d]
Message-receive failed: %s\n", ctx.Id, reqId, err.Error())
+ }
+ break loop
+ }
+ logger.Printf(logger.INFO, "[revocation:%d:%d] Received
request: %v\n", ctx.Id, reqId, msg)
+
+ // handle request
+ switch m := msg.(type) {
+ case *message.RevocationQueryMsg:
+
//----------------------------------------------------------
+ // REVOCATION_QUERY
+
//----------------------------------------------------------
+ go func(id int, m *message.RevocationQueryMsg) {
+ logger.Printf(logger.INFO, "[revocation:%d:%d]
Query request received.\n", ctx.Id, id)
+ var resp *message.RevocationQueryResponseMsg
+ ctx.Add()
+ defer func() {
+ // send response
+ if resp != nil {
+ if err := mc.Send(resp,
ctx.Signaller()); err != nil {
+
logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to send response: %s\n",
ctx.Id, id, err.Error())
+ }
+ }
+ // go-routine finished
+ logger.Printf(logger.DBG,
"[revocation:%d:%d] Query request finished.\n", ctx.Id, id)
+ ctx.Remove()
+ }()
+
+ pkey := ed25519.NewPublicKeyFromBytes(m.Zone)
+ valid, err := s.Query(ctx, pkey)
+ if err != nil {
+ logger.Printf(logger.ERROR,
"[revocation:%d:%d] Failed to query revocation status: %s\n", ctx.Id, id,
err.Error())
+ if err ==
transport.ErrChannelInterrupted {
+ resp = nil
+ }
+ return
+ }
+ resp =
message.NewRevocationQueryResponseMsg(valid)
+ }(reqId, m)
+
+ case *message.RevocationRevokeMsg:
+
//----------------------------------------------------------
+ // REVOCATION_REVOKE
+
//----------------------------------------------------------
+ go func(id int, m *message.RevocationRevokeMsg) {
+ logger.Printf(logger.INFO, "[revocation:%d:%d]
Revoke request received.\n", ctx.Id, id)
+ var resp *message.RevocationRevokeResponseMsg
+ ctx.Add()
+ defer func() {
+ // send response
+ if resp != nil {
+ if err := mc.Send(resp,
ctx.Signaller()); err != nil {
+
logger.Printf(logger.ERROR, "[revocation:%d:%d] Failed to send response: %s\n",
ctx.Id, id, err.Error())
+ }
+ }
+ // go-routine finished
+ logger.Printf(logger.DBG,
"[revocation:%d:%d] Revoke request finished.\n", ctx.Id, id)
+ ctx.Remove()
+ }()
+
+ rd := NewRevDataFromMsg(m)
+ valid, err := s.Revoke(ctx, rd)
+ if err != nil {
+ logger.Printf(logger.ERROR,
"[revocation:%d:%d] Failed to revoke key: %s\n", ctx.Id, id, err.Error())
+ if err ==
transport.ErrChannelInterrupted {
+ resp = nil
+ }
+ return
+ }
+ resp =
message.NewRevocationRevokeResponseMsg(valid)
+ }(reqId, m)
+
+ default:
+
//----------------------------------------------------------
+ // UNKNOWN message type received
+
//----------------------------------------------------------
+ logger.Printf(logger.ERROR, "[revocation:%d:%d]
Unhandled message of type (%d)\n", ctx.Id, reqId, msg.Header().MsgType)
+ break loop
+ }
+ }
+ // close client connection
+ mc.Close()
+
+ // cancel all tasks running for this session/connection
+ logger.Printf(logger.INFO, "[revocation:%d] Start closing session...
[%d]\n", ctx.Id, ctx.Waiting())
+ ctx.Cancel()
+}
diff --git a/src/gnunet/util/database.go b/src/gnunet/util/database.go
new file mode 100644
index 0000000..48da749
--- /dev/null
+++ b/src/gnunet/util/database.go
@@ -0,0 +1,72 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package util
+
+import (
+ "database/sql"
+ "fmt"
+ "os"
+ "strings"
+
+ _ "github.com/go-sql-driver/mysql"
+ _ "github.com/mattn/go-sqlite3"
+)
+
+// Error messages related to databases
+var (
+ ErrSqlInvalidDatabaseSpec = fmt.Errorf("Invalid database specification")
+ ErrSqlNoDatabase = fmt.Errorf("Database not found")
+)
+
+// ConnectSqlDatabase connects to an SQL database (various types and flavors):
+// The 'spec' option defines the arguments required to connect to a database;
+// the meaning and format of the arguments depends on the specific SQL
database.
+// The arguments are seperated by the '+' character; the first (and mandatory)
+// argument defines the SQL database type. Other arguments depend on the value
+// of this first argument.
+// The following SQL types are implemented:
+// * 'sqlite3': SQLite3-compatible database; the second argument specifies the
+// file that holds the data (e.g. "sqlite3+/home/user/store.db")
+// * 'mysql': A MySQL-compatible database; the second argument specifies the
+// information required to log into the database (e.g.
+// "[user[:passwd]@][proto[(addr)]]/dbname[?param1=value1&...]").
+func ConnectSqlDatabase(spec string) (db *sql.DB, err error) {
+ // split spec string into segments
+ specs := strings.Split(spec, ":")
+ if len(specs) < 2 {
+ return nil, ErrSqlInvalidDatabaseSpec
+ }
+ switch specs[0] {
+ case "sqlite3":
+ // check if the database file exists
+ var fi os.FileInfo
+ if fi, err = os.Stat(specs[1]); err != nil {
+ return nil, ErrSqlNoDatabase
+ }
+ if fi.IsDir() {
+ return nil, ErrSqlNoDatabase
+ }
+ // open the database file
+ return sql.Open("sqlite3", specs[1])
+ case "mysql":
+ // just connect to the database
+ return sql.Open("mysql", specs[1])
+ }
+ return nil, ErrSqlInvalidDatabaseSpec
+}
diff --git a/src/gnunet/util/key_value_store.go
b/src/gnunet/util/key_value_store.go
new file mode 100644
index 0000000..d87b747
--- /dev/null
+++ b/src/gnunet/util/key_value_store.go
@@ -0,0 +1,188 @@
+// This file is part of gnunet-go, a GNUnet-implementation in Golang.
+// Copyright (C) 2019, 2020 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
+
+package util
+
+import (
+ "context"
+ "database/sql"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/go-redis/redis"
+)
+
+// Error messages related to the key/value-store implementations
+var (
+ ErrKVSInvalidSpec = fmt.Errorf("Invalid KVStore specification")
+ ErrKVSNotAvailable = fmt.Errorf("KVStore not available")
+)
+
+// KeyValueStore interface for implementations that store and retrieve
+// key/value pairs. Keys and values are strings.
+type KeyValueStore interface {
+ Put(key string, value string) error // put a key/value pair into store
+ Get(key string) (string, error) // retrieve a value for a key from
store
+ List() ([]string, error) // get all keys from the store
+}
+
+// OpenKVStore opens a key/value store for further put/get operations.
+// The 'spec' option specifies the arguments required to connect to a specific
+// persistence mechanism. The arguments in the 'spec' string are separated by
+// the '+' character.
+// The first argument specifies the type of key/value store to be used; the
+// meaning and format of the following arguments depend on this type.
+//
+// Key/Value Store types defined:
+// * 'redis': Use a Redis server for persistance; the specification is
+// "redis+addr+[passwd]+db". 'db' must be an integer value.
+// * 'mysql': MySQL-compatible database (see 'database.go' for details)
+// * 'sqlite3': SQLite3-compatible database (see 'database.go' for details)
+func OpenKVStore(spec string) (KeyValueStore, error) {
+ // check specification string
+ specs := strings.Split(spec, "+")
+ if len(specs) < 2 {
+ return nil, ErrKVSInvalidSpec
+ }
+ switch specs[0] {
+ case "redis":
+ //--------------------------------------------------------------
+ // NoSQL-based persistance
+ //--------------------------------------------------------------
+ if len(specs) < 4 {
+ return nil, ErrKVSInvalidSpec
+ }
+ db, err := strconv.Atoi(specs[3])
+ if err != nil {
+ return nil, ErrKVSInvalidSpec
+ }
+ kvs := new(KvsRedis)
+ kvs.db = db
+ kvs.client = redis.NewClient(&redis.Options{
+ Addr: specs[1],
+ Password: specs[2],
+ DB: db,
+ })
+ if kvs.client == nil {
+ err = ErrKVSNotAvailable
+ }
+ return kvs, err
+
+ case "sqlite3", "mysql":
+ //--------------------------------------------------------------
+ // SQL-based persistance
+ //--------------------------------------------------------------
+ kvs := new(KvsSql)
+ var err error
+
+ // connect to SQL database
+ kvs.db, err = ConnectSqlDatabase(spec)
+ if err != nil {
+ return nil, err
+ }
+ // get number of key/value pairs (as a check for existing table)
+ row := kvs.db.QueryRow("select count(*) from store")
+ var num int
+ if row.Scan(&num) != nil {
+ return nil, ErrKVSNotAvailable
+ }
+ return kvs, nil
+ }
+ return nil, ErrKVSInvalidSpec
+}
+
+//======================================================================
+// NoSQL-based key-value-stores
+//======================================================================
+
+// Redis-based key/value store
+type KvsRedis struct {
+ client *redis.Client // client connection
+ db int // index to database
+}
+
+// Put a key/value pair into the store
+func (kvs *KvsRedis) Put(key string, value string) error {
+ return kvs.client.Set(context.TODO(), key, value, 0).Err()
+}
+
+// Get a value for a given key from store
+func (kvs *KvsRedis) Get(key string) (value string, err error) {
+ return kvs.client.Get(context.TODO(), key).Result()
+}
+
+// Get a list of all keys in store
+func (kvs *KvsRedis) List() (keys []string, err error) {
+ var (
+ crs uint64
+ segm []string
+ ctx = context.TODO()
+ )
+ for {
+ segm, crs, err = kvs.client.Scan(ctx, crs, "*", 10).Result()
+ if err != nil {
+ return nil, err
+ }
+ if crs == 0 {
+ break
+ }
+ keys = append(keys, segm...)
+ }
+ return
+}
+
+//======================================================================
+// SQL-based key-value-store
+//======================================================================
+
+// SQL-based key/value store
+type KvsSql struct {
+ db *sql.DB
+}
+
+// Put a key/value pair into the store
+func (kvs *KvsSql) Put(key string, value string) error {
+ _, err := kvs.db.Exec("insert into store(key,value) values(?,?)", key,
value)
+ return err
+}
+
+// Get a value for a given key from store
+func (kvs *KvsSql) Get(key string) (value string, err error) {
+ row := kvs.db.QueryRow("select value from store where key=?", key)
+ err = row.Scan(&value)
+ return
+}
+
+// Get a list of all keys in store
+func (kvs *KvsSql) List() (keys []string, err error) {
+ var (
+ rows *sql.Rows
+ key string
+ )
+ rows, err = kvs.db.Query("select key from store")
+ if err == nil {
+ for rows.Next() {
+ if err = rows.Scan(&key); err != nil {
+ break
+ }
+ keys = append(keys, key)
+ }
+ }
+ return
+}
diff --git a/src/gnunet/util/time.go b/src/gnunet/util/time.go
index e1e0e30..bf2a1c2 100644
--- a/src/gnunet/util/time.go
+++ b/src/gnunet/util/time.go
@@ -69,6 +69,18 @@ func (t AbsoluteTime) Add(d time.Duration) AbsoluteTime {
}
}
+// Diff returns the relative time between two absolute times;
+// the ordering of the absolute times doesn't matter.
+func (t AbsoluteTime) Diff(t2 AbsoluteTime) RelativeTime {
+ var d uint64
+ if t.Compare(t2) == 1 {
+ d = t.Val - t2.Val
+ } else {
+ d = t2.Val - t.Val
+ }
+ return RelativeTime{d}
+}
+
// Expired returns true if the timestamp is in the past.
func (t AbsoluteTime) Expired() bool {
// check for "never"
--
To stop receiving notification emails like this one, please contact
address@hidden.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [gnunet-go] branch master updated: Milestone #3 (RC1),
gnunet <=