github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsmd/root_metadata_signed.go (about) 1 // Copyright 2017 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package kbfsmd 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 12 "github.com/keybase/client/go/kbfs/kbfscodec" 13 "github.com/keybase/client/go/kbfs/kbfscrypto" 14 "github.com/keybase/client/go/kbfs/tlf" 15 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 // RootMetadataSigned is the top-level MD object stored in MD server 19 type RootMetadataSigned struct { 20 // SigInfo is the signature over the root metadata by the 21 // last modifying user's private signing key. 22 SigInfo kbfscrypto.SignatureInfo 23 // WriterSigInfo is the signature over the writer metadata by 24 // the last modifying writer's private signing key. 25 WriterSigInfo kbfscrypto.SignatureInfo 26 // all the metadata 27 MD RootMetadata 28 } 29 30 func checkWriterSig(rmds *RootMetadataSigned) error { 31 if mdv2, ok := rmds.MD.(*RootMetadataV2); ok { 32 if !mdv2.WriterMetadataSigInfo.Equals(rmds.WriterSigInfo) { 33 return fmt.Errorf( 34 "Expected writer sig info %v, got %v", 35 mdv2.WriterMetadataSigInfo, rmds.WriterSigInfo) 36 } 37 } 38 return nil 39 } 40 41 // makeRootMetadataSigned makes a RootMetadataSigned object from the 42 // given info. If md stores the writer signature info internally, it 43 // must match the given one. 44 func makeRootMetadataSigned(sigInfo, writerSigInfo kbfscrypto.SignatureInfo, 45 md RootMetadata) (*RootMetadataSigned, error) { 46 rmds := &RootMetadataSigned{ 47 MD: md, 48 SigInfo: sigInfo, 49 WriterSigInfo: writerSigInfo, 50 } 51 err := checkWriterSig(rmds) 52 if err != nil { 53 return nil, err 54 } 55 return rmds, nil 56 } 57 58 // SignRootMetadata signs the given RootMetadata and returns a 59 // *RootMetadataSigned object. rootMetadataSigner and 60 // writerMetadataSigner should be the same, except in tests. 61 func SignRootMetadata( 62 ctx context.Context, codec kbfscodec.Codec, 63 rootMetadataSigner, writerMetadataSigner kbfscrypto.Signer, 64 brmd RootMetadata) (*RootMetadataSigned, error) { 65 // encode the root metadata 66 buf, err := codec.Encode(brmd) 67 if err != nil { 68 return nil, err 69 } 70 71 var sigInfo, writerSigInfo kbfscrypto.SignatureInfo 72 if mdv2, ok := brmd.(*RootMetadataV2); ok { 73 // sign the root metadata 74 sigInfo, err = rootMetadataSigner.Sign(ctx, buf) 75 if err != nil { 76 return nil, err 77 } 78 // Assume that writerMetadataSigner has already signed 79 // mdv2 internally. If not, makeRootMetadataSigned 80 // will catch it. 81 writerSigInfo = mdv2.WriterMetadataSigInfo 82 } else { 83 // sign the root metadata -- use the KBFS signing prefix. 84 sigInfo, err = rootMetadataSigner.SignForKBFS(ctx, buf) 85 if err != nil { 86 return nil, err 87 } 88 buf, err = brmd.GetSerializedWriterMetadata(codec) 89 if err != nil { 90 return nil, err 91 } 92 // sign the writer metadata 93 writerSigInfo, err = writerMetadataSigner.SignForKBFS(ctx, buf) 94 if err != nil { 95 return nil, err 96 } 97 } 98 return makeRootMetadataSigned(sigInfo, writerSigInfo, brmd) 99 } 100 101 // GetWriterMetadataSigInfo returns the signature of the writer 102 // metadata. 103 func (rmds *RootMetadataSigned) GetWriterMetadataSigInfo() kbfscrypto.SignatureInfo { 104 return rmds.WriterSigInfo 105 } 106 107 // Version returns the metadata version of this MD block, depending on 108 // which features it uses. 109 func (rmds *RootMetadataSigned) Version() MetadataVer { 110 return rmds.MD.Version() 111 } 112 113 // MakeFinalCopy returns a complete copy of this RootMetadataSigned 114 // with the revision incremented and the final bit set. 115 func (rmds *RootMetadataSigned) MakeFinalCopy( 116 codec kbfscodec.Codec, finalizedInfo *tlf.HandleExtension) (*RootMetadataSigned, error) { 117 if finalizedInfo.Type != tlf.HandleExtensionFinalized { 118 return nil, fmt.Errorf( 119 "Extension %s does not have finalized type", 120 finalizedInfo) 121 } 122 if rmds.MD.IsFinal() { 123 return nil, MetadataIsFinalError{} 124 } 125 newMd, err := rmds.MD.DeepCopy(codec) 126 if err != nil { 127 return nil, err 128 } 129 // Set the final flag. 130 newMd.SetFinalBit() 131 // Set the copied bit, so that clients don't take the ops and byte 132 // counts in it seriously. 133 newMd.SetWriterMetadataCopiedBit() 134 // Increment revision but keep the PrevRoot -- 135 // We want the client to be able to verify the signature by masking out the final 136 // bit, decrementing the revision, and nulling out the finalized extension info. 137 // This way it can easily tell a server didn't modify anything unexpected when 138 // creating the final metadata block. Note that PrevRoot isn't being updated. This 139 // is to make verification easier for the client as otherwise it'd need to request 140 // the head revision - 1. 141 newMd.SetRevision(rmds.MD.RevisionNumber() + 1) 142 newMd.SetFinalizedInfo(finalizedInfo) 143 return makeRootMetadataSigned( 144 rmds.SigInfo.DeepCopy(), rmds.WriterSigInfo.DeepCopy(), 145 newMd) 146 } 147 148 // IsValidAndSigned verifies the RootMetadataSigned, checks the root 149 // signature, and returns an error if a problem was found. This 150 // should be the first thing checked on an RMDS retrieved from an 151 // untrusted source, and then the signing users and keys should be 152 // validated, either by comparing to the current device key (using 153 // IsLastModifiedBy), or by checking with KBPKI. 154 func (rmds *RootMetadataSigned) IsValidAndSigned( 155 ctx context.Context, codec kbfscodec.Codec, 156 teamMemChecker TeamMembershipChecker, extra ExtraMetadata, 157 offline keybase1.OfflineAvailability) error { 158 // Optimization -- if the RootMetadata signature is nil, it 159 // will fail verification. 160 if rmds.SigInfo.IsNil() { 161 return errors.New("Missing RootMetadata signature") 162 } 163 // Optimization -- if the WriterMetadata signature is nil, it 164 // will fail verification. 165 if rmds.WriterSigInfo.IsNil() { 166 return errors.New("Missing WriterMetadata signature") 167 } 168 169 err := rmds.MD.IsValidAndSigned( 170 ctx, codec, teamMemChecker, extra, rmds.WriterSigInfo.VerifyingKey, 171 offline) 172 if err != nil { 173 return err 174 } 175 176 md := rmds.MD 177 if rmds.MD.IsFinal() { 178 mdCopy, err := md.DeepCopy(codec) 179 if err != nil { 180 return err 181 } 182 // Mask out finalized additions. These are the only 183 // things allowed to change in the finalized metadata 184 // block. 185 mdCopy.ClearFinalBit() 186 mdCopy.ClearWriterMetadataCopiedBit() 187 mdCopy.SetRevision(md.RevisionNumber() - 1) 188 mdCopy.SetFinalizedInfo(nil) 189 md = mdCopy 190 } 191 // Re-marshal the whole RootMetadata. This is not avoidable 192 // without support from ugorji/codec. 193 buf, err := codec.Encode(md) 194 if err != nil { 195 return err 196 } 197 198 err = kbfscrypto.Verify(buf, rmds.SigInfo) 199 if err != nil { 200 return fmt.Errorf("Could not verify root metadata: %v", err) 201 } 202 203 buf, err = md.GetSerializedWriterMetadata(codec) 204 if err != nil { 205 return err 206 } 207 208 err = kbfscrypto.Verify(buf, rmds.WriterSigInfo) 209 if err != nil { 210 return fmt.Errorf("Could not verify writer metadata: %v", err) 211 } 212 213 return nil 214 } 215 216 // IsLastModifiedBy verifies that the RootMetadataSigned is written by 217 // the given user and device (identified by the device verifying key), 218 // and returns an error if not. 219 func (rmds *RootMetadataSigned) IsLastModifiedBy( 220 uid keybase1.UID, key kbfscrypto.VerifyingKey) error { 221 err := rmds.MD.IsLastModifiedBy(uid, key) 222 if err != nil { 223 return err 224 } 225 226 if rmds.SigInfo.VerifyingKey != key { 227 return fmt.Errorf("Last modifier verifying key %v != %v", 228 rmds.SigInfo.VerifyingKey, key) 229 } 230 231 writer := rmds.MD.LastModifyingWriter() 232 if !rmds.MD.IsWriterMetadataCopiedSet() { 233 if writer != uid { 234 return fmt.Errorf("Last writer %s != %s", writer, uid) 235 } 236 if rmds.WriterSigInfo.VerifyingKey != key { 237 return fmt.Errorf( 238 "Last writer verifying key %v != %v", 239 rmds.WriterSigInfo.VerifyingKey, key) 240 } 241 } 242 243 return nil 244 } 245 246 // EncodeRootMetadataSigned serializes a metadata block. This should 247 // be used instead of directly calling codec.Encode(), as it handles 248 // some version-specific quirks. 249 func EncodeRootMetadataSigned( 250 codec kbfscodec.Codec, rmds *RootMetadataSigned) ([]byte, error) { 251 err := checkWriterSig(rmds) 252 if err != nil { 253 return nil, err 254 } 255 rmdsCopy := *rmds 256 if rmdsCopy.Version() < SegregatedKeyBundlesVer { 257 // For v2, the writer signature is in rmds.MD, so 258 // remove the one in rmds. 259 rmdsCopy.WriterSigInfo = kbfscrypto.SignatureInfo{} 260 } 261 return codec.Encode(rmdsCopy) 262 } 263 264 // DecodeRootMetadataSigned deserializes a metadata block into the 265 // specified versioned structure. 266 func DecodeRootMetadataSigned( 267 codec kbfscodec.Codec, tlf tlf.ID, ver, max MetadataVer, buf []byte) ( 268 *RootMetadataSigned, error) { 269 rmd, err := makeMutableRootMetadataForDecode(codec, tlf, ver, max, buf) 270 if err != nil { 271 return nil, err 272 } 273 rmds := RootMetadataSigned{ 274 MD: rmd, 275 } 276 if err := codec.Decode(buf, &rmds); err != nil { 277 return nil, err 278 } 279 if ver < SegregatedKeyBundlesVer { 280 // For v2, the writer signature is in rmds.MD, so copy 281 // it out. 282 if !rmds.WriterSigInfo.IsNil() { 283 return nil, fmt.Errorf( 284 "Decoded RootMetadataSigned with version "+ 285 "%d unexpectedly has non-nil "+ 286 "writer signature %s", 287 ver, rmds.WriterSigInfo) 288 } 289 mdv2 := rmds.MD.(*RootMetadataV2) 290 rmds.WriterSigInfo = mdv2.WriterMetadataSigInfo 291 } 292 return &rmds, nil 293 }