github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsblock/context.go (about) 1 // Copyright 2016 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 kbfsblock 6 7 import ( 8 "encoding/hex" 9 "fmt" 10 11 "github.com/keybase/client/go/kbfs/kbfscrypto" 12 "github.com/keybase/client/go/protocol/keybase1" 13 ) 14 15 // RefNonce is a 64-bit unique sequence of bytes for identifying this 16 // reference of a block ID from other references to the same 17 // (duplicated) block. 18 type RefNonce [8]byte 19 20 // ZeroRefNonce is a special BlockRefNonce used for the initial 21 // reference to a block. 22 var ZeroRefNonce = RefNonce([8]byte{0, 0, 0, 0, 0, 0, 0, 0}) 23 24 func (nonce RefNonce) String() string { 25 return hex.EncodeToString(nonce[:]) 26 } 27 28 // MakeRefNonce generates a non-zero block reference nonce using a 29 // CSPRNG. This is used for distinguishing different references to the 30 // same ID. 31 func MakeRefNonce() (RefNonce, error) { 32 var nonce RefNonce 33 for nonce == ZeroRefNonce { 34 err := kbfscrypto.RandRead(nonce[:]) 35 if err != nil { 36 return ZeroRefNonce, err 37 } 38 } 39 return nonce, nil 40 } 41 42 // Context contains all the information used by the server to identify 43 // blocks (other than the ID). 44 // 45 // NOTE: Don't add or modify anything in this struct without 46 // considering how old clients will handle them. 47 type Context struct { 48 // Creator is the UID that was first charged for the initial 49 // reference to this block. 50 Creator keybase1.UserOrTeamID `codec:"c"` 51 // Writer is the UID that should be charged for this reference to 52 // the block. If empty, it defaults to Creator. 53 Writer keybase1.UserOrTeamID `codec:"w,omitempty" json:",omitempty"` 54 // When RefNonce is all 0s, this is the initial reference to a 55 // particular block. Using a constant refnonce for the initial 56 // reference allows the server to identify and optimize for the 57 // common case where there is only one reference for a block. Two 58 // initial references cannot happen simultaneously, because the 59 // encrypted block contents (and thus the block ID) will be 60 // randomized by the server-side block crypt key half. All 61 // subsequent references to the same block must have a random 62 // RefNonce (it can't be a monotonically increasing number because 63 // that would require coordination among clients). 64 RefNonce RefNonce `codec:"r,omitempty"` 65 // BlockType indicates the type of the block (data 66 // vs. metadata). This is used, for example, when deciding how the 67 // block affects quotas. 68 BlockType keybase1.BlockType `codec:"b,omitempty"` 69 } 70 71 // MakeFirstContext makes the initial context for a block with the 72 // given creator. 73 func MakeFirstContext( 74 creator keybase1.UserOrTeamID, bType keybase1.BlockType) Context { 75 return Context{Creator: creator, BlockType: bType} 76 } 77 78 // MakeContext makes a context with the given creator, writer, and 79 // nonce, where the writer is not necessarily equal to the creator, 80 // and the nonce is usually non-zero. 81 func MakeContext( 82 creator keybase1.UserOrTeamID, writer keybase1.UserOrTeamID, nonce RefNonce, 83 bType keybase1.BlockType) Context { 84 return Context{ 85 Creator: creator, 86 Writer: writer, 87 RefNonce: nonce, 88 BlockType: bType, 89 } 90 } 91 92 // GetCreator returns the creator of the associated block. 93 func (c Context) GetCreator() keybase1.UserOrTeamID { 94 return c.Creator 95 } 96 97 // GetWriter returns the writer of the associated block. 98 func (c Context) GetWriter() keybase1.UserOrTeamID { 99 if !c.Writer.IsNil() { 100 return c.Writer 101 } 102 return c.Creator 103 } 104 105 // SetWriter sets the Writer field, if necessary. 106 func (c *Context) SetWriter(newWriter keybase1.UserOrTeamID) { 107 if c.Creator != newWriter { 108 c.Writer = newWriter 109 } else { 110 // save some bytes by not populating the separate Writer 111 // field if it matches the creator. 112 c.Writer = "" 113 } 114 } 115 116 // GetRefNonce returns the ref nonce of the associated block. 117 func (c Context) GetRefNonce() RefNonce { 118 return c.RefNonce 119 } 120 121 // GetBlockType returns the block type of the associated block. 122 func (c Context) GetBlockType() keybase1.BlockType { 123 return c.BlockType 124 } 125 126 // IsFirstRef returns whether or not p represents the first reference 127 // to the corresponding ID. 128 func (c Context) IsFirstRef() bool { 129 return c.RefNonce == ZeroRefNonce 130 } 131 132 func (c Context) String() string { 133 if c == (Context{}) { 134 return "Context{}" 135 } 136 s := fmt.Sprintf("Context{Creator: %s", c.Creator) 137 if len(c.Writer) > 0 { 138 s += fmt.Sprintf(", Writer: %s", c.Writer) 139 } 140 if c.RefNonce != ZeroRefNonce { 141 s += fmt.Sprintf(", RefNonce: %s", c.RefNonce) 142 } 143 if c.BlockType != keybase1.BlockType_DATA { 144 s += ", BlockType: " + c.BlockType.String() 145 } 146 s += "}" 147 return s 148 } 149 150 // ContextMap is a map from a block ID to a list of its contexts. 151 type ContextMap map[ID][]Context