-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
root_metadata_signed.go
293 lines (269 loc) · 8.83 KB
/
root_metadata_signed.go
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.
package kbfsmd
import (
"context"
"errors"
"fmt"
"github.com/keybase/client/go/kbfs/kbfscodec"
"github.com/keybase/client/go/kbfs/kbfscrypto"
"github.com/keybase/client/go/kbfs/tlf"
"github.com/keybase/client/go/protocol/keybase1"
)
// RootMetadataSigned is the top-level MD object stored in MD server
type RootMetadataSigned struct {
// SigInfo is the signature over the root metadata by the
// last modifying user's private signing key.
SigInfo kbfscrypto.SignatureInfo
// WriterSigInfo is the signature over the writer metadata by
// the last modifying writer's private signing key.
WriterSigInfo kbfscrypto.SignatureInfo
// all the metadata
MD RootMetadata
}
func checkWriterSig(rmds *RootMetadataSigned) error {
if mdv2, ok := rmds.MD.(*RootMetadataV2); ok {
if !mdv2.WriterMetadataSigInfo.Equals(rmds.WriterSigInfo) {
return fmt.Errorf(
"Expected writer sig info %v, got %v",
mdv2.WriterMetadataSigInfo, rmds.WriterSigInfo)
}
}
return nil
}
// makeRootMetadataSigned makes a RootMetadataSigned object from the
// given info. If md stores the writer signature info internally, it
// must match the given one.
func makeRootMetadataSigned(sigInfo, writerSigInfo kbfscrypto.SignatureInfo,
md RootMetadata) (*RootMetadataSigned, error) {
rmds := &RootMetadataSigned{
MD: md,
SigInfo: sigInfo,
WriterSigInfo: writerSigInfo,
}
err := checkWriterSig(rmds)
if err != nil {
return nil, err
}
return rmds, nil
}
// SignRootMetadata signs the given RootMetadata and returns a
// *RootMetadataSigned object. rootMetadataSigner and
// writerMetadataSigner should be the same, except in tests.
func SignRootMetadata(
ctx context.Context, codec kbfscodec.Codec,
rootMetadataSigner, writerMetadataSigner kbfscrypto.Signer,
brmd RootMetadata) (*RootMetadataSigned, error) {
// encode the root metadata
buf, err := codec.Encode(brmd)
if err != nil {
return nil, err
}
var sigInfo, writerSigInfo kbfscrypto.SignatureInfo
if mdv2, ok := brmd.(*RootMetadataV2); ok {
// sign the root metadata
sigInfo, err = rootMetadataSigner.Sign(ctx, buf)
if err != nil {
return nil, err
}
// Assume that writerMetadataSigner has already signed
// mdv2 internally. If not, makeRootMetadataSigned
// will catch it.
writerSigInfo = mdv2.WriterMetadataSigInfo
} else {
// sign the root metadata -- use the KBFS signing prefix.
sigInfo, err = rootMetadataSigner.SignForKBFS(ctx, buf)
if err != nil {
return nil, err
}
buf, err = brmd.GetSerializedWriterMetadata(codec)
if err != nil {
return nil, err
}
// sign the writer metadata
writerSigInfo, err = writerMetadataSigner.SignForKBFS(ctx, buf)
if err != nil {
return nil, err
}
}
return makeRootMetadataSigned(sigInfo, writerSigInfo, brmd)
}
// GetWriterMetadataSigInfo returns the signature of the writer
// metadata.
func (rmds *RootMetadataSigned) GetWriterMetadataSigInfo() kbfscrypto.SignatureInfo {
return rmds.WriterSigInfo
}
// Version returns the metadata version of this MD block, depending on
// which features it uses.
func (rmds *RootMetadataSigned) Version() MetadataVer {
return rmds.MD.Version()
}
// MakeFinalCopy returns a complete copy of this RootMetadataSigned
// with the revision incremented and the final bit set.
func (rmds *RootMetadataSigned) MakeFinalCopy(
codec kbfscodec.Codec, finalizedInfo *tlf.HandleExtension) (*RootMetadataSigned, error) {
if finalizedInfo.Type != tlf.HandleExtensionFinalized {
return nil, fmt.Errorf(
"Extension %s does not have finalized type",
finalizedInfo)
}
if rmds.MD.IsFinal() {
return nil, MetadataIsFinalError{}
}
newMd, err := rmds.MD.DeepCopy(codec)
if err != nil {
return nil, err
}
// Set the final flag.
newMd.SetFinalBit()
// Set the copied bit, so that clients don't take the ops and byte
// counts in it seriously.
newMd.SetWriterMetadataCopiedBit()
// Increment revision but keep the PrevRoot --
// We want the client to be able to verify the signature by masking out the final
// bit, decrementing the revision, and nulling out the finalized extension info.
// This way it can easily tell a server didn't modify anything unexpected when
// creating the final metadata block. Note that PrevRoot isn't being updated. This
// is to make verification easier for the client as otherwise it'd need to request
// the head revision - 1.
newMd.SetRevision(rmds.MD.RevisionNumber() + 1)
newMd.SetFinalizedInfo(finalizedInfo)
return makeRootMetadataSigned(
rmds.SigInfo.DeepCopy(), rmds.WriterSigInfo.DeepCopy(),
newMd)
}
// IsValidAndSigned verifies the RootMetadataSigned, checks the root
// signature, and returns an error if a problem was found. This
// should be the first thing checked on an RMDS retrieved from an
// untrusted source, and then the signing users and keys should be
// validated, either by comparing to the current device key (using
// IsLastModifiedBy), or by checking with KBPKI.
func (rmds *RootMetadataSigned) IsValidAndSigned(
ctx context.Context, codec kbfscodec.Codec,
teamMemChecker TeamMembershipChecker, extra ExtraMetadata,
offline keybase1.OfflineAvailability) error {
// Optimization -- if the RootMetadata signature is nil, it
// will fail verification.
if rmds.SigInfo.IsNil() {
return errors.New("Missing RootMetadata signature")
}
// Optimization -- if the WriterMetadata signature is nil, it
// will fail verification.
if rmds.WriterSigInfo.IsNil() {
return errors.New("Missing WriterMetadata signature")
}
err := rmds.MD.IsValidAndSigned(
ctx, codec, teamMemChecker, extra, rmds.WriterSigInfo.VerifyingKey,
offline)
if err != nil {
return err
}
md := rmds.MD
if rmds.MD.IsFinal() {
mdCopy, err := md.DeepCopy(codec)
if err != nil {
return err
}
// Mask out finalized additions. These are the only
// things allowed to change in the finalized metadata
// block.
mdCopy.ClearFinalBit()
mdCopy.ClearWriterMetadataCopiedBit()
mdCopy.SetRevision(md.RevisionNumber() - 1)
mdCopy.SetFinalizedInfo(nil)
md = mdCopy
}
// Re-marshal the whole RootMetadata. This is not avoidable
// without support from ugorji/codec.
buf, err := codec.Encode(md)
if err != nil {
return err
}
err = kbfscrypto.Verify(buf, rmds.SigInfo)
if err != nil {
return fmt.Errorf("Could not verify root metadata: %v", err)
}
buf, err = md.GetSerializedWriterMetadata(codec)
if err != nil {
return err
}
err = kbfscrypto.Verify(buf, rmds.WriterSigInfo)
if err != nil {
return fmt.Errorf("Could not verify writer metadata: %v", err)
}
return nil
}
// IsLastModifiedBy verifies that the RootMetadataSigned is written by
// the given user and device (identified by the device verifying key),
// and returns an error if not.
func (rmds *RootMetadataSigned) IsLastModifiedBy(
uid keybase1.UID, key kbfscrypto.VerifyingKey) error {
err := rmds.MD.IsLastModifiedBy(uid, key)
if err != nil {
return err
}
if rmds.SigInfo.VerifyingKey != key {
return fmt.Errorf("Last modifier verifying key %v != %v",
rmds.SigInfo.VerifyingKey, key)
}
writer := rmds.MD.LastModifyingWriter()
if !rmds.MD.IsWriterMetadataCopiedSet() {
if writer != uid {
return fmt.Errorf("Last writer %s != %s", writer, uid)
}
if rmds.WriterSigInfo.VerifyingKey != key {
return fmt.Errorf(
"Last writer verifying key %v != %v",
rmds.WriterSigInfo.VerifyingKey, key)
}
}
return nil
}
// EncodeRootMetadataSigned serializes a metadata block. This should
// be used instead of directly calling codec.Encode(), as it handles
// some version-specific quirks.
func EncodeRootMetadataSigned(
codec kbfscodec.Codec, rmds *RootMetadataSigned) ([]byte, error) {
err := checkWriterSig(rmds)
if err != nil {
return nil, err
}
rmdsCopy := *rmds
if rmdsCopy.Version() < SegregatedKeyBundlesVer {
// For v2, the writer signature is in rmds.MD, so
// remove the one in rmds.
rmdsCopy.WriterSigInfo = kbfscrypto.SignatureInfo{}
}
return codec.Encode(rmdsCopy)
}
// DecodeRootMetadataSigned deserializes a metadata block into the
// specified versioned structure.
func DecodeRootMetadataSigned(
codec kbfscodec.Codec, tlf tlf.ID, ver, max MetadataVer, buf []byte) (
*RootMetadataSigned, error) {
rmd, err := makeMutableRootMetadataForDecode(codec, tlf, ver, max, buf)
if err != nil {
return nil, err
}
rmds := RootMetadataSigned{
MD: rmd,
}
if err := codec.Decode(buf, &rmds); err != nil {
return nil, err
}
if ver < SegregatedKeyBundlesVer {
// For v2, the writer signature is in rmds.MD, so copy
// it out.
if !rmds.WriterSigInfo.IsNil() {
return nil, fmt.Errorf(
"Decoded RootMetadataSigned with version "+
"%d unexpectedly has non-nil "+
"writer signature %s",
ver, rmds.WriterSigInfo)
}
mdv2 := rmds.MD.(*RootMetadataV2)
rmds.WriterSigInfo = mdv2.WriterMetadataSigInfo
}
return &rmds, nil
}