diff options
Diffstat (limited to 'src/gnunet/service/gns/block.go')
-rw-r--r-- | src/gnunet/service/gns/block.go | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/src/gnunet/service/gns/block.go b/src/gnunet/service/gns/block.go new file mode 100644 index 0000000..9d83f99 --- /dev/null +++ b/src/gnunet/service/gns/block.go | |||
@@ -0,0 +1,185 @@ | |||
1 | package gns | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | |||
6 | "gnunet/crypto" | ||
7 | "gnunet/enums" | ||
8 | "gnunet/message" | ||
9 | "gnunet/util" | ||
10 | |||
11 | "github.com/bfix/gospel/crypto/ed25519" | ||
12 | "github.com/bfix/gospel/data" | ||
13 | ) | ||
14 | |||
15 | var ( | ||
16 | ErrBlockNotDecrypted = fmt.Errorf("GNS block not decrypted") | ||
17 | ) | ||
18 | |||
19 | //====================================================================== | ||
20 | // GNS block: An encrypted and signed container for GNS resource records | ||
21 | // that represents the "atomic" data structure associated with a GNS | ||
22 | // label in a given zone. | ||
23 | //====================================================================== | ||
24 | |||
25 | // SignedBlockData: signed and encrypted list of resource records stored | ||
26 | // in a GNSRecordSet | ||
27 | type SignedBlockData struct { | ||
28 | Purpose *crypto.SignaturePurpose // Size and purpose of signature (8 bytes) | ||
29 | Expire util.AbsoluteTime // Expiration time of the block. | ||
30 | EncData []byte `size:"*"` // encrypted GNSRecordSet | ||
31 | |||
32 | // transient data (not serialized) | ||
33 | data []byte // unencrypted GNSRecord set | ||
34 | } | ||
35 | |||
36 | // GNSBlock is the result of GNS lookups for a given label in a zone. | ||
37 | type GNSBlock struct { | ||
38 | Signature []byte `size:"64"` // Signature of the block. | ||
39 | DerivedKey []byte `size:"32"` // Derived key used for signing | ||
40 | Block *SignedBlockData | ||
41 | |||
42 | // transient data (not serialized) | ||
43 | checked bool // block integrity checked | ||
44 | verified bool // block signature verified (internal) | ||
45 | decrypted bool // block data decrypted (internal) | ||
46 | } | ||
47 | |||
48 | // String returns the human-readable representation of a GNSBlock | ||
49 | func (b *GNSBlock) String() string { | ||
50 | return fmt.Sprintf("GNSBlock{Verified=%v,Decrypted=%v,data=[%d]}", | ||
51 | b.verified, b.decrypted, len(b.Block.EncData)) | ||
52 | } | ||
53 | |||
54 | // Records returns the list of resource records in a block. | ||
55 | func (b *GNSBlock) Records() ([]*message.GNSResourceRecord, error) { | ||
56 | // check if block is decrypted | ||
57 | if !b.decrypted { | ||
58 | return nil, ErrBlockNotDecrypted | ||
59 | } | ||
60 | // parse block data into record set | ||
61 | rs := NewGNSRecordSet() | ||
62 | if err := data.Unmarshal(rs, b.Block.data); err != nil { | ||
63 | return nil, err | ||
64 | } | ||
65 | return rs.Records, nil | ||
66 | } | ||
67 | |||
68 | // Verify the integrity of the block data from a signature. | ||
69 | func (b *GNSBlock) Verify(zoneKey *ed25519.PublicKey, label string) (err error) { | ||
70 | // Integrity check performed | ||
71 | b.checked = true | ||
72 | |||
73 | // verify derived key | ||
74 | dkey := ed25519.NewPublicKeyFromBytes(b.DerivedKey) | ||
75 | dkey2 := crypto.DerivePublicKey(zoneKey, label, "gns") | ||
76 | if !dkey.Q.Equals(dkey2.Q) { | ||
77 | return fmt.Errorf("Invalid signature key for GNS Block") | ||
78 | } | ||
79 | // verify signature | ||
80 | var ( | ||
81 | sig *ed25519.EcSignature | ||
82 | buf []byte | ||
83 | ok bool | ||
84 | ) | ||
85 | if sig, err = ed25519.NewEcSignatureFromBytes(b.Signature); err != nil { | ||
86 | return | ||
87 | } | ||
88 | if buf, err = data.Marshal(b.Block); err != nil { | ||
89 | return | ||
90 | } | ||
91 | if ok, err = dkey.EcVerify(buf, sig); err == nil && !ok { | ||
92 | err = fmt.Errorf("Signature verification failed for GNS block") | ||
93 | } | ||
94 | b.verified = true | ||
95 | return | ||
96 | } | ||
97 | |||
98 | // Decrypt block data with a key/iv combination derived from (PKEY,label) | ||
99 | func (b *GNSBlock) Decrypt(zoneKey *ed25519.PublicKey, label string) (err error) { | ||
100 | // decrypt payload | ||
101 | b.Block.data, err = crypto.DecryptBlock(b.Block.EncData, zoneKey, label) | ||
102 | b.decrypted = true | ||
103 | return | ||
104 | } | ||
105 | |||
106 | // NewGNSBlock instantiates an empty GNS block | ||
107 | func NewGNSBlock() *GNSBlock { | ||
108 | return &GNSBlock{ | ||
109 | Signature: make([]byte, 64), | ||
110 | DerivedKey: make([]byte, 32), | ||
111 | Block: &SignedBlockData{ | ||
112 | Purpose: new(crypto.SignaturePurpose), | ||
113 | Expire: *new(util.AbsoluteTime), | ||
114 | EncData: nil, | ||
115 | data: nil, | ||
116 | }, | ||
117 | checked: false, | ||
118 | verified: false, | ||
119 | decrypted: false, | ||
120 | } | ||
121 | } | ||
122 | |||
123 | //---------------------------------------------------------------------- | ||
124 | // GNSRecordSet | ||
125 | //---------------------------------------------------------------------- | ||
126 | |||
127 | // GNSRecordSet ist the GNUnet data structure for a list of resource records | ||
128 | // in a GNSBlock. As part of GNUnet messages, the record set is padded so that | ||
129 | // the binary size of (records||padding) is the smallest power of two. | ||
130 | type GNSRecordSet struct { | ||
131 | Count uint32 `order:"big"` // number of resource records | ||
132 | Records []*message.GNSResourceRecord `size:"Count"` // list of resource records | ||
133 | Padding []byte `size:"*"` // padding | ||
134 | } | ||
135 | |||
136 | // NewGNSRecordSet returns an empty resource record set. | ||
137 | func NewGNSRecordSet() *GNSRecordSet { | ||
138 | return &GNSRecordSet{ | ||
139 | Count: 0, | ||
140 | Records: make([]*message.GNSResourceRecord, 0), | ||
141 | Padding: make([]byte, 0), | ||
142 | } | ||
143 | } | ||
144 | |||
145 | // AddRecord to append a resource record to the set. | ||
146 | func (rs *GNSRecordSet) AddRecord(rec *message.GNSResourceRecord) { | ||
147 | rs.Count++ | ||
148 | rs.Records = append(rs.Records, rec) | ||
149 | } | ||
150 | |||
151 | //====================================================================== | ||
152 | // List of resource records types (for GNS/DNS queries) | ||
153 | //====================================================================== | ||
154 | |||
155 | // RRTypeList is a list of integers representing RR types. | ||
156 | type RRTypeList []int | ||
157 | |||
158 | // Initialize a new type list with given type values | ||
159 | func NewRRTypeList(args ...int) (res RRTypeList) { | ||
160 | for _, val := range args { | ||
161 | // if GNS_TYPE_ANY is encountered, it becomes the sole type | ||
162 | if val == enums.GNS_TYPE_ANY { | ||
163 | res = make(RRTypeList, 1) | ||
164 | res[0] = val | ||
165 | return | ||
166 | } | ||
167 | res = append(res, val) | ||
168 | } | ||
169 | return | ||
170 | } | ||
171 | |||
172 | // HasType returns true if the type is included in the list | ||
173 | func (tl RRTypeList) HasType(t int) bool { | ||
174 | // return true if type is GNS_TYPE_ANY | ||
175 | if tl[0] == enums.GNS_TYPE_ANY { | ||
176 | return true | ||
177 | } | ||
178 | // check for type in list | ||
179 | for _, val := range tl { | ||
180 | if val == t { | ||
181 | return true | ||
182 | } | ||
183 | } | ||
184 | return false | ||
185 | } | ||