aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Fix <brf@hoi-polloi.org>2020-05-26 11:09:41 +0200
committerBernd Fix <brf@hoi-polloi.org>2020-05-26 11:09:41 +0200
commitdfbb3455b58cfa7b74c4442cae0c8bade7ffef7c (patch)
treecd6e2f32d4893a0f2073c2abf27fbac8856c7d94
parent58968de4f88aad3014ca1cfd5ce3d99590733642 (diff)
downloadgnunet-go-dfbb3455b58cfa7b74c4442cae0c8bade7ffef7c.tar.gz
gnunet-go-dfbb3455b58cfa7b74c4442cae0c8bade7ffef7c.zip
Milestone #3 (RC3): Using new test case from RFC draft.
-rw-r--r--src/cmd/revoke-zonekey/main.go25
-rw-r--r--src/gnunet/message/msg_revocation.go2
-rw-r--r--src/gnunet/service/revocation/pow.go215
-rw-r--r--src/gnunet/service/revocation/pow_test.go132
4 files changed, 290 insertions, 84 deletions
diff --git a/src/cmd/revoke-zonekey/main.go b/src/cmd/revoke-zonekey/main.go
index fe0baaa..7deeda6 100644
--- a/src/cmd/revoke-zonekey/main.go
+++ b/src/cmd/revoke-zonekey/main.go
@@ -31,7 +31,6 @@ import (
31 "gnunet/service/revocation" 31 "gnunet/service/revocation"
32 "gnunet/util" 32 "gnunet/util"
33 33
34 "github.com/bfix/gospel/crypto/ed25519"
35 "github.com/bfix/gospel/data" 34 "github.com/bfix/gospel/data"
36) 35)
37 36
@@ -59,12 +58,12 @@ func main() {
59 58
60 // define layout of persistant data 59 // define layout of persistant data
61 var revData struct { 60 var revData struct {
62 Rd *revocation.RevData // Revocation data 61 Rd *revocation.RevDataCalc // Revocation data
63 T util.RelativeTime // time spend in calculations 62 T util.RelativeTime // time spend in calculations
64 Last uint64 // last value used for PoW test 63 Last uint64 // last value used for PoW test
65 Numbits uint8 // number of leading zero-bits 64 Numbits uint8 // number of leading zero-bits
66 } 65 }
67 dataBuf := make([]byte, 377) 66 dataBuf := make([]byte, 450)
68 67
69 // read revocation object from file 68 // read revocation object from file
70 file, err := os.Open(filename) 69 file, err := os.Open(filename)
@@ -77,8 +76,7 @@ func main() {
77 if err != nil { 76 if err != nil {
78 log.Fatal("Invalid zonekey: " + err.Error()) 77 log.Fatal("Invalid zonekey: " + err.Error())
79 } 78 }
80 pkey := ed25519.NewPublicKeyFromBytes(keyData) 79 revData.Rd = revocation.NewRevDataCalc(keyData)
81 revData.Rd = revocation.NewRevData(util.AbsoluteTimeNow(), pkey)
82 revData.Numbits = uint8(bits) 80 revData.Numbits = uint8(bits)
83 revData.T = util.NewRelativeTime(0) 81 revData.T = util.NewRelativeTime(0)
84 cont = false 82 cont = false
@@ -121,11 +119,14 @@ func main() {
121 wg.Add(1) 119 wg.Add(1)
122 go func() { 120 go func() {
123 defer wg.Done() 121 defer wg.Done()
122 cb := func(average float64, last uint64) {
123 log.Printf("Improved PoW: %f average zero bits, %d steps\n", average, last)
124 }
124 125
125 startTime := util.AbsoluteTimeNow() 126 startTime := util.AbsoluteTimeNow()
126 result, last := revData.Rd.Compute(ctx, bits, revData.Last) 127 average, last := revData.Rd.Compute(ctx, bits, revData.Last, cb)
127 if result != 32 { 128 if average < float64(bits) {
128 log.Printf("Incomplete revocation: Only %d of 32 PoWs available!\n", result) 129 log.Printf("Incomplete revocation: Only %f zero bits on average!\n", average)
129 } else { 130 } else {
130 log.Println("Revocation data object:") 131 log.Println("Revocation data object:")
131 log.Println(" 0x" + hex.EncodeToString(revData.Rd.Blob())) 132 log.Println(" 0x" + hex.EncodeToString(revData.Rd.Blob()))
@@ -158,7 +159,7 @@ func main() {
158 log.Fatal("Internal error: " + err.Error()) 159 log.Fatal("Internal error: " + err.Error())
159 } 160 }
160 if len(buf) != len(dataBuf) { 161 if len(buf) != len(dataBuf) {
161 log.Fatal("Internal error: Buffer mismatch") 162 log.Fatalf("Internal error: Buffer mismatch %d != %d", len(buf), len(dataBuf))
162 } 163 }
163 n, err := file.Write(buf) 164 n, err := file.Write(buf)
164 if err != nil { 165 if err != nil {
diff --git a/src/gnunet/message/msg_revocation.go b/src/gnunet/message/msg_revocation.go
index 8c7db06..c17f440 100644
--- a/src/gnunet/message/msg_revocation.go
+++ b/src/gnunet/message/msg_revocation.go
@@ -105,6 +105,7 @@ type RevocationRevokeMsg struct {
105 MsgSize uint16 `order:"big"` // total size of message 105 MsgSize uint16 `order:"big"` // total size of message
106 MsgType uint16 `order:"big"` // REVOCATION_REVOKE (638) 106 MsgType uint16 `order:"big"` // REVOCATION_REVOKE (638)
107 Timestamp util.AbsoluteTime // Timestamp of revocation creation 107 Timestamp util.AbsoluteTime // Timestamp of revocation creation
108 TTL util.RelativeTime // TTL of revocation
108 PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of PoW values 109 PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of PoW values
109 Signature []byte `size:"64"` // Signature (Proof-of-ownership). 110 Signature []byte `size:"64"` // Signature (Proof-of-ownership).
110 ZoneKey []byte `size:"32"` // public zone key to be revoked 111 ZoneKey []byte `size:"32"` // public zone key to be revoked
@@ -116,6 +117,7 @@ func NewRevocationRevokeMsg(zoneKey *ed25519.PublicKey, sig *ed25519.EcSignature
116 MsgSize: 364, 117 MsgSize: 364,
117 MsgType: REVOCATION_REVOKE, 118 MsgType: REVOCATION_REVOKE,
118 Timestamp: util.AbsoluteTimeNow(), 119 Timestamp: util.AbsoluteTimeNow(),
120 TTL: util.RelativeTime{0},
119 PoWs: make([]uint64, 32), 121 PoWs: make([]uint64, 32),
120 Signature: make([]byte, 64), 122 Signature: make([]byte, 64),
121 ZoneKey: make([]byte, 32), 123 ZoneKey: make([]byte, 32),
diff --git a/src/gnunet/service/revocation/pow.go b/src/gnunet/service/revocation/pow.go
index 4f7fde2..03a89af 100644
--- a/src/gnunet/service/revocation/pow.go
+++ b/src/gnunet/service/revocation/pow.go
@@ -22,6 +22,8 @@ import (
22 "bytes" 22 "bytes"
23 "context" 23 "context"
24 "encoding/binary" 24 "encoding/binary"
25 "fmt"
26 "sort"
25 "time" 27 "time"
26 28
27 "gnunet/crypto" 29 "gnunet/crypto"
@@ -62,13 +64,13 @@ func NewPoWData(pow uint64, ts util.AbsoluteTime, zoneKey []byte) *PoWData {
62 return rd 64 return rd
63} 65}
64 66
67// SetPoW sets a new PoW value in the data structure
65func (p *PoWData) SetPoW(pow uint64) error { 68func (p *PoWData) SetPoW(pow uint64) error {
66 p.PoW = pow 69 p.PoW = pow
67 blob, err := data.Marshal(p) 70 p.blob = p.Blob()
68 if err != nil { 71 if p.blob == nil {
69 return err 72 return fmt.Errorf("Invalid PoW work unit")
70 } 73 }
71 p.blob = blob
72 return nil 74 return nil
73} 75}
74 76
@@ -98,10 +100,19 @@ func (p *PoWData) Next() {
98// Compute calculates the current result for a PoWData content. 100// Compute calculates the current result for a PoWData content.
99// The result is returned as a big integer value. 101// The result is returned as a big integer value.
100func (p *PoWData) Compute() *math.Int { 102func (p *PoWData) Compute() *math.Int {
101 key := argon2.Key(p.blob, []byte("gnunet-revocation-proof-of-work"), 3, 1024, 1, 64) 103 key := argon2.IDKey(p.blob, []byte("gnunet-revocation-proof-of-work"), 3, 1024, 1, 64)
102 return math.NewIntFromBytes(key) 104 return math.NewIntFromBytes(key)
103} 105}
104 106
107// Blob returns a serialized instance of the work unit
108func (p *PoWData) Blob() []byte {
109 blob, err := data.Marshal(p)
110 if err != nil {
111 return nil
112 }
113 return blob
114}
115
105//---------------------------------------------------------------------- 116//----------------------------------------------------------------------
106// Revocation data 117// Revocation data
107//---------------------------------------------------------------------- 118//----------------------------------------------------------------------
@@ -109,6 +120,7 @@ func (p *PoWData) Compute() *math.Int {
109// RevData is the revocation data (wire format) 120// RevData is the revocation data (wire format)
110type RevData struct { 121type RevData struct {
111 Timestamp util.AbsoluteTime // Timestamp of creation 122 Timestamp util.AbsoluteTime // Timestamp of creation
123 TTL util.RelativeTime // TTL of revocation
112 PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of PoW values 124 PoWs []uint64 `size:"32" order:"big"` // (Sorted) list of PoW values
113 Signature []byte `size:"64"` // Signature (Proof-of-ownership). 125 Signature []byte `size:"64"` // Signature (Proof-of-ownership).
114 ZoneKey []byte `size:"32"` // public zone key to be revoked 126 ZoneKey []byte `size:"32"` // public zone key to be revoked
@@ -121,18 +133,6 @@ type SignedRevData struct {
121 Timestamp util.AbsoluteTime // Timestamp of creation 133 Timestamp util.AbsoluteTime // Timestamp of creation
122} 134}
123 135
124// NewRevData initializes a new RevData instance
125func NewRevData(ts util.AbsoluteTime, pkey *ed25519.PublicKey) *RevData {
126 rd := &RevData{
127 Timestamp: ts,
128 PoWs: make([]uint64, 32),
129 Signature: make([]byte, 64),
130 ZoneKey: make([]byte, 32),
131 }
132 copy(rd.ZoneKey, pkey.Bytes())
133 return rd
134}
135
136// NewRevDataFromMsg initializes a new RevData instance from a GNUnet message 136// NewRevDataFromMsg initializes a new RevData instance from a GNUnet message
137func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData { 137func NewRevDataFromMsg(m *message.RevocationRevokeMsg) *RevData {
138 rd := &RevData{ 138 rd := &RevData{
@@ -153,7 +153,7 @@ func (rd *RevData) Sign(skey *ed25519.PrivateKey) error {
153 Size: 48, 153 Size: 48,
154 Purpose: enums.SIG_REVOCATION, 154 Purpose: enums.SIG_REVOCATION,
155 }, 155 },
156 ZoneKey: rd.ZoneKey, 156 ZoneKey: util.Clone(rd.ZoneKey),
157 Timestamp: rd.Timestamp, 157 Timestamp: rd.Timestamp,
158 } 158 }
159 sigData, err := data.Marshal(sigBlock) 159 sigData, err := data.Marshal(sigBlock)
@@ -182,7 +182,7 @@ func (rd *RevData) Verify(withSig bool) int {
182 Size: 48, 182 Size: 48,
183 Purpose: enums.SIG_REVOCATION, 183 Purpose: enums.SIG_REVOCATION,
184 }, 184 },
185 ZoneKey: rd.ZoneKey, 185 ZoneKey: util.Clone(rd.ZoneKey),
186 Timestamp: rd.Timestamp, 186 Timestamp: rd.Timestamp,
187 } 187 }
188 sigData, err := data.Marshal(sigBlock) 188 sigData, err := data.Marshal(sigBlock)
@@ -202,8 +202,8 @@ func (rd *RevData) Verify(withSig bool) int {
202 202
203 // (2) check PoWs 203 // (2) check PoWs
204 var ( 204 var (
205 zbits int = 512 205 zbits float64 = 0
206 last uint64 = 0 206 last uint64 = 0
207 ) 207 )
208 for _, pow := range rd.PoWs { 208 for _, pow := range rd.PoWs {
209 // check sequence order 209 // check sequence order
@@ -213,73 +213,144 @@ func (rd *RevData) Verify(withSig bool) int {
213 last = pow 213 last = pow
214 // compute number of leading zero-bits 214 // compute number of leading zero-bits
215 work := NewPoWData(pow, rd.Timestamp, rd.ZoneKey) 215 work := NewPoWData(pow, rd.Timestamp, rd.ZoneKey)
216 lzb := 512 - work.Compute().BitLen() 216 zbits += float64(512 - work.Compute().BitLen())
217 if lzb < zbits {
218 zbits = lzb
219 }
220 } 217 }
218 zbits /= 32.0
221 219
222 // (3) check expiration 220 // (3) check expiration
223 ttl := time.Duration((zbits-24)*365*24) * time.Hour 221 if zbits > 24.0 {
224 if util.AbsoluteTimeNow().Add(ttl).Expired() { 222 ttl := time.Duration(int((zbits-24)*365*24)) * time.Hour
225 return -2 223 if util.AbsoluteTimeNow().Add(ttl).Expired() {
224 return -2
225 }
226 }
227 return int(zbits)
228}
229
230//----------------------------------------------------------------------
231// RevData structure for computation
232//----------------------------------------------------------------------
233
234// RevDataCalc is the revocation data structure used while computing
235// the revocation data object.
236type RevDataCalc struct {
237 RevData
238 Bits []uint16 `size:"32" order:"big"` // number of leading zeros
239 SmallestIdx byte // index of smallest number of leading zeros
240}
241
242// NewRevDataCalc initializes a new RevDataCalc instance
243func NewRevDataCalc(pkey []byte) *RevDataCalc {
244 rd := &RevDataCalc{
245 RevData: RevData{
246 Timestamp: util.AbsoluteTimeNow(),
247 PoWs: make([]uint64, 32),
248 Signature: make([]byte, 64),
249 ZoneKey: make([]byte, 32),
250 },
251 Bits: make([]uint16, 32),
252 SmallestIdx: 0,
226 } 253 }
227 return zbits 254 copy(rd.ZoneKey, pkey)
255 return rd
228} 256}
229 257
230// Compute tries to compute a valid Revocation; it returns the number of 258// Average number of leading zero-bits in current list
231// solved PoWs. The computation is complete if 32 PoWs have been found. 259func (rdc *RevDataCalc) Average() float64 {
232func (rd *RevData) Compute(ctx context.Context, bits int, last uint64) (int, uint64) { 260 var sum uint16 = 0
233 // set difficulty based on requested number of leading zero-bits 261 for _, num := range rdc.Bits {
234 difficulty := math.TWO.Pow(512 - bits).Sub(math.ONE) 262 sum += num
263 }
264 return float64(sum) / 32.
265}
235 266
236 // initialize a new work record (single PoW computation) 267// Insert a PoW that is "better than the worst" current PoW element.
237 work := NewPoWData(0, rd.Timestamp, rd.ZoneKey) 268func (rdc *RevDataCalc) Insert(pow uint64, bits uint16) (float64, uint16) {
269 if bits > rdc.Bits[rdc.SmallestIdx] {
270 rdc.PoWs[rdc.SmallestIdx] = pow
271 rdc.Bits[rdc.SmallestIdx] = bits
272 rdc.sortBits()
273 }
274 return rdc.Average(), rdc.Bits[rdc.SmallestIdx]
275}
238 276
239 // work on all PoWs in a revocation data structure; make sure all PoWs 277// Get the smallest bit position
240 // are set to a valid value (that results in a valid compute() result 278func (rdc *RevDataCalc) sortBits() {
241 // below a given threshold) 279 var (
242 for i, pow := range rd.PoWs { 280 min uint16 = 512
243 // handle "new" pow value: set it to last_pow+1 281 pos = 0
244 // this ensures a correctly sorted pow list by design. 282 )
245 if pow == 0 && last != 0 { 283 for i, bits := range rdc.Bits {
246 pow, last = last, 0 284 if bits < min {
285 min = bits
286 pos = i
247 } 287 }
248 if pow == 0 && i > 0 { 288 }
249 pow = rd.PoWs[i-1] + 1 289 rdc.SmallestIdx = byte(pos)
290}
291
292// Compute tries to compute a valid Revocation; it returns the average number
293// of leading zero-bits and the last PoW value tried. The computation is
294// complete if the average above is greater or equal to 'bits'.
295func (rdc *RevDataCalc) Compute(ctx context.Context, bits int, last uint64, cb func(float64, uint64)) (float64, uint64) {
296 // find the largest PoW value in current work unit
297 work := NewPoWData(0, rdc.Timestamp, rdc.ZoneKey)
298 var max uint64 = 0
299 for i, pow := range rdc.PoWs {
300 if pow == 0 {
301 max++
302 work.SetPoW(max)
303 res := work.Compute()
304 rdc.Bits[i] = uint16(512 - res.BitLen())
305 } else if pow > max {
306 max = pow
250 } 307 }
251 // prepare for PoW_i 308 }
252 work.SetPoW(pow) 309 // adjust 'last' value
310 if last <= max {
311 last = max + 1
312 }
253 313
254 // Find PoW value in an (interruptable) loop 314 // Find PoW value in an (interruptable) loop
255 out := make(chan bool) 315 out := make(chan bool)
256 go func() { 316 go func() {
257 for { 317 work.SetPoW(last + 1)
258 res := work.Compute() 318 smallest := rdc.Bits[rdc.SmallestIdx]
259 if res.Cmp(difficulty) < 0 { 319 average := rdc.Average()
260 break 320 for average < float64(bits) {
261 } 321 res := work.Compute()
262 work.Next() 322 num := uint16(512 - res.BitLen())
263 } 323 if num > smallest {
264 out <- true 324 pow := work.GetPoW()
265 }() 325 average, smallest = rdc.Insert(pow, num)
266 loop: 326 cb(average, pow)
267 for {
268 select {
269 case <-out:
270 rd.PoWs[i] = work.GetPoW()
271 break loop
272 case <-ctx.Done():
273 return i, work.GetPoW() + 1
274 } 327 }
328 work.Next()
329 }
330 out <- true
331 }()
332loop:
333 for {
334 select {
335 case <-out:
336 break loop
337 case <-ctx.Done():
338 break loop
275 } 339 }
276 } 340 }
277 // we have found all valid PoW values. 341 // re-order the PoWs for compliance
278 return 32, 0 342 sort.Slice(rdc.PoWs, func(i, j int) bool { return rdc.PoWs[i] < rdc.PoWs[j] })
343 for i, pow := range rdc.PoWs {
344 work.SetPoW(pow)
345 rdc.Bits[i] = uint16(512 - work.Compute().BitLen())
346 }
347 rdc.sortBits()
348 return rdc.Average(), work.GetPoW()
279} 349}
280 350
281func (rd *RevData) Blob() []byte { 351// Blob returns the binary data structure (wire format).
282 blob, err := data.Marshal(rd) 352func (rdc *RevDataCalc) Blob() []byte {
353 blob, err := data.Marshal(rdc)
283 if err != nil { 354 if err != nil {
284 return nil 355 return nil
285 } 356 }
diff --git a/src/gnunet/service/revocation/pow_test.go b/src/gnunet/service/revocation/pow_test.go
new file mode 100644
index 0000000..aba07d9
--- /dev/null
+++ b/src/gnunet/service/revocation/pow_test.go
@@ -0,0 +1,132 @@
1package revocation
2
3import (
4 "bytes"
5 "encoding/hex"
6 "fmt"
7 "testing"
8
9 "gnunet/util"
10
11 "github.com/bfix/gospel/crypto/ed25519"
12 "github.com/bfix/gospel/data"
13 "github.com/bfix/gospel/math"
14)
15
16type testData struct {
17 skey string
18 pkey string
19 revdata string
20}
21
22var (
23 test_data = []testData{
24 {
25
26 "e01d304d45676849edcb36c843ad31837c9de8c7e58028a2e7c2a9894f130b6f", // private scalar D
27 "d2c825295cfd3073b6149c4393aa9483c51cfaf62731d2bf1127856913233b78", // public key
28 "" +
29 "0005a5fc192e1d2c" + // timestamp
30 "0000395d1827c000" + // TTL
31 "f74d39f9ee9a7344" + // PoW_0
32 "f74d39f9ee9a7610" +
33 "f74d39f9ee9a7677" +
34 "f74d39f9ee9a7774" +
35 "f74d39f9ee9a777d" +
36 "f74d39f9ee9a77a3" +
37 "f74d39f9ee9a77ad" +
38 "f74d39f9ee9a77b9" +
39 "f74d39f9ee9a77de" +
40 "f74d39f9ee9a7851" +
41 "f74d39f9ee9a786f" +
42 "f74d39f9ee9a78a3" +
43 "f74d39f9ee9a78ba" +
44 "f74d39f9ee9a78ca" +
45 "f74d39f9ee9a7916" +
46 "f74d39f9ee9a79a9" +
47 "f74d39f9ee9a7a37" +
48 "f74d39f9ee9a7a57" +
49 "f74d39f9ee9a7a5c" +
50 "f74d39f9ee9a7a9e" +
51 "f74d39f9ee9a7ad3" +
52 "f74d39f9ee9a7b1b" +
53 "f74d39f9ee9a7b7b" +
54 "f74d39f9ee9a7b83" +
55 "f74d39f9ee9a7b8b" +
56 "f74d39f9ee9a7bbe" +
57 "f74d39f9ee9a7bcc" +
58 "f74d39f9ee9a7be6" +
59 "f74d39f9ee9a7c2b" +
60 "f74d39f9ee9a7c5b" +
61 "f74d39f9ee9a7c5f" +
62 "f74d39f9ee9a7c83" + // PoW_31
63 "05b94e2ad6496a8938aaf122f91edbacf2401cce8ec02e551e2a4433e0a76256" + // Sig.R
64 "09195bbe7636e9fd9076f8f20bc62467cc8371c487e7809efeaeb6ef7178b623" + // Sig.S
65 "d2c825295cfd3073b6149c4393aa9483c51cfaf62731d2bf1127856913233b78", // PKEY
66 },
67 }
68)
69
70func TestRevocationRFC(t *testing.T) {
71
72 for i, td := range test_data {
73 if testing.Verbose() {
74 fmt.Println("---------------------------------")
75 fmt.Printf("Test case #%d\n", i+1)
76 fmt.Println("---------------------------------")
77 }
78
79 // construct private/public key pair from test data
80 skey_d, err := hex.DecodeString(td.skey)
81 if err != nil {
82 t.Fatal(err)
83 }
84 d := math.NewIntFromBytes(util.Reverse(skey_d))
85 skey := ed25519.NewPrivateKeyFromD(d)
86 pkey_d, err := hex.DecodeString(td.pkey)
87 if err != nil {
88 t.Fatal(err)
89 }
90 if bytes.Compare(skey.Public().Bytes(), pkey_d) != 0 {
91 t.Fatal("Private/Public key mismatch")
92 }
93
94 // assemble revocation data object
95 rev_d, err := hex.DecodeString(td.revdata)
96 if err != nil {
97 t.Fatal(err)
98 }
99 revData := new(RevData)
100 if err = data.Unmarshal(revData, rev_d); err != nil {
101 t.Fatal(err)
102 }
103 if bytes.Compare(revData.ZoneKey, pkey_d) != 0 {
104 t.Fatal("Wrong zone key in test revocation")
105 }
106
107 // show revdata content
108 if testing.Verbose() {
109 fmt.Println("REVDATA:")
110 fmt.Printf(" Timestamp: %s\n", revData.Timestamp.String())
111 fmt.Printf(" TTL: %s\n", revData.TTL.String())
112
113 work := NewPoWData(0, revData.Timestamp, revData.ZoneKey)
114 for i, pow := range revData.PoWs {
115 fmt.Printf(" PoW #%d: %d\n", i, pow)
116 work.SetPoW(pow)
117 buf := work.Blob()
118 fmt.Printf(" P: %s\n", hex.EncodeToString(buf))
119 v := work.Compute()
120 fmt.Printf(" H: %s\n", hex.EncodeToString(v.Bytes()))
121 num := 512 - v.BitLen()
122 fmt.Printf(" --> %d leading zeros\n", num)
123 }
124 fmt.Printf(" Signature: %s\n", hex.EncodeToString(revData.Signature))
125 fmt.Printf(" ZoneKey: %s\n", hex.EncodeToString(revData.ZoneKey))
126 }
127
128 // verify revocation data object
129 rc := revData.Verify(true)
130 fmt.Printf("REV_Verify (pkey): %d\n", rc)
131 }
132}