aboutsummaryrefslogtreecommitdiff
path: root/src/gnunet/service/revocation/module.go
blob: 2682effca76e20f1460185ef0807e0055ddfd3f4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// This file is part of gnunet-go, a GNUnet-implementation in Golang.
// 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

package revocation

import (
	"context"
	"gnunet/config"
	"gnunet/core"
	"gnunet/crypto"
	"gnunet/enums"
	"gnunet/service"
	"gnunet/service/store"
	"gnunet/util"
	"net/http"

	"github.com/bfix/gospel/data"
	"github.com/bfix/gospel/logger"
)

//======================================================================
// "GNUnet Revocation" implementation
//======================================================================

// The minimum average difficulty acceptable for a set of revocation PoWs
const MinAvgDifficulty = 23

// Module handles the revocation-related calls to other modules.
type Module struct {
	service.ModuleImpl

	bloomf *data.BloomFilter // bloomfilter for fast revocation check
	kvs    store.KVStore     // storage for known revocations
}

// NewModule returns an initialized revocation module
func NewModule(ctx context.Context, c *core.Core) (m *Module) {
	// create and init instance
	m = &Module{
		ModuleImpl: *service.NewModuleImpl(),
	}
	init := func() (err error) {
		// Initialize access to revocation data storage
		if m.kvs, err = store.NewKVStore(config.Cfg.Revocation.Storage); err != nil {
			return
		}
		// traverse the storage and build bloomfilter for all keys
		m.bloomf = data.NewBloomFilter(1000000, 1e-8)
		var keys []string
		if keys, err = m.kvs.List(); err != nil {
			return
		}
		for _, key := range keys {
			m.bloomf.Add([]byte(key))
		}
		return
	}
	if err := init(); err != nil {
		logger.Printf(logger.ERROR, "[revocation] Failed to initialize module: %s\n", err.Error())
		return nil
	}
	// register as listener for core events
	listener := m.Run(ctx, m.event, m.Filter(), 0, nil)
	c.Register("gns", listener)
	return m
}

//----------------------------------------------------------------------

// Filter returns the event filter for the service
func (m *Module) Filter() *core.EventFilter {
	f := core.NewEventFilter()
	f.AddMsgType(enums.MSG_REVOCATION_QUERY)
	f.AddMsgType(enums.MSG_REVOCATION_QUERY_RESPONSE)
	f.AddMsgType(enums.MSG_REVOCATION_REVOKE)
	f.AddMsgType(enums.MSG_REVOCATION_REVOKE_RESPONSE)
	return f
}

// Event handler
func (m *Module) event(ctx context.Context, ev *core.Event) {

}

//----------------------------------------------------------------------

// Export functions
func (m *Module) Export(fcn map[string]any) {
	// add exported functions from module
	fcn["rev:query"] = m.Query
	fcn["rev:revoke"] = m.Revoke
}

// Import functions
func (m *Module) Import(fcm map[string]any) {
	// nothing to import now.
}

//----------------------------------------------------------------------

// Query return true if the pkey is valid (not revoked) and false
// if the pkey has been revoked ["rev:query"]
func (m *Module) Query(ctx context.Context, zkey *crypto.ZoneKey) (valid bool, err error) {
	// fast check first: is the key in the bloomfilter?
	data := zkey.Bytes()
	if !m.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 = m.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 a key with given revocation data ["rev:revoke"]
func (m *Module) Revoke(ctx context.Context, rd *RevData) (success bool, err error) {
	// verify the revocation data
	diff, rc := rd.Verify(true)
	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
	}
	if diff < float64(MinAvgDifficulty) {
		logger.Println(logger.WARN, "[revocation] Revoke: Difficulty to small")
		return false, nil
	}

	// store the revocation data
	// (1) add it to the bloomfilter
	m.bloomf.Add(rd.ZoneKeySig.KeyData)
	// (2) add it to the store
	var buf []byte
	if buf, err = data.Marshal(rd); err != nil {
		return false, err
	}
	value := util.EncodeBinaryToString(buf)
	err = m.kvs.Put(rd.ZoneKeySig.ID(), value)
	return true, err
}

//----------------------------------------------------------------------

// RPC returns the route and handler function for a JSON-RPC request
func (m *Module) RPC() (string, func(http.ResponseWriter, *http.Request)) {
	return "/revocation/", func(wrt http.ResponseWriter, req *http.Request) {
		_, _ = wrt.Write([]byte(`{"msg": "This is REVOCATION" }`))
	}
}