github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/git/crypto.go (about) 1 package git 2 3 import ( 4 "fmt" 5 6 "golang.org/x/crypto/nacl/secretbox" 7 "golang.org/x/net/context" 8 9 "github.com/keybase/client/go/libkb" 10 "github.com/keybase/client/go/protocol/keybase1" 11 "github.com/keybase/client/go/teams" 12 ) 13 14 // publicCryptKey is a zero key used for public repos 15 var publicCryptKey keybase1.TeamApplicationKey 16 17 func init() { 18 var zero [libkb.NaclDHKeySecretSize]byte 19 publicCryptKey = keybase1.TeamApplicationKey{ 20 Application: keybase1.TeamApplication_GIT_METADATA, 21 KeyGeneration: 1, 22 Key: keybase1.Bytes32(zero), 23 } 24 } 25 26 // Crypto implements Cryptoer interface. 27 type Crypto struct { 28 libkb.Contextified 29 } 30 31 var _ Cryptoer = &Crypto{} 32 33 // NewCrypto returns a Crypto object. 34 func NewCrypto(g *libkb.GlobalContext) *Crypto { 35 return &Crypto{ 36 Contextified: libkb.NewContextified(g), 37 } 38 } 39 40 // Box encrypts the plaintext with the most current key for the given team. It yields a NaCl 41 // ciphertext and nonce, and also says which generation of the key it used. 42 func (c *Crypto) Box(ctx context.Context, plaintext []byte, teamSpec keybase1.TeamIDWithVisibility) (*keybase1.EncryptedGitMetadata, error) { 43 team, err := c.loadTeam(ctx, teamSpec, 0) 44 if err != nil { 45 return nil, err 46 } 47 48 public := teamSpec.Visibility == keybase1.TLFVisibility_PUBLIC 49 50 key := publicCryptKey 51 if !public { 52 key, err = team.GitMetadataKey(ctx) 53 if err != nil { 54 return nil, err 55 } 56 } 57 58 nonce, err := libkb.RandomNaclDHNonce() 59 if err != nil { 60 return nil, err 61 } 62 63 var encKey [libkb.NaclSecretBoxKeySize]byte = key.Key 64 sealed := secretbox.Seal(nil, plaintext, &nonce, &encKey) 65 66 return &keybase1.EncryptedGitMetadata{ 67 V: libkb.CurrentGitMetadataEncryptionVersion, 68 E: sealed, 69 N: nonce, 70 Gen: key.KeyGeneration, 71 }, nil 72 } 73 74 // Unbox decrypts the given ciphertext with the given nonce, for the given generation of the 75 // given team. Can return an error. Will return a non-nil plaintext on success. 76 func (c *Crypto) Unbox(ctx context.Context, teamSpec keybase1.TeamIDWithVisibility, metadata *keybase1.EncryptedGitMetadata) (plaintext []byte, err error) { 77 defer c.G().CTrace(ctx, fmt.Sprintf("git.Crypto#Unbox(%s, vis:%v)", teamSpec.TeamID, teamSpec.Visibility), &err)() 78 79 if metadata.V != 1 { 80 return nil, fmt.Errorf("invalid EncryptedGitMetadata version: %d", metadata.V) 81 } 82 83 public := teamSpec.Visibility == keybase1.TLFVisibility_PUBLIC 84 if public != teamSpec.TeamID.IsPublic() { 85 return nil, libkb.NewTeamVisibilityError(public, teamSpec.TeamID.IsPublic()) 86 } 87 88 key := publicCryptKey 89 if !public { 90 key, err = c.fastLoadKeyAtGeneration(ctx, teamSpec, metadata) 91 if err != nil { 92 return nil, err 93 } 94 } 95 96 var encKey [libkb.NaclSecretBoxKeySize]byte = key.Key 97 var naclNonce [libkb.NaclDHNonceSize]byte = metadata.N 98 99 plaintext, ok := secretbox.Open(nil, metadata.E, &naclNonce, &encKey) 100 if !ok { 101 return nil, libkb.DecryptOpenError{} 102 } 103 return plaintext, nil 104 } 105 106 func (c *Crypto) fastLoadKeyAtGeneration(ctx context.Context, teamSpec keybase1.TeamIDWithVisibility, metadata *keybase1.EncryptedGitMetadata) (key keybase1.TeamApplicationKey, err error) { 107 teamID := teamSpec.TeamID 108 arg := keybase1.FastTeamLoadArg{ 109 ID: teamID, 110 Public: false, 111 Applications: []keybase1.TeamApplication{keybase1.TeamApplication_GIT_METADATA}, 112 KeyGenerationsNeeded: []keybase1.PerTeamKeyGeneration{metadata.Gen}, 113 } 114 mctx := libkb.NewMetaContext(ctx, c.G()) 115 res, err := mctx.G().GetFastTeamLoader().Load(mctx, arg) 116 if err != nil { 117 return key, err 118 } 119 n := len(res.ApplicationKeys) 120 if n != 1 { 121 return key, fmt.Errorf("wrong number of keys back from FTL; wanted 1 but got %d", n) 122 } 123 if metadata.Gen > 0 && res.ApplicationKeys[0].KeyGeneration != metadata.Gen { 124 return key, fmt.Errorf("wrong generation back from FTL; wanted %d but got %d", metadata.Gen, res.ApplicationKeys[0].KeyGeneration) 125 } 126 127 if res.ApplicationKeys[0].Application != keybase1.TeamApplication_GIT_METADATA { 128 return key, fmt.Errorf("wrong application; wanted %d but got %d", keybase1.TeamApplication_GIT_METADATA, res.ApplicationKeys[0].Application) 129 } 130 return res.ApplicationKeys[0], nil 131 } 132 133 func (c *Crypto) loadTeam(ctx context.Context, teamSpec keybase1.TeamIDWithVisibility, needKeyGeneration keybase1.PerTeamKeyGeneration) (*teams.Team, error) { 134 public := teamSpec.Visibility == keybase1.TLFVisibility_PUBLIC 135 arg := keybase1.LoadTeamArg{ 136 ID: teamSpec.TeamID, 137 Public: public, 138 } 139 if needKeyGeneration != 0 { 140 arg.Refreshers.NeedApplicationsAtGenerations = map[keybase1.PerTeamKeyGeneration][]keybase1.TeamApplication{ 141 needKeyGeneration: {keybase1.TeamApplication_GIT_METADATA}, 142 } 143 } 144 team, err := teams.Load(ctx, c.G(), arg) 145 if err != nil { 146 return nil, err 147 } 148 149 return team, nil 150 }