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