github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/teams/hidden/hidden.go (about) 1 package hidden 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 7 "github.com/keybase/client/go/libkb" 8 "github.com/keybase/client/go/merkletree2" 9 "github.com/keybase/client/go/protocol/keybase1" 10 "github.com/keybase/client/go/sig3" 11 ) 12 13 func importChain(mctx libkb.MetaContext, raw []sig3.ExportJSON) (ret []sig3.Generic, err error) { 14 ret = make([]sig3.Generic, 0, len(raw)) 15 for _, s := range raw { 16 g, err := s.Import() 17 if err != nil { 18 return nil, err 19 } 20 ret = append(ret, g) 21 } 22 return ret, nil 23 } 24 25 func populateLink(mctx libkb.MetaContext, ret *keybase1.HiddenTeamChain, link sig3.Generic) (err error) { 26 hsh, err := sig3.Hash(link) 27 if err != nil { 28 return err 29 } 30 q := link.Seqno() 31 ret.Outer[q] = hsh.Export() 32 if q > ret.Last { 33 ret.Last = q 34 } 35 if sig3.IsStubbed(link) { 36 return nil 37 } 38 rotateKey, ok := link.(*sig3.RotateKey) 39 if !ok { 40 return nil 41 } 42 rkex, err := rotateKey.Export() 43 if err != nil { 44 return err 45 } 46 ret.Inner[q] = *rkex 47 48 // Because this link isn't stubbed, we can bump the `LastFull` field 49 // forward if it's one more than previous. ret.LastFull will start at 0 50 // so this should work for the first link. 51 if ret.LastFull+1 == q { 52 ret.LastFull = q 53 } 54 55 // For each PTK (right now we really only expect one - the Reader PTK), 56 // update our maximum PTK generation 57 for _, ptk := range rotateKey.PTKs() { 58 max, ok := ret.LastPerTeamKeys[ptk.PTKType] 59 if !ok || max < q { 60 ret.LastPerTeamKeys[ptk.PTKType] = q 61 } 62 if ptk.PTKType == keybase1.PTKType_READER { 63 _, found := ret.ReaderPerTeamKeys[ptk.Generation] 64 if found { 65 return newRepeatPTKGenerationError(ptk.Generation, "clashes another hidden link") 66 } 67 ret.ReaderPerTeamKeys[ptk.Generation] = q 68 } 69 } 70 ret.MerkleRoots[q] = link.Inner().MerkleRoot.Export() 71 72 return nil 73 } 74 75 // GenerateKeyRotationParams are for generating a sig3 KeyRotation to store on the hidden team chain. 76 // Fill in all parameters of the struct (there were too many to pass as a list) 77 type GenerateKeyRotationParams struct { 78 TeamID keybase1.TeamID 79 IsPublic bool 80 IsImplicit bool 81 MerkleRoot *libkb.MerkleRoot 82 Me libkb.UserForSignatures 83 SigningKey libkb.GenericKey 84 MainPrev keybase1.LinkTriple 85 HiddenPrev *keybase1.LinkTriple 86 Gen keybase1.PerTeamKeyGeneration 87 NewSigningKey libkb.NaclSigningKeyPair 88 NewEncryptionKey libkb.NaclDHKeyPair 89 Check keybase1.PerTeamSeedCheck 90 Admin *sig3.ChainLocation 91 } 92 93 // GenerateKeyRotation generates and signs a new sig3 KeyRotation. The result can be passed to 94 // sig/multi.json and stored along with other sig1, sig2 or sig3 signatures in an atomic transaction. 95 func GenerateKeyRotation(mctx libkb.MetaContext, p GenerateKeyRotationParams) (ret *libkb.SigMultiItem, ratchets *keybase1.HiddenTeamChainRatchetSet, err error) { 96 97 s3, ratchets, err := generateKeyRotationSig3(mctx, p) 98 if err != nil { 99 return nil, nil, err 100 } 101 102 sigMultiItem := &libkb.SigMultiItem{ 103 SigningKID: p.SigningKey.GetKID(), 104 Type: "team.rotate_key_hidden", 105 SeqType: sig3.ChainTypeTeamPrivateHidden, 106 TeamID: p.TeamID, 107 Sig3: &libkb.Sig3{ 108 Inner: s3.Inner, 109 Outer: s3.Outer, 110 Sig: s3.Sig, 111 }, 112 PublicKeys: &libkb.SigMultiItemPublicKeys{ 113 Encryption: p.NewEncryptionKey.GetKID(), 114 Signing: p.NewSigningKey.GetKID(), 115 }, 116 Version: 3, 117 } 118 119 return sigMultiItem, ratchets, nil 120 } 121 122 func generateKeyRotationSig3(mctx libkb.MetaContext, p GenerateKeyRotationParams) (ret *sig3.ExportJSON, ratchets *keybase1.HiddenTeamChainRatchetSet, err error) { 123 124 outer := sig3.OuterLink{} 125 if p.HiddenPrev != nil { 126 outer.Seqno = p.HiddenPrev.Seqno + 1 127 if p.HiddenPrev.LinkID.IsNil() { 128 return nil, nil, NewGenerateError("unexpected nil prev") 129 } 130 tmp, err := sig3.ImportLinkID(p.HiddenPrev.LinkID) 131 if err != nil { 132 return nil, nil, err 133 } 134 outer.Prev = tmp 135 } else { 136 outer.Seqno = keybase1.Seqno(1) 137 } 138 tmp, err := sig3.ImportTail(p.MainPrev) 139 if err != nil { 140 return nil, nil, err 141 } 142 rsq := p.MerkleRoot.Seqno() 143 if rsq == nil { 144 return nil, nil, NewGenerateError("cannot work with a nil merkle root seqno") 145 } 146 teamIDimport, err := sig3.ImportTeamID(p.TeamID) 147 if err != nil { 148 return nil, nil, err 149 } 150 now := mctx.G().Clock().Now() 151 inner := sig3.InnerLink{ 152 Ctime: sig3.TimeSec(now.Unix()), 153 ClientInfo: &sig3.ClientInfo{ 154 Desc: libkb.GoClientID, 155 Version: libkb.Version, 156 }, 157 MerkleRoot: sig3.MerkleRoot{ 158 Ctime: sig3.TimeSec(p.MerkleRoot.Ctime()), 159 Hash: p.MerkleRoot.HashMeta(), 160 Seqno: *rsq, 161 }, 162 ParentChain: *tmp, 163 Signer: sig3.Signer{ 164 UID: sig3.ImportUID(p.Me.GetUID()), 165 KID: sig3.ImportKID(p.SigningKey.GetKID()), 166 EldestSeqno: p.Me.GetEldestSeqno(), 167 }, 168 Team: &sig3.Team{ 169 Admin: p.Admin, 170 TeamID: *teamIDimport, 171 IsPublic: p.IsPublic, 172 IsImplicit: p.IsImplicit, 173 }, 174 } 175 checkPostImage, err := p.Check.Hash() 176 if err != nil { 177 return nil, nil, err 178 } 179 rkb := sig3.RotateKeyBody{ 180 PTKs: []sig3.PerTeamKey{ 181 { 182 AppkeyDerivationVersion: sig3.AppkeyDerivationXOR, 183 Generation: p.Gen, 184 SeedCheck: *checkPostImage, 185 EncryptionKID: p.NewEncryptionKey.GetBinaryKID(), 186 SigningKID: p.NewSigningKey.GetBinaryKID(), 187 PTKType: keybase1.PTKType_READER, 188 }, 189 }, 190 } 191 192 keyPair := func(g libkb.GenericKey) (*sig3.KeyPair, error) { 193 signing, ok := g.(libkb.NaclSigningKeyPair) 194 if !ok { 195 return nil, NewGenerateError("bad key pair, wrong type: %T", g) 196 } 197 if signing.Private == nil { 198 return nil, NewGenerateError("bad key pair, got null private key") 199 } 200 return sig3.NewKeyPair(*signing.Private, g.GetBinaryKID()), nil 201 } 202 203 rk := sig3.NewRotateKey(outer, inner, rkb) 204 outerKeyPair, err := keyPair(p.SigningKey) 205 if err != nil { 206 return nil, nil, err 207 } 208 innerKeyPair, err := keyPair(p.NewSigningKey) 209 if err != nil { 210 return nil, nil, err 211 } 212 213 sig, err := rk.Sign(*outerKeyPair, []sig3.KeyPair{*innerKeyPair}) 214 if err != nil { 215 return nil, nil, err 216 } 217 bun, err := sig.Export() 218 if err != nil { 219 return nil, nil, err 220 } 221 outer = sig.Outer 222 outerHash, err := outer.Hash() 223 if err != nil { 224 return nil, nil, err 225 } 226 ratchets = &keybase1.HiddenTeamChainRatchetSet{} 227 ratchets.Add(keybase1.RatchetType_SELF, 228 keybase1.LinkTripleAndTime{ 229 Triple: keybase1.LinkTriple{ 230 Seqno: outer.Seqno, 231 SeqType: sig3.ChainTypeTeamPrivateHidden, 232 LinkID: outerHash.Export(), 233 }, 234 Time: keybase1.ToTime(now), 235 }, 236 ) 237 return &bun, ratchets, nil 238 } 239 240 func CheckFeatureGateForSupportWithRotationType(mctx libkb.MetaContext, teamID keybase1.TeamID, rt keybase1.RotationType) (ret keybase1.RotationType, err error) { 241 if rt == keybase1.RotationType_VISIBLE { 242 return rt, nil 243 } 244 ok, err := checkFeatureGateForSupport(mctx, teamID) 245 if err != nil { 246 return rt, err 247 } 248 249 switch { 250 case rt == keybase1.RotationType_CLKR && !ok: 251 return keybase1.RotationType_VISIBLE, nil 252 case rt == keybase1.RotationType_CLKR && ok: 253 return keybase1.RotationType_HIDDEN, nil 254 255 case rt == keybase1.RotationType_HIDDEN && ok: 256 return keybase1.RotationType_HIDDEN, nil 257 case rt == keybase1.RotationType_HIDDEN && !ok: 258 return keybase1.RotationType_HIDDEN, NewHiddenChainNotSupportedError(teamID) 259 260 default: 261 return keybase1.RotationType_HIDDEN, fmt.Errorf("unhandled case") 262 } 263 } 264 265 type rawSupport struct { 266 Status libkb.AppStatus `json:"status"` 267 Support bool `json:"support"` 268 } 269 270 func (r *rawSupport) GetAppStatus() *libkb.AppStatus { 271 return &r.Status 272 } 273 274 func featureGateForTeamFromServer(mctx libkb.MetaContext, teamID keybase1.TeamID) (ok bool, err error) { 275 arg := libkb.NewAPIArg("team/supports_hidden_chain") 276 arg.SessionType = libkb.APISessionTypeREQUIRED 277 arg.Args = libkb.HTTPArgs{ 278 "id": libkb.S{Val: string(teamID)}, 279 } 280 var raw rawSupport 281 err = mctx.G().API.GetDecode(mctx, arg, &raw) 282 if err != nil { 283 return false, err 284 } 285 return raw.Support, nil 286 } 287 288 func checkFeatureGateForSupport(mctx libkb.MetaContext, teamID keybase1.TeamID) (ok bool, err error) { 289 userFlagEnabled := mctx.G().FeatureFlags.Enabled(mctx, libkb.FeatureCheckForHiddenChainSupport) 290 runmode := mctx.G().Env.GetRunMode() 291 if runmode != libkb.ProductionRunMode { 292 return true, nil 293 } 294 if runmode == libkb.ProductionRunMode && !userFlagEnabled { 295 return false, nil 296 } 297 298 return mctx.G().GetHiddenTeamChainManager().TeamSupportsHiddenChain(mctx, teamID) 299 } 300 301 func CheckFeatureGateForSupport(mctx libkb.MetaContext, teamID keybase1.TeamID) (err error) { 302 ok, err := checkFeatureGateForSupport(mctx, teamID) 303 if err != nil { 304 return err 305 } 306 if !ok { 307 return NewHiddenChainNotSupportedError(teamID) 308 } 309 return nil 310 } 311 312 func ProcessHiddenResponseFunc(m libkb.MetaContext, teamID keybase1.TeamID, apiRes *libkb.APIRes, blindRootHashStr string) (hiddenResp *libkb.MerkleHiddenResponse, err error) { 313 if CheckFeatureGateForSupport(m, teamID) != nil { 314 m.Debug("Skipped ProcessHiddenResponseFunc as the feature flag is off (%v)", err) 315 return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeFLAGOFF}, nil 316 } 317 318 if blindRootHashStr == "" { 319 m.Debug("blind tree root not found in the main tree: %v", err) 320 // TODO: Y2K-770 Until the root of the blind tree starts getting 321 // included in the main tree, we can get such root from the server as an 322 // additional parameter and assume the server is honest. 323 blindRootHashStr, err = apiRes.Body.AtKey("last_blind_root_hash").GetString() 324 if err != nil { 325 return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeNONE}, nil 326 } 327 m.Debug("the server is providing a blind tree root which is not included in the main tree. We trust the server on this as the blind tree is an experimental feature.") 328 } 329 blindRootHashBytes, err := hex.DecodeString(blindRootHashStr) 330 if err != nil { 331 return nil, err 332 } 333 334 return ParseAndVerifyCommittedHiddenLinkID(m, teamID, apiRes, merkletree2.Hash(blindRootHashBytes)) 335 } 336 337 func ParseAndVerifyCommittedHiddenLinkID(m libkb.MetaContext, teamID keybase1.TeamID, apiRes *libkb.APIRes, blindHash merkletree2.Hash) (hiddenResp *libkb.MerkleHiddenResponse, err error) { 338 lastHiddenSeqnoInt, err := apiRes.Body.AtKey("last_hidden_seqno").GetInt() 339 if err != nil { 340 m.Debug("Error decoding last_hidden_seqno (%v), assuming the server did not send it.", err.Error()) 341 return &libkb.MerkleHiddenResponse{RespType: libkb.MerkleHiddenResponseTypeNONE}, nil 342 } 343 lastHiddenSeqno := keybase1.Seqno(lastHiddenSeqnoInt) 344 return makeHiddenRespFromTeamLeaf(m, lastHiddenSeqno) 345 } 346 347 func makeHiddenRespFromTeamLeaf(m libkb.MetaContext, lastHiddenSeqno keybase1.Seqno) (hiddenResp *libkb.MerkleHiddenResponse, err error) { 348 return &libkb.MerkleHiddenResponse{ 349 RespType: libkb.MerkleHiddenResponseTypeOK, 350 UncommittedSeqno: lastHiddenSeqno, 351 }, nil 352 }