github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/kbfsmd/key_bundle.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 kbfsmd
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/keybase/client/go/kbfs/kbfscrypto"
    11  	"github.com/keybase/client/go/protocol/keybase1"
    12  	"github.com/keybase/go-codec/codec"
    13  )
    14  
    15  // TLFCryptKeyInfo is a per-device key half entry in the
    16  // TLF{Writer,Reader}KeyBundleV{2,3}.
    17  type TLFCryptKeyInfo struct {
    18  	ClientHalf   kbfscrypto.EncryptedTLFCryptKeyClientHalf
    19  	ServerHalfID kbfscrypto.TLFCryptKeyServerHalfID
    20  	EPubKeyIndex int `codec:"i,omitempty"`
    21  
    22  	codec.UnknownFieldSetHandler
    23  }
    24  
    25  // DevicePublicKeys is a set of a user's devices (identified by the
    26  // corresponding device CryptPublicKey).
    27  type DevicePublicKeys map[kbfscrypto.CryptPublicKey]bool
    28  
    29  // Equals returns whether both sets of keys are equal.
    30  func (dpk DevicePublicKeys) Equals(other DevicePublicKeys) bool {
    31  	if len(dpk) != len(other) {
    32  		return false
    33  	}
    34  
    35  	for k := range dpk {
    36  		if !other[k] {
    37  			return false
    38  		}
    39  	}
    40  
    41  	return true
    42  }
    43  
    44  // UserDevicePublicKeys is a map from users to that user's set of devices.
    45  type UserDevicePublicKeys map[keybase1.UID]DevicePublicKeys
    46  
    47  // RemoveKeylessUsersForTest returns a new UserDevicePublicKeys objects with
    48  // all the users with an empty DevicePublicKeys removed.
    49  func (udpk UserDevicePublicKeys) RemoveKeylessUsersForTest() UserDevicePublicKeys {
    50  	udpkRemoved := make(UserDevicePublicKeys)
    51  	for u, dpk := range udpk {
    52  		if len(dpk) > 0 {
    53  			udpkRemoved[u] = dpk
    54  		}
    55  	}
    56  	return udpkRemoved
    57  }
    58  
    59  // Equals returns whether both sets of users are equal, and they all
    60  // have corresponding equal sets of keys.
    61  func (udpk UserDevicePublicKeys) Equals(other UserDevicePublicKeys) bool {
    62  	if len(udpk) != len(other) {
    63  		return false
    64  	}
    65  
    66  	for u, dpk := range udpk {
    67  		if !dpk.Equals(other[u]) {
    68  			return false
    69  		}
    70  	}
    71  
    72  	return true
    73  }
    74  
    75  // DeviceKeyServerHalves is a map from a user devices (identified by the
    76  // corresponding device CryptPublicKey) to corresponding key server
    77  // halves.
    78  type DeviceKeyServerHalves map[kbfscrypto.CryptPublicKey]kbfscrypto.TLFCryptKeyServerHalf
    79  
    80  // UserDeviceKeyServerHalves maps a user's keybase UID to their
    81  // DeviceServerHalves map.
    82  type UserDeviceKeyServerHalves map[keybase1.UID]DeviceKeyServerHalves
    83  
    84  // MergeUsers returns a UserDeviceKeyServerHalves that contains all
    85  // the users in serverHalves and other, which must be disjoint. This
    86  // isn't a deep copy.
    87  func (serverHalves UserDeviceKeyServerHalves) MergeUsers(
    88  	other UserDeviceKeyServerHalves) (UserDeviceKeyServerHalves, error) {
    89  	merged := make(UserDeviceKeyServerHalves,
    90  		len(serverHalves)+len(other))
    91  	for uid, deviceServerHalves := range serverHalves {
    92  		merged[uid] = deviceServerHalves
    93  	}
    94  	for uid, deviceServerHalves := range other {
    95  		if _, ok := merged[uid]; ok {
    96  			return nil, fmt.Errorf(
    97  				"user %s is in both UserDeviceKeyServerHalves",
    98  				uid)
    99  		}
   100  		merged[uid] = deviceServerHalves
   101  	}
   102  	return merged, nil
   103  }
   104  
   105  // splitTLFCryptKey splits the given TLFCryptKey into two parts -- the
   106  // client-side part (which is encrypted with the given keys), and the
   107  // server-side part, which will be uploaded to the server.
   108  func splitTLFCryptKey(uid keybase1.UID,
   109  	tlfCryptKey kbfscrypto.TLFCryptKey,
   110  	ePrivKey kbfscrypto.TLFEphemeralPrivateKey, ePubIndex int,
   111  	pubKey kbfscrypto.CryptPublicKey) (
   112  	TLFCryptKeyInfo, kbfscrypto.TLFCryptKeyServerHalf, error) {
   113  	//    * create a new random server half
   114  	//    * mask it with the key to get the client half
   115  	//    * encrypt the client half
   116  	var serverHalf kbfscrypto.TLFCryptKeyServerHalf
   117  	serverHalf, err := kbfscrypto.MakeRandomTLFCryptKeyServerHalf()
   118  	if err != nil {
   119  		return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err
   120  	}
   121  
   122  	clientHalf := kbfscrypto.MaskTLFCryptKey(serverHalf, tlfCryptKey)
   123  
   124  	var encryptedClientHalf kbfscrypto.EncryptedTLFCryptKeyClientHalf
   125  	encryptedClientHalf, err =
   126  		kbfscrypto.EncryptTLFCryptKeyClientHalf(ePrivKey, pubKey, clientHalf)
   127  	if err != nil {
   128  		return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err
   129  	}
   130  
   131  	var serverHalfID kbfscrypto.TLFCryptKeyServerHalfID
   132  	serverHalfID, err =
   133  		kbfscrypto.MakeTLFCryptKeyServerHalfID(uid, pubKey, serverHalf)
   134  	if err != nil {
   135  		return TLFCryptKeyInfo{}, kbfscrypto.TLFCryptKeyServerHalf{}, err
   136  	}
   137  
   138  	clientInfo := TLFCryptKeyInfo{
   139  		ClientHalf:   encryptedClientHalf,
   140  		ServerHalfID: serverHalfID,
   141  		EPubKeyIndex: ePubIndex,
   142  	}
   143  	return clientInfo, serverHalf, nil
   144  }
   145  
   146  // DeviceServerHalfRemovalInfo is a map from a device's crypt public
   147  // key to a list of server halves to remove.
   148  type DeviceServerHalfRemovalInfo map[kbfscrypto.CryptPublicKey][]kbfscrypto.TLFCryptKeyServerHalfID
   149  
   150  // UserServerHalfRemovalInfo contains a map from devices (identified
   151  // by its crypt public key) to a list of IDs for key server halves to
   152  // remove (one per key generation). For logging purposes, it also
   153  // contains a bool indicating whether all of the user's devices were
   154  // removed.
   155  type UserServerHalfRemovalInfo struct {
   156  	UserRemoved         bool
   157  	DeviceServerHalfIDs DeviceServerHalfRemovalInfo
   158  }
   159  
   160  // addGeneration merges the keys in genInfo (which must be one per
   161  // device) into ri. genInfo must have the same UserRemoved value and
   162  // keys as ri.
   163  func (ri UserServerHalfRemovalInfo) addGeneration(
   164  	uid keybase1.UID, genInfo UserServerHalfRemovalInfo) error {
   165  	if ri.UserRemoved != genInfo.UserRemoved {
   166  		return fmt.Errorf(
   167  			"UserRemoved=%t != generation UserRemoved=%t for user %s",
   168  			ri.UserRemoved, genInfo.UserRemoved, uid)
   169  	}
   170  
   171  	if len(ri.DeviceServerHalfIDs) != len(genInfo.DeviceServerHalfIDs) {
   172  		return fmt.Errorf(
   173  			"device count=%d != generation device count=%d for user %s",
   174  			len(ri.DeviceServerHalfIDs),
   175  			len(genInfo.DeviceServerHalfIDs), uid)
   176  	}
   177  
   178  	idCount := -1
   179  	for key, serverHalfIDs := range genInfo.DeviceServerHalfIDs {
   180  		if idCount == -1 {
   181  			idCount = len(ri.DeviceServerHalfIDs[key])
   182  		} else {
   183  			localIDCount := len(ri.DeviceServerHalfIDs[key])
   184  			if localIDCount != idCount {
   185  				return fmt.Errorf(
   186  					"expected %d keys, got %d for user %s and device %s",
   187  					idCount, localIDCount, uid, key)
   188  			}
   189  		}
   190  
   191  		if len(serverHalfIDs) != 1 {
   192  			return fmt.Errorf(
   193  				"expected exactly one key, got %d for user %s and device %s",
   194  				len(serverHalfIDs), uid, key)
   195  		}
   196  		if _, ok := ri.DeviceServerHalfIDs[key]; !ok {
   197  			return fmt.Errorf(
   198  				"no generation info for user %s and device %s",
   199  				uid, key)
   200  		}
   201  		ri.DeviceServerHalfIDs[key] = append(
   202  			ri.DeviceServerHalfIDs[key], serverHalfIDs[0])
   203  	}
   204  
   205  	return nil
   206  }
   207  
   208  // ServerHalfRemovalInfo is a map from users and devices to a list of
   209  // server half IDs to remove from the server.
   210  type ServerHalfRemovalInfo map[keybase1.UID]UserServerHalfRemovalInfo
   211  
   212  // AddGeneration merges the keys in genInfo (which must be one per
   213  // device) into info. genInfo must have the same users as info.
   214  func (info ServerHalfRemovalInfo) AddGeneration(
   215  	genInfo ServerHalfRemovalInfo) error {
   216  	if len(info) != len(genInfo) {
   217  		return fmt.Errorf(
   218  			"user count=%d != generation user count=%d",
   219  			len(info), len(genInfo))
   220  	}
   221  
   222  	for uid, removalInfo := range genInfo {
   223  		if _, ok := info[uid]; !ok {
   224  			return fmt.Errorf("no generation info for user %s", uid)
   225  		}
   226  		err := info[uid].addGeneration(uid, removalInfo)
   227  		if err != nil {
   228  			return err
   229  		}
   230  	}
   231  	return nil
   232  }
   233  
   234  // MergeUsers returns a ServerHalfRemovalInfo that contains all the
   235  // users in info and other, which must be disjoint. This isn't a deep
   236  // copy.
   237  func (info ServerHalfRemovalInfo) MergeUsers(
   238  	other ServerHalfRemovalInfo) (ServerHalfRemovalInfo, error) {
   239  	merged := make(ServerHalfRemovalInfo, len(info)+len(other))
   240  	for uid, removalInfo := range info {
   241  		merged[uid] = removalInfo
   242  	}
   243  	for uid, removalInfo := range other {
   244  		if _, ok := merged[uid]; ok {
   245  			return nil, fmt.Errorf(
   246  				"user %s is in both ServerHalfRemovalInfos",
   247  				uid)
   248  		}
   249  		merged[uid] = removalInfo
   250  	}
   251  	return merged, nil
   252  }