github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/upak_lite.go (about) 1 package libkb 2 3 import ( 4 "fmt" 5 "io" 6 7 "github.com/buger/jsonparser" 8 "github.com/keybase/client/go/jsonparserw" 9 keybase1 "github.com/keybase/client/go/protocol/keybase1" 10 ) 11 12 func LoadUPAKLite(arg LoadUserArg) (ret *keybase1.UPKLiteV1AllIncarnations, err error) { 13 uid := arg.uid 14 m := arg.m 15 16 user, err := LoadUserFromServer(m, uid, nil) 17 if err != nil { 18 return nil, err 19 } 20 leaf, err := lookupMerkleLeaf(m, uid, false, nil, MerkleOpts{NoServerPolling: false}) 21 if err != nil { 22 return nil, err 23 } 24 loader := NewHighSigChainLoader(m, user, leaf) 25 highChain, err := loader.Load() 26 if err != nil { 27 return nil, err 28 } 29 return buildUPKLiteAllIncarnations(m, user, leaf, highChain) 30 } 31 32 type HighSigChainLoader struct { 33 MetaContextified 34 user *User 35 leaf *MerkleUserLeaf 36 chain *HighSigChain 37 chainType *ChainType 38 ckf ComputedKeyFamily 39 dirtyTail *MerkleTriple 40 } 41 42 type HighSigChain struct { 43 MetaContextified 44 uid keybase1.UID 45 username NormalizedUsername 46 chainLinks ChainLinks 47 // for a locally delegated key 48 localCki *ComputedKeyInfos 49 // For a user who has never done a reset, this is 1. 50 currentSubchainStart keybase1.Seqno 51 prevSubchains []ChainLinks 52 } 53 54 const UPKLiteMinorVersionCurrent = keybase1.UPKLiteMinorVersion_V0 55 56 func NewHighSigChainLoader(m MetaContext, user *User, leaf *MerkleUserLeaf) *HighSigChainLoader { 57 hsc := HighSigChain{ 58 MetaContextified: NewMetaContextified(m), 59 uid: user.GetUID(), 60 username: user.GetNormalizedName(), 61 } 62 loader := HighSigChainLoader{ 63 user: user, 64 leaf: leaf, 65 chain: &hsc, 66 chainType: PublicChain, 67 MetaContextified: NewMetaContextified(m), 68 } 69 loader.ckf.kf = user.GetKeyFamily() 70 return &loader 71 } 72 73 func (l *HighSigChainLoader) Load() (ret *HighSigChain, err error) { 74 // request new links (and the unverified tail) from the server 75 // and put them into the highSigChain 76 err = l.LoadFromServer() 77 if err != nil { 78 return nil, err 79 } 80 // verify the chain 81 err = l.chain.VerifyChain(l.M()) 82 if err != nil { 83 return nil, err 84 } 85 // compute keys 86 err = l.VerifySigsAndComputeKeys() 87 if err != nil { 88 return nil, err 89 } 90 return l.chain, nil 91 } 92 93 func (l *HighSigChainLoader) selfUID() (uid keybase1.UID) { 94 // for now let's always assume this isn't applicable, because there's 95 // no distinction for loading yourself 96 return uid 97 } 98 99 func (l *HighSigChainLoader) LoadFromServer() (err error) { 100 srv := l.GetMerkleTriple() 101 l.dirtyTail, err = l.chain.LoadFromServer(l.M(), srv, l.selfUID()) 102 return err 103 } 104 105 func (hsc *HighSigChain) LoadFromServer(m MetaContext, t *MerkleTriple, selfUID keybase1.UID) (dirtyTail *MerkleTriple, err error) { 106 // get the high sigs from the server 107 // ------------------ 108 m, tbs := m.WithTimeBuckets() 109 110 apiArg := APIArg{ 111 Endpoint: "sig/get_high", 112 SessionType: APISessionTypeOPTIONAL, 113 Args: HTTPArgs{ 114 "uid": S{Val: hsc.uid.String()}, 115 "c3": I{Val: int(sigCompression3Unstubbed)}, 116 }, 117 } 118 resp, finisher, err := m.G().API.GetResp(m, apiArg) 119 if err != nil { 120 return nil, err 121 } 122 defer finisher() 123 recordFin := tbs.Record("HighSigChain.LoadFromServer.ReadAll") 124 body, err := io.ReadAll(resp.Body) 125 if err != nil { 126 recordFin() 127 return nil, err 128 } 129 recordFin() 130 131 // parse the response 132 // ------------------ 133 if val, err := jsonparserw.GetInt(body, "status", "code"); err == nil { 134 if keybase1.StatusCode(val) == keybase1.StatusCode_SCDeleted { 135 return nil, UserDeletedError{} 136 } 137 } 138 var links ChainLinks 139 var lastLink *ChainLink 140 141 _, err = jsonparserw.ArrayEach(body, func(value []byte, dataType jsonparser.ValueType, offset int, inErr error) { 142 var link *ChainLink 143 144 parentSigChain := &SigChain{} // because we don't want the cache to use these 145 link, err = ImportLinkFromServer(m, parentSigChain, value, selfUID) 146 if err != nil { 147 m.Debug("Error importing link: %s", err.Error()) 148 } 149 links = append(links, link) 150 lastLink = link 151 }, "sigs") 152 if err != nil { 153 return nil, err 154 } 155 156 foundTail, err := lastLink.checkAgainstMerkleTree(t) 157 if err != nil { 158 return nil, err 159 } 160 if !foundTail { 161 err = fmt.Errorf("Last link is not the tail") 162 return nil, err 163 } 164 dirtyTail = lastLink.ToMerkleTriple() 165 166 hsc.chainLinks = links 167 return dirtyTail, err 168 } 169 170 func (hsc *HighSigChain) VerifyChain(m MetaContext) (err error) { 171 defer m.Trace("HighSigChain.VerifyChain", &err)() 172 173 for i := len(hsc.chainLinks) - 1; i >= 0; i-- { 174 curr := hsc.chainLinks[i] 175 m.VLogf(VLog1, "| verify high chain link %d (%s)", curr.GetSeqno(), curr.id) 176 if err = curr.VerifyLink(); err != nil { 177 return err 178 } 179 var expectedPrevID LinkID 180 var expectedPrevSeqno keybase1.Seqno 181 if curr.GetHighSkip() != nil { 182 expectedPrevSeqno = curr.GetHighSkip().Seqno 183 expectedPrevID = curr.GetHighSkip().Hash 184 } else { 185 // fallback to normal prevs if the link doesn't have a high_skip 186 expectedPrevSeqno = curr.GetSeqno() - 1 187 expectedPrevID = curr.GetPrev() 188 } 189 if i == 0 && (expectedPrevSeqno != 0 || expectedPrevID != nil) { 190 return ChainLinkPrevHashMismatchError{ 191 fmt.Sprintf("The first link should have 0,nil for it's expected previous. It had %d, %s", expectedPrevSeqno, expectedPrevID), 192 } 193 } 194 if i > 0 { 195 prev := hsc.chainLinks[i-1] 196 if !prev.id.Eq(expectedPrevID) { 197 return ChainLinkPrevHashMismatchError{fmt.Sprintf("Chain mismatch at seqno=%d", curr.GetSeqno())} 198 } 199 if prev.GetSeqno() != expectedPrevSeqno { 200 return ChainLinkWrongSeqnoError{fmt.Sprintf("Chain seqno mismatch at seqno=%d (previous=%d)", curr.GetSeqno(), prev.GetSeqno())} 201 } 202 } 203 if err = curr.CheckNameAndID(hsc.username, hsc.uid); err != nil { 204 return err 205 } 206 // this isn't being used for anything right now, but it might be useful 207 // if we ever want to do caching, especially as it can be distinguished 208 // from the other field, chainVerified 209 curr.highChainVerified = true 210 } 211 return nil 212 } 213 214 func (l *HighSigChainLoader) VerifySigsAndComputeKeys() (err error) { 215 _, err = l.chain.VerifySigsAndComputeKeys(l.M(), l.leaf.eldest, &l.ckf) 216 return err 217 } 218 219 func (hsc *HighSigChain) verifySigsAndComputeKeysCurrent(m MetaContext, eldest keybase1.KID, ckf *ComputedKeyFamily) (linksConsumed int, err error) { 220 // this is the case immediately after a reset 221 if ckf.kf == nil || eldest.IsNil() { 222 m.VLogf(VLog1, "UPAKLite short-circuit verifySigsAndComputeKeysCurrent, since no Key available") 223 hsc.localCki = NewComputedKeyInfos(hsc.G()) 224 ckf.cki = hsc.localCki 225 return 0, err 226 } 227 228 var links ChainLinks 229 links, err = cropToRightmostSubchain(m, hsc.chainLinks, eldest, hsc.uid) 230 if err != nil { 231 return 0, err 232 } 233 if len(links) == 0 { 234 // actually, not sure how this can happen without eldest being nil 235 m.VLogf(VLog1, "Empty chain after we limited to eldest %s", eldest) 236 hsc.localCki = NewComputedKeyInfos(hsc.G()) 237 ckf.cki = hsc.localCki 238 return 0, nil 239 } 240 241 hsc.currentSubchainStart = links[0].GetSeqno() 242 m.VLogf(VLog1, "UPAKLite verifying current chain starting at %d", int(hsc.currentSubchainStart)) 243 _, ckf.cki, err = verifySubchain(m, hsc.username, *ckf.kf, links) 244 return len(links), err 245 } 246 247 func (hsc *HighSigChain) VerifySigsAndComputeKeys(m MetaContext, eldest keybase1.KID, ckf *ComputedKeyFamily) (cached bool, err error) { 248 username := hsc.username 249 uid := hsc.uid 250 hsc.currentSubchainStart = 0 251 252 // current 253 linksConsumed, err := hsc.verifySigsAndComputeKeysCurrent(m, eldest, ckf) 254 if err != nil || ckf.kf == nil { 255 return false, err 256 } 257 258 // historical 259 historicalLinks := hsc.chainLinks.omittingNRightmostLinks(linksConsumed) 260 if len(historicalLinks) > 0 { 261 m.VLogf(VLog1, "After consuming %d links, there are %d historical links left", 262 linksConsumed, len(historicalLinks)) 263 // errors are ignored from this method in full sigchain loads also, because we'd rather 264 // not block current behavior from a failure in here. 265 _, prevSubchains, _ := verifySigsAndComputeKeysHistorical(m, uid, username, historicalLinks, *ckf.kf) 266 hsc.prevSubchains = prevSubchains 267 } 268 269 return false, nil 270 } 271 272 func (l *HighSigChainLoader) GetMerkleTriple() (ret *MerkleTriple) { 273 // leaf is what the server said was the leaf for the user 274 if l.leaf != nil { 275 ret = l.chainType.GetMerkleTriple(l.leaf) 276 } 277 return ret 278 } 279 280 func extractDeviceKeys(cki *ComputedKeyInfos) *map[keybase1.KID]keybase1.PublicKeyV2NaCl { 281 deviceKeys := make(map[keybase1.KID]keybase1.PublicKeyV2NaCl) 282 if cki != nil { 283 for kid := range cki.Infos { 284 if !KIDIsPGP(kid) { 285 deviceKeys[kid] = cki.exportDeviceKeyV2(kid) 286 } 287 } 288 } 289 return &deviceKeys 290 } 291 292 func buildUPKLiteAllIncarnations(m MetaContext, user *User, leaf *MerkleUserLeaf, hsc *HighSigChain) (ret *keybase1.UPKLiteV1AllIncarnations, err error) { 293 // build the current UPKLiteV1 294 uid := user.GetUID() 295 name := user.GetName() 296 status := user.GetStatus() 297 eldestSeqno := hsc.currentSubchainStart 298 deviceKeys := extractDeviceKeys(hsc.GetComputedKeyInfos()) 299 currentUpk := keybase1.UPKLiteV1{ 300 Uid: uid, 301 Username: name, 302 EldestSeqno: eldestSeqno, 303 Status: status, 304 DeviceKeys: *deviceKeys, 305 Reset: nil, 306 } 307 308 // build the historical (aka PastIncarnations) UPKLiteV1s 309 var previousUpks []keybase1.UPKLiteV1 310 resetMap := make(map[int](*keybase1.ResetSummary)) 311 if resets := leaf.resets; resets != nil { 312 for _, l := range resets.chain { 313 tmp := l.Summarize() 314 resetMap[int(l.ResetSeqno)] = &tmp 315 } 316 } 317 for idx, subchain := range hsc.prevSubchains { 318 latestLink := subchain[len(subchain)-1] 319 eldestSeqno = subchain[0].GetSeqno() 320 reset := resetMap[idx+1] 321 if reset != nil { 322 reset.EldestSeqno = eldestSeqno 323 } 324 prevDeviceKeys := extractDeviceKeys(latestLink.cki) 325 prevUpk := keybase1.UPKLiteV1{ 326 Uid: uid, 327 Username: name, 328 EldestSeqno: eldestSeqno, 329 Status: status, 330 DeviceKeys: *prevDeviceKeys, 331 Reset: reset, 332 } 333 previousUpks = append(previousUpks, prevUpk) 334 } 335 336 // Collect the link IDs (that is, the hashes of the signature inputs) from all chainlinks. 337 linkIDs := map[keybase1.Seqno]keybase1.LinkID{} 338 for _, link := range hsc.chainLinks { 339 linkIDs[link.GetSeqno()] = link.LinkID().Export() 340 } 341 342 final := keybase1.UPKLiteV1AllIncarnations{ 343 Current: currentUpk, 344 PastIncarnations: previousUpks, 345 SeqnoLinkIDs: linkIDs, 346 MinorVersion: UPKLiteMinorVersionCurrent, 347 } 348 ret = &final 349 return ret, err 350 } 351 352 func (hsc HighSigChain) GetComputedKeyInfos() (cki *ComputedKeyInfos) { 353 cki = hsc.localCki 354 if cki == nil { 355 if l := last(hsc.chainLinks); l != nil { 356 if l.cki == nil { 357 hsc.G().Log.Debug("GetComputedKeyInfos: l.cki is nil") 358 } 359 cki = l.cki 360 } 361 } 362 return cki 363 }