aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Fix <brf@hoi-polloi.org>2020-02-17 12:34:45 +0100
committerBernd Fix <brf@hoi-polloi.org>2020-02-17 12:34:45 +0100
commitdb8edd2189c526a79039661844314081739010e1 (patch)
tree011d51a33c739b4319cf1dddb62c6f838ccfdf82
parent7bfc54a066e5a2c65f43ef17783fc9b3abbfbef0 (diff)
downloadgnunet-go-db8edd2189c526a79039661844314081739010e1.tar.gz
gnunet-go-db8edd2189c526a79039661844314081739010e1.zip
Started revocation implementation.
-rw-r--r--src/cmd/pow-test/main.go48
-rw-r--r--src/gnunet/message/factory.go12
-rw-r--r--src/gnunet/message/msg_revocation.go181
-rw-r--r--src/gnunet/service/gns/module.go10
-rw-r--r--src/gnunet/service/revocation/pow.go231
5 files changed, 482 insertions, 0 deletions
diff --git a/src/cmd/pow-test/main.go b/src/cmd/pow-test/main.go
new file mode 100644
index 0000000..4b74b6e
--- /dev/null
+++ b/src/cmd/pow-test/main.go
@@ -0,0 +1,48 @@
1package main
2
3import (
4 "flag"
5 "fmt"
6 "log"
7
8 "gnunet/service/revocation"
9
10 "github.com/bfix/gospel/crypto/ed25519"
11 "github.com/bfix/gospel/math"
12)
13
14func main() {
15 var (
16 quiet bool
17 bits int
18 )
19 flag.IntVar(&bits, "b", 25, "Number of leading zero bits")
20 flag.BoolVar(&quiet, "q", false, "Be quiet")
21 flag.Parse()
22 fmt.Printf("Leading zeros required: %d\n", bits)
23
24 // generate a random key pair
25 pkey, _ := ed25519.NewKeypair()
26
27 // initialize RevData structure
28 rd := revocation.NewRevData(0, pkey)
29
30 // pre-set difficulty
31 difficulty := math.TWO.Pow(512 - bits).Sub(math.ONE)
32
33 var count uint64 = 0
34 for {
35 result, err := rd.Compute()
36 if err != nil {
37 log.Fatal(err)
38 }
39 //fmt.Printf("Nonce=%d, Result=(%d) %v\n", rd.GetNonce(), result.BitLen(), result)
40 if result.Cmp(difficulty) < 0 {
41 break
42 }
43 count++
44 rd.Next()
45 }
46 fmt.Printf("PoW found after %d iterations:\n", count)
47 fmt.Printf("--> Nonce=%d\n", rd.GetNonce())
48}
diff --git a/src/gnunet/message/factory.go b/src/gnunet/message/factory.go
index f6a8783..e6e6e73 100644
--- a/src/gnunet/message/factory.go
+++ b/src/gnunet/message/factory.go
@@ -81,6 +81,18 @@ func NewEmptyMessage(msgType uint16) (Message, error) {
81 return NewNamecacheCacheMsg(nil), nil 81 return NewNamecacheCacheMsg(nil), nil
82 case NAMECACHE_BLOCK_CACHE_RESPONSE: 82 case NAMECACHE_BLOCK_CACHE_RESPONSE:
83 return NewNamecacheCacheResponseMsg(), nil 83 return NewNamecacheCacheResponseMsg(), nil
84
85 //------------------------------------------------------------------
86 // Revocation
87 //------------------------------------------------------------------
88 case REVOCATION_QUERY:
89 return NewRevocationQueryMsg(nil), nil
90 case REVOCATION_QUERY_RESPONSE:
91 return NewRevocationQueryResponseMsg(true), nil
92 case REVOCATION_REVOKE:
93 return NewRevocationRevokeMsg(0, nil, nil), nil
94 case REVOCATION_REVOKE_RESPONSE:
95 return NewRevocationRevokeResponseMsg(false), nil
84 } 96 }
85 return nil, errors.New(fmt.Sprintf("Unknown message type %d", msgType)) 97 return nil, errors.New(fmt.Sprintf("Unknown message type %d", msgType))
86} 98}
diff --git a/src/gnunet/message/msg_revocation.go b/src/gnunet/message/msg_revocation.go
new file mode 100644
index 0000000..fea727c
--- /dev/null
+++ b/src/gnunet/message/msg_revocation.go
@@ -0,0 +1,181 @@
1// This file is part of gnunet-go, a GNUnet-implementation in Golang.
2// Copyright (C) 2019, 2020 Bernd Fix >Y<
3//
4// gnunet-go is free software: you can redistribute it and/or modify it
5// under the terms of the GNU Affero General Public License as published
6// by the Free Software Foundation, either version 3 of the License,
7// or (at your option) any later version.
8//
9// gnunet-go is distributed in the hope that it will be useful, but
10// WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//
17// SPDX-License-Identifier: AGPL3.0-or-later
18
19package message
20
21import (
22 "fmt"
23
24 "gnunet/crypto"
25 "gnunet/enums"
26 "gnunet/util"
27
28 "github.com/bfix/gospel/crypto/ed25519"
29)
30
31//----------------------------------------------------------------------
32// REVOCATION_QUERY
33//----------------------------------------------------------------------
34
35// RevocationQueryMsg
36type RevocationQueryMsg struct {
37 MsgSize uint16 `order:"big"` // total size of message
38 MsgType uint16 `order:"big"` // REVOCATION_QUERY (636)
39 Reserved uint32 `order:"big"` // Reserved for future use
40 Zone []byte `size:"32"` // Zone that is to be checked for revocation
41}
42
43// NewRevocationQueryMsg creates a new message for a given zone.
44func NewRevocationQueryMsg(zone *ed25519.PublicKey) *RevocationQueryMsg {
45 msg := &RevocationQueryMsg{
46 MsgSize: 40,
47 MsgType: REVOCATION_QUERY,
48 Reserved: 0,
49 Zone: make([]byte, 32),
50 }
51 if zone != nil {
52 copy(msg.Zone, zone.Bytes())
53 }
54 return msg
55}
56
57// String returns a human-readable representation of the message.
58func (m *RevocationQueryMsg) String() string {
59 return fmt.Sprintf("RevocationQueryMsg{zone=%s}", util.EncodeBinaryToString(m.Zone))
60}
61
62// Header returns the message header in a separate instance.
63func (msg *RevocationQueryMsg) Header() *MessageHeader {
64 return &MessageHeader{msg.MsgSize, msg.MsgType}
65}
66
67//----------------------------------------------------------------------
68// REVOCATION_QUERY_RESPONSE
69//----------------------------------------------------------------------
70
71// RevocationQueryResponseMsg
72type RevocationQueryResponseMsg struct {
73 MsgSize uint16 `order:"big"` // total size of message
74 MsgType uint16 `order:"big"` // REVOCATION_QUERY_RESPONSE (637)
75 Valid uint32 `order:"big"` // revoked(0), valid(1)
76}
77
78// NewRevocationQueryResponseMsg creates a new response for a query.
79func NewRevocationQueryResponseMsg(revoked bool) *RevocationQueryResponseMsg {
80 valid := 1
81 if revoked {
82 valid = 0
83 }
84 return &RevocationQueryResponseMsg{
85 MsgSize: 8,
86 MsgType: REVOCATION_QUERY_RESPONSE,
87 Valid: uint32(valid),
88 }
89}
90
91// String returns a human-readable representation of the message.
92func (m *RevocationQueryResponseMsg) String() string {
93 return fmt.Sprintf("RevocationQueryResponseMsg{valid=%d}", m.Valid)
94}
95
96// Header returns the message header in a separate instance.
97func (msg *RevocationQueryResponseMsg) Header() *MessageHeader {
98 return &MessageHeader{msg.MsgSize, msg.MsgType}
99}
100
101//----------------------------------------------------------------------
102// REVOCATION_REVOKE
103//----------------------------------------------------------------------
104
105// RevocationRevokeMsg
106type RevocationRevokeMsg struct {
107 MsgSize uint16 `order:"big"` // total size of message
108 MsgType uint16 `order:"big"` // REVOCATION_QUERY (636)
109 Reserved uint32 `order:"big"` // Reserved for future use
110 PoW uint64 `order:"big"` // Proof-of-work: nonce that satisfy condition
111 Signature []byte `size:"64"` // Signature of the revocation.
112 Purpose *crypto.SignaturePurpose // Size and purpose of signature (8 bytes)
113 ZoneKey []byte `size:"32"` // Zone key to be revoked
114}
115
116// NewRevocationRevokeMsg creates a new message for a given zone.
117func NewRevocationRevokeMsg(pow uint64, zoneKey *ed25519.PublicKey, sig *ed25519.EcSignature) *RevocationRevokeMsg {
118 msg := &RevocationRevokeMsg{
119 MsgSize: 120,
120 MsgType: REVOCATION_REVOKE,
121 Reserved: 0,
122 PoW: pow,
123 Signature: make([]byte, 64),
124 Purpose: &crypto.SignaturePurpose{
125 Size: 40,
126 Purpose: enums.SIG_REVOCATION,
127 },
128 ZoneKey: make([]byte, 32),
129 }
130 if zoneKey != nil {
131 copy(msg.ZoneKey, zoneKey.Bytes())
132 }
133 if sig != nil {
134 copy(msg.Signature, sig.Bytes())
135 }
136 return msg
137}
138
139// String returns a human-readable representation of the message.
140func (m *RevocationRevokeMsg) String() string {
141 return fmt.Sprintf("RevocationRevokeMsg{pow=%d,zone=%s}", m.PoW, util.EncodeBinaryToString(m.ZoneKey))
142}
143
144// Header returns the message header in a separate instance.
145func (msg *RevocationRevokeMsg) Header() *MessageHeader {
146 return &MessageHeader{msg.MsgSize, msg.MsgType}
147}
148
149//----------------------------------------------------------------------
150// REVOCATION_REVOKE_RESPONSE
151//----------------------------------------------------------------------
152
153// RevocationRevokeResponseMsg
154type RevocationRevokeResponseMsg struct {
155 MsgSize uint16 `order:"big"` // total size of message
156 MsgType uint16 `order:"big"` // REVOCATION_QUERY_RESPONSE (637)
157 Success uint32 `order:"big"` // Revoke successful?
158}
159
160// NewRevocationRevokeResponseMsg creates a new response for a query.
161func NewRevocationRevokeResponseMsg(success bool) *RevocationRevokeResponseMsg {
162 status := 0
163 if success {
164 status = 1
165 }
166 return &RevocationRevokeResponseMsg{
167 MsgSize: 8,
168 MsgType: REVOCATION_QUERY_RESPONSE,
169 Success: uint32(status),
170 }
171}
172
173// String returns a human-readable representation of the message.
174func (m *RevocationRevokeResponseMsg) String() string {
175 return fmt.Sprintf("RevocationRevokeResponseMsg{success=%v}", m.Success == 1)
176}
177
178// Header returns the message header in a separate instance.
179func (msg *RevocationRevokeResponseMsg) Header() *MessageHeader {
180 return &MessageHeader{msg.MsgSize, msg.MsgType}
181}
diff --git a/src/gnunet/service/gns/module.go b/src/gnunet/service/gns/module.go
index 5626432..e8d8847 100644
--- a/src/gnunet/service/gns/module.go
+++ b/src/gnunet/service/gns/module.go
@@ -280,6 +280,16 @@ func (gns *GNSModule) ResolveRelative(labels []string, pkey *ed25519.PublicKey,
280 set.AddRecord(inst.rec) 280 set.AddRecord(inst.rec)
281 } 281 }
282 } 282 }
283
284 // if the result set is not empty, add all supplemental records we are not
285 // asking for explicitly.
286 if set.Count > 0 {
287 for _, rec := range records {
288 if !kind.HasType(int(rec.Type)) && (int(rec.Flags)&enums.GNS_FLAG_SUPPL) != 0 {
289 set.AddRecord(rec)
290 }
291 }
292 }
283 return 293 return
284} 294}
285 295
diff --git a/src/gnunet/service/revocation/pow.go b/src/gnunet/service/revocation/pow.go
new file mode 100644
index 0000000..368b4e4
--- /dev/null
+++ b/src/gnunet/service/revocation/pow.go
@@ -0,0 +1,231 @@
1// This file is part of gnunet-go, a GNUnet-implementation in Golang.
2// Copyright (C) 2019, 2020 Bernd Fix >Y<
3//
4// gnunet-go is free software: you can redistribute it and/or modify it
5// under the terms of the GNU Affero General Public License as published
6// by the Free Software Foundation, either version 3 of the License,
7// or (at your option) any later version.
8//
9// gnunet-go is distributed in the hope that it will be useful, but
10// WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//
17// SPDX-License-Identifier: AGPL3.0-or-later
18
19package revocation
20
21import (
22 "bytes"
23 "crypto/sha256"
24 "crypto/sha512"
25 "encoding/binary"
26 "sync"
27
28 "gnunet/crypto"
29 "gnunet/util"
30
31 "github.com/bfix/gospel/crypto/ed25519"
32 "github.com/bfix/gospel/data"
33 "github.com/bfix/gospel/math"
34 "golang.org/x/crypto/hkdf"
35 "golang.org/x/crypto/scrypt"
36)
37
38//----------------------------------------------------------------------
39// Revocation data
40//----------------------------------------------------------------------
41
42// RevData is the revocation data structure (wire format)
43type RevData struct {
44 Nonce uint64 `order:"big"` // start with this nonce value
45 ZoneKey []byte `size:"32"` // public zone key to be revoked
46
47 // transient attributes (not serialized)
48 blob []byte // binary representation of serialized data
49}
50
51// NewRevData creates a RevData instance for the given arguments.
52func NewRevData(nonce uint64, zoneKey *ed25519.PublicKey) *RevData {
53 rd := &RevData{
54 Nonce: nonce,
55 ZoneKey: make([]byte, 32),
56 }
57 copy(rd.ZoneKey, zoneKey.Bytes())
58 blob, err := data.Marshal(rd)
59 if err != nil {
60 return nil
61 }
62 rd.blob = blob
63 return rd
64}
65
66// GetNonce returns the last checked nonce value
67func (r *RevData) GetNonce() uint64 {
68 if r.blob != nil {
69 var val uint64
70 binary.Read(bytes.NewReader(r.blob[:8]), binary.BigEndian, &val)
71 r.Nonce = val
72 }
73 return r.Nonce
74}
75
76// Next selects the next nonce to be tested.
77func (r *RevData) Next() {
78 var incr func(pos int)
79 incr = func(pos int) {
80 r.blob[pos]++
81 if r.blob[pos] != 0 || pos == 0 {
82 return
83 }
84 incr(pos - 1)
85 }
86 incr(7)
87}
88
89// Compute calculates the current result for a RevData content.
90// The result is returned as a big integer value.
91func (r *RevData) Compute() (*math.Int, error) {
92
93 // generate key material
94 k, err := scrypt.Key(r.blob, []byte("gnunet-revocation-proof-of-work"), 2, 8, 2, 64)
95 if err != nil {
96 return nil, err
97 }
98
99 // generate keys
100 skey := crypto.NewSymmetricKey()
101 copy(skey.AESKey, k[:32])
102 copy(skey.TwofishKey, k[32:])
103
104 // generate initialization vector
105 iv := crypto.NewSymmetricIV()
106 prk := hkdf.Extract(sha512.New, k[:32], []byte("gnunet-proof-of-work-ivAES!"))
107 rdr := hkdf.Expand(sha256.New, prk, []byte("gnunet-revocation-proof-of-work"))
108 rdr.Read(iv.AESIv)
109 prk = hkdf.Extract(sha512.New, k[32:], []byte("gnunet-proof-of-work-ivFISH"))
110 rdr = hkdf.Expand(sha256.New, prk, []byte("gnunet-revocation-proof-of-work"))
111 rdr.Read(iv.TwofishIv)
112
113 // perform encryption
114 enc, err := crypto.SymmetricEncrypt(r.blob, skey, iv)
115 if err != nil {
116 return nil, err
117 }
118
119 // compute result
120 result, err := scrypt.Key(enc, []byte("gnunet-revocation-proof-of-work"), 2, 8, 2, 64)
121 return math.NewIntFromBytes(result), nil
122}
123
124//----------------------------------------------------------------------
125// Command types for Worker
126//----------------------------------------------------------------------
127
128// StartCmd starts the PoW calculation beginng at given nonce. If a
129// revocation is initiated the first time, the nonce is 0. If the computation
130// was interrupted (because the revocation service was shutting down), the
131// computation can resume for the next unchecked nonce value.
132// see: StartResponse
133type StartCmd struct {
134 ID int // Command identifier (to relate responses)
135 task *RevData // RevData instance to be started
136}
137
138// PauseCmd temporarily pauses the calculation of a PoW.
139// see: PauseResponse
140type PauseCmd struct {
141 ID int // Command identifier (to relate responses)
142 taskID int // identifier for PoW task
143}
144
145// ResumeCmd resumes a paused PoW calculation.
146// see: ResumeResponse
147type ResumeCmd struct {
148 ID int // Command identifier (to relate responses)
149 taskID int // identifier for PoW task
150}
151
152// BreakCmd interrupts a running PoW calculation
153type BreakCmd struct {
154 ID int // Command identifier (to relate responses)
155 taskID int // identifier for PoW task
156}
157
158//----------------------------------------------------------------------
159// Response types for Worker
160//----------------------------------------------------------------------
161
162// StartResponse is a reply to the StartCmd message
163type StartResponse struct {
164 ID int // Command identifier (to relate responses)
165 taskID int // identifier for PoW task
166 err error // error code (nil on success)
167}
168
169// PauseResponse is a reply to the PauseCmd message
170type PauseResponse struct {
171 ID int // Command identifier (to relate responses)
172 err error // error code (nil on success)
173}
174
175// ResumeResponse is a reply to the ResumeCmd message
176type ResumeResponse struct {
177 ID int // Command identifier (to relate responses)
178 err error // error code (nil on success)
179}
180
181// BreakResponse is a reply to the BreakCmd message
182type BreakResponse struct {
183 ID int // Command identifier (to relate responses)
184 Nonce uint64 // last checked nonce value
185}
186
187//----------------------------------------------------------------------
188// Worker instance
189//----------------------------------------------------------------------
190
191// Task represents a currently active PoW calculation
192type Task struct {
193 ID int
194 rev *RevData
195 active bool
196}
197
198// Worker is the revocation worker. It is responsible to manage ad schedule
199// the proof-of-work tasks for revocations.
200type Worker struct {
201 tasks map[int]*Task
202 wg *sync.WaitGroup
203}
204
205func NewWorker() *Worker {
206 return &Worker{
207 tasks: make(map[int]*Task),
208 wg: new(sync.WaitGroup),
209 }
210}
211
212func (w *Worker) Run(wg *sync.WaitGroup, cmdCh chan interface{}, responseCh chan interface{}) {
213 defer wg.Done()
214 for {
215 select {
216 case cmd := <-cmdCh:
217 switch x := cmd.(type) {
218 case *StartCmd:
219 task := &Task{
220 ID: util.NextID(),
221 rev: x.task,
222 active: true,
223 }
224 w.tasks[task.ID] = task
225 }
226
227 default:
228 // compute a single round of currently active tasks
229 }
230 }
231}