github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/saltpackkeys/saltpack_pseudonym_resolvers.go (about) 1 package saltpackkeys 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/protocol/keybase1" 9 "github.com/keybase/client/go/teams" 10 "github.com/keybase/go-framed-msgpack-rpc/rpc" 11 "github.com/keybase/saltpack" 12 ) 13 14 // KeyPseudonymResolver resolves new (team based) Key Pseudonyms, but falls back to old Kbfs Pseudonyms when it cannot find any match. 15 // A mock implementation (which does not communicate with the sever and avoids circular dependencies) is available in the saltpackkeysmocks package. 16 type KeyPseudonymResolver struct { 17 m libkb.MetaContext 18 } 19 20 var _ saltpack.SymmetricKeyResolver = (*KeyPseudonymResolver)(nil) 21 22 func NewKeyPseudonymResolver(m libkb.MetaContext) saltpack.SymmetricKeyResolver { 23 return &KeyPseudonymResolver{m: m} 24 } 25 26 func (r *KeyPseudonymResolver) ResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) { 27 keyPseudonyms := []libkb.KeyPseudonym{} 28 for _, identifier := range identifiers { 29 pseudonym := libkb.KeyPseudonym{} 30 if len(pseudonym) != len(identifier) { 31 return nil, fmt.Errorf("identifier is the wrong length for a key pseudonym (%d != %d)", len(pseudonym), len(identifier)) 32 } 33 copy(pseudonym[:], identifier) 34 keyPseudonyms = append(keyPseudonyms, pseudonym) 35 } 36 37 results, err := libkb.GetKeyPseudonyms(r.m, keyPseudonyms) 38 if err != nil { 39 return nil, err 40 } 41 42 success := false 43 44 symmetricKeys := []*saltpack.SymmetricKey{} 45 for _, result := range results { 46 if result.Err != nil || result.Info.Application != keybase1.TeamApplication_SALTPACK { 47 r.m.Debug("skipping unresolved pseudonym: %s", result.Err) 48 symmetricKeys = append(symmetricKeys, nil) 49 continue 50 } 51 r.m.Debug("resolved pseudonym for %s, fetching key", result.Info.ID) 52 symmetricKey, err := r.getSymmetricKey(result.Info.ID, result.Info.KeyGen) 53 if err != nil { 54 return nil, err 55 } 56 success = true 57 symmetricKeys = append(symmetricKeys, symmetricKey) 58 } 59 60 if success { 61 return symmetricKeys, nil 62 } 63 r.m.Debug("No pseudonyms resolved, fallback to old kbfs pseudonyms") 64 // Fallback to old kbfs pseudonyms 65 return r.kbfsResolveKeys(identifiers) 66 } 67 68 func (r *KeyPseudonymResolver) getSymmetricKey(id keybase1.UserOrTeamID, gen libkb.KeyGen) (*saltpack.SymmetricKey, error) { 69 // For now resolving key pseudonyms for users is not necessary, as keybase encrypt does not 70 // use symmetric per user encryption keys. 71 72 team, err := teams.Load(r.m.Ctx(), r.m.G(), keybase1.LoadTeamArg{ 73 ID: keybase1.TeamID(id), 74 }) 75 if err != nil { 76 return nil, err 77 } 78 79 var key keybase1.TeamApplicationKey 80 key, err = team.SaltpackEncryptionKeyAtGeneration(r.m.Ctx(), keybase1.PerTeamKeyGeneration(gen)) 81 if err != nil { 82 return nil, err 83 } 84 85 ssk := saltpack.SymmetricKey(key.Key) 86 return &ssk, nil 87 } 88 89 func (r *KeyPseudonymResolver) kbfsResolveKeys(identifiers [][]byte) ([]*saltpack.SymmetricKey, error) { 90 tlfPseudonyms := []libkb.TlfPseudonym{} 91 for _, identifier := range identifiers { 92 pseudonym := libkb.TlfPseudonym{} 93 if len(pseudonym) != len(identifier) { 94 return nil, fmt.Errorf("identifier is the wrong length for a TLF pseudonym (%d != %d)", len(pseudonym), len(identifier)) 95 } 96 copy(pseudonym[:], identifier) 97 tlfPseudonyms = append(tlfPseudonyms, pseudonym) 98 } 99 100 results, err := libkb.GetTlfPseudonyms(r.m.Ctx(), r.m.G(), tlfPseudonyms) 101 if err != nil { 102 return nil, err 103 } 104 105 symmetricKeys := []*saltpack.SymmetricKey{} 106 for _, result := range results { 107 if result.Err != nil { 108 r.m.Debug("skipping unresolved pseudonym: %s", result.Err) 109 symmetricKeys = append(symmetricKeys, nil) 110 continue 111 } 112 r.m.Debug("resolved pseudonym for %s, fetching key", result.Info.Name) 113 symmetricKey, err := r.kbfsGetSymmetricKey(r.m, *result.Info) 114 if err != nil { 115 return nil, err 116 } 117 symmetricKeys = append(symmetricKeys, symmetricKey) 118 } 119 return symmetricKeys, nil 120 } 121 122 func (r *KeyPseudonymResolver) getCryptKeys(m libkb.MetaContext, name string) (keybase1.GetTLFCryptKeysRes, error) { 123 xp := m.G().ConnectionManager.LookupByClientType(keybase1.ClientType_KBFS) 124 if xp == nil { 125 return keybase1.GetTLFCryptKeysRes{}, libkb.KBFSNotRunningError{} 126 } 127 cli := &keybase1.TlfKeysClient{ 128 Cli: rpc.NewClient(xp, libkb.NewContextifiedErrorUnwrapper(r.m.G()), libkb.LogTagsFromContext), 129 } 130 return cli.GetTLFCryptKeys(m.Ctx(), keybase1.TLFQuery{ 131 TlfName: name, 132 IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_CLI, 133 }) 134 } 135 136 func (r *KeyPseudonymResolver) kbfsGetSymmetricKey(m libkb.MetaContext, info libkb.TlfPseudonymServerInfo) (*saltpack.SymmetricKey, error) { 137 // NOTE: In order to handle finalized TLFs (which is one of the main 138 // benefits of using TLF keys to begin with, for forward readability), we 139 // need the server to tell us what the current, potentially-finalized name 140 // of the TLF is. If that's not the same as what the name was when the 141 // message was sent, we can't necessarily check that the server is being 142 // honest. That's ok insofar as we're not relying on these keys for 143 // authenticity, but it's a drag to not be able to use the pseudonym 144 // machinery. 145 146 // TODO: Check as much as we can, if the original TLF was fully resolved. 147 148 // Strip "/keybase/private/" from the name. 149 basename := strings.TrimPrefix(info.UntrustedCurrentName, "/keybase/private/") 150 if len(basename) >= len(info.UntrustedCurrentName) { 151 return nil, fmt.Errorf("unexpected prefix, expected '/keybase/private', found %q", info.UntrustedCurrentName) 152 } 153 res, err := r.getCryptKeys(m, basename) 154 if err != nil { 155 return nil, err 156 } 157 for _, key := range res.CryptKeys { 158 if libkb.KeyGen(key.KeyGeneration) == info.KeyGen { 159 // Success! 160 return (*saltpack.SymmetricKey)(&key.Key), nil 161 } 162 } 163 return nil, fmt.Errorf("no keys in TLF %q matched generation %d", basename, info.KeyGen) 164 }