github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/libkb/skb_keyring.go (about) 1 package libkb 2 3 import ( 4 "encoding/base64" 5 "fmt" 6 "io" 7 "os" 8 "sync" 9 "time" 10 11 "github.com/keybase/client/go/kbcrypto" 12 keybase1 "github.com/keybase/client/go/protocol/keybase1" 13 "github.com/keybase/go-codec/codec" 14 context "golang.org/x/net/context" 15 ) 16 17 type SKBKeyringFile struct { 18 Contextified 19 sync.Mutex 20 username NormalizedUsername 21 filename string 22 Blocks []*SKB 23 fpIndex map[PGPFingerprint]*SKB 24 kidIndex map[keybase1.KID]*SKB 25 dirty bool 26 } 27 28 func NewSKBKeyringFile(g *GlobalContext, un NormalizedUsername) *SKBKeyringFile { 29 return &SKBKeyringFile{ 30 Contextified: NewContextified(g), 31 username: un, 32 filename: g.SKBFilenameForUser(un), 33 fpIndex: make(map[PGPFingerprint]*SKB), 34 kidIndex: make(map[keybase1.KID]*SKB), 35 dirty: false, 36 } 37 } 38 39 func (k *SKBKeyringFile) Load(ctx context.Context) (err error) { 40 k.Lock() 41 defer k.Unlock() 42 return k.loadLocked(ctx) 43 } 44 45 func (k *SKBKeyringFile) Username() NormalizedUsername { 46 return k.username 47 } 48 49 func (k *SKBKeyringFile) IsForUsername(un NormalizedUsername) bool { 50 return k.username.Eq(un) 51 } 52 53 func (k *SKBKeyringFile) MTime() (mtime time.Time, err error) { 54 k.Lock() 55 defer k.Unlock() 56 var fi os.FileInfo 57 fi, err = os.Stat(k.filename) 58 if err != nil { 59 return mtime, err 60 } 61 return fi.ModTime(), err 62 } 63 64 func (k *SKBKeyringFile) MarkDirty() { 65 k.Lock() 66 defer k.Unlock() 67 k.dirty = true 68 } 69 70 type skbPacket struct { 71 skb *SKB 72 } 73 74 // Okay to panic in Codec{Encode,Decode}Self, since the 75 // encoder/decoder catches panics and turns them back into errors. 76 77 func (s *skbPacket) CodecEncodeSelf(e *codec.Encoder) { 78 err := kbcrypto.EncodePacket(s.skb, e) 79 if err != nil { 80 panic(err) 81 } 82 } 83 84 func (s *skbPacket) CodecDecodeSelf(d *codec.Decoder) { 85 var skb SKB 86 err := kbcrypto.DecodePacket(d, &skb) 87 if err != nil { 88 panic(err) 89 } 90 s.skb = &skb 91 } 92 93 func encodeSKBPacketList(skbs []*SKB, w io.Writer) error { 94 ch := kbcrypto.CodecHandle() 95 encoder := codec.NewEncoder(w, ch) 96 97 packets := make([]skbPacket, len(skbs)) 98 for i := range skbs { 99 packets[i].skb = skbs[i] 100 } 101 102 return encoder.Encode(packets) 103 } 104 105 func decodeSKBPacketList(r io.Reader, g *GlobalContext) ([]*SKB, error) { 106 ch := kbcrypto.CodecHandle() 107 decoder := codec.NewDecoder(r, ch) 108 109 var packets []skbPacket 110 err := decoder.Decode(&packets) 111 if err != nil { 112 return nil, err 113 } 114 115 skbs := make([]*SKB, len(packets)) 116 for i, s := range packets { 117 s.skb.SetGlobalContext(g) 118 skbs[i] = s.skb 119 } 120 return skbs, nil 121 } 122 123 func (k *SKBKeyringFile) loadLocked(ctx context.Context) (err error) { 124 k.G().Log.CDebugf(ctx, "+ Loading SKB keyring: %s", k.filename) 125 126 file, err := os.OpenFile(k.filename, os.O_RDONLY, 0) 127 if err != nil { 128 if os.IsNotExist(err) { 129 k.G().Log.CDebugf(ctx, "| Keybase secret keyring doesn't exist: %s", k.filename) 130 } else { 131 k.G().Log.CWarningf(ctx, "Error opening %s: %s", k.filename, err) 132 133 MobilePermissionDeniedCheck(k.G(), err, fmt.Sprintf("skb keyring: %s", k.filename)) 134 } 135 return err 136 } 137 defer func() { 138 closeErr := file.Close() 139 if err == nil { 140 err = closeErr 141 } 142 }() 143 144 stream := base64.NewDecoder(base64.StdEncoding, file) 145 skbs, err := decodeSKBPacketList(stream, k.G()) 146 if err != nil { 147 return err 148 } 149 150 k.Blocks = skbs 151 152 k.G().Log.CDebugf(ctx, "- Loaded SKB keyring: %s -> %s", k.filename, ErrToOk(err)) 153 return nil 154 } 155 156 func (k *SKBKeyringFile) addToIndexLocked(g GenericKey, b *SKB) { 157 if g == nil { 158 return 159 } 160 if fp := GetPGPFingerprintFromGenericKey(g); fp != nil { 161 k.fpIndex[*fp] = b 162 } 163 k.kidIndex[g.GetKID()] = b 164 } 165 166 func (k *SKBKeyringFile) Index() (err error) { 167 k.Lock() 168 defer k.Unlock() 169 return k.indexLocked() 170 } 171 172 func (k *SKBKeyringFile) indexLocked() (err error) { 173 for _, b := range k.Blocks { 174 var key GenericKey 175 key, err = b.GetPubKey() 176 if err != nil { 177 return 178 } 179 // Last-writer wins! 180 k.addToIndexLocked(key, b) 181 } 182 k.G().Log.Debug("| Indexed %d secret keys", len(k.Blocks)) 183 return 184 } 185 186 func (k *SKBKeyringFile) SearchWithComputedKeyFamily(ckf *ComputedKeyFamily, ska SecretKeyArg) []*SKB { 187 k.Lock() 188 defer k.Unlock() 189 190 var kid keybase1.KID 191 k.G().Log.Debug("+ SKBKeyringFile.SearchWithComputedKeyFamily") 192 defer func() { 193 var res string 194 if kid.Exists() { 195 res = kid.String() 196 } else { 197 res = "<nil>" 198 } 199 k.G().Log.Debug("- SKBKeyringFile.SearchWithComputedKeyFamily -> %s\n", res) 200 }() 201 k.G().Log.Debug("| Searching %d possible blocks", len(k.Blocks)) 202 var blocks []*SKB 203 for i := len(k.Blocks) - 1; i >= 0; i-- { 204 k.G().Log.Debug("| trying key index# -> %d", i) 205 if key, err := k.Blocks[i].GetPubKey(); err == nil && key != nil { 206 kid = key.GetKID() 207 active := ckf.GetKeyRole(kid) 208 k.G().Log.Debug("| Checking KID: %s -> %d", kid, int(active)) 209 if !ska.KeyType.nonDeviceKeyMatches(key) { 210 k.G().Log.Debug("| Skipped, doesn't match type=%s", ska.KeyType) 211 } else if !KeyMatchesQuery(key, ska.KeyQuery, ska.ExactMatch) { 212 k.G().Log.Debug("| Skipped, doesn't match query=%s", ska.KeyQuery) 213 214 } else if active != DLGSibkey { 215 k.G().Log.Debug("| Skipped, active=%d", int(active)) 216 } else { 217 blocks = append(blocks, k.Blocks[i]) 218 } 219 } else { 220 k.G().Log.Debug("| failed --> %v", err) 221 } 222 } 223 return blocks 224 } 225 226 func (k *SKBKeyringFile) PushAndSave(skb *SKB) error { 227 k.Lock() 228 defer k.Unlock() 229 if err := k.pushLocked(skb); err != nil { 230 return err 231 } 232 return k.saveLocked() 233 } 234 235 func (k *SKBKeyringFile) HasPGPKeys() bool { 236 k.Lock() 237 defer k.Unlock() 238 return len(k.fpIndex) > 0 239 } 240 241 func (k *SKBKeyringFile) AllPGPBlocks() ([]*SKB, error) { 242 k.Lock() 243 defer k.Unlock() 244 var pgpBlocks []*SKB 245 for _, block := range k.Blocks { 246 k, err := block.GetPubKey() 247 if err != nil { 248 return nil, err 249 } 250 if fp := GetPGPFingerprintFromGenericKey(k); fp != nil { 251 pgpBlocks = append(pgpBlocks, block) 252 } 253 } 254 return pgpBlocks, nil 255 } 256 257 func (k *SKBKeyringFile) RemoveAllPGPBlocks() error { 258 k.Lock() 259 defer k.Unlock() 260 var blocks []*SKB 261 for _, block := range k.Blocks { 262 k, err := block.GetPubKey() 263 if err != nil { 264 return err 265 } 266 if fp := GetPGPFingerprintFromGenericKey(k); fp == nil { 267 blocks = append(blocks, block) 268 } 269 } 270 k.Blocks = blocks 271 k.fpIndex = make(map[PGPFingerprint]*SKB) 272 k.kidIndex = make(map[keybase1.KID]*SKB) 273 err := k.indexLocked() 274 if err != nil { 275 return err 276 } 277 k.dirty = true 278 279 return nil 280 } 281 282 func (k *SKBKeyringFile) Push(skb *SKB) error { 283 k.Lock() 284 defer k.Unlock() 285 return k.pushLocked(skb) 286 } 287 288 func (k *SKBKeyringFile) pushLocked(skb *SKB) error { 289 key, err := skb.GetPubKey() 290 if err != nil { 291 return fmt.Errorf("Failed to get pubkey: %s", err) 292 } 293 k.dirty = true 294 k.Blocks = append(k.Blocks, skb) 295 k.addToIndexLocked(key, skb) 296 return nil 297 } 298 299 func (k *SKBKeyringFile) Save() error { 300 k.Lock() 301 defer k.Unlock() 302 return k.saveLocked() 303 } 304 305 func (k *SKBKeyringFile) saveLocked() error { 306 if !k.dirty { 307 k.G().Log.Debug("SKBKeyringFile: saveLocked %s: not dirty, so skipping save", k.filename) 308 return nil 309 } 310 if err := MakeParentDirs(k.G().Log, k.filename); err != nil { 311 k.G().Log.Debug("SKBKeyringFile: saveLocked %s: failed to make parent dirs: %s", k.filename, err) 312 return err 313 } 314 k.G().Log.Debug("SKBKeyringFile: saveLocked %s: dirty, safe saving", k.filename) 315 if err := SafeWriteToFile(k.G().Log, k, 0); err != nil { 316 k.G().Log.Debug("SKBKeyringFile: saveLocked %s: SafeWriteToFile error: %s", k.filename, err) 317 return err 318 } 319 k.dirty = false 320 k.G().Log.Debug("SKBKeyringFile: saveLocked success for %s", k.filename) 321 return nil 322 } 323 324 func (k *SKBKeyringFile) LookupByFingerprint(fp PGPFingerprint) *SKB { 325 k.Lock() 326 defer k.Unlock() 327 ret, ok := k.fpIndex[fp] 328 if !ok { 329 ret = nil 330 } 331 return ret 332 } 333 334 // FindSecretKey will, given a list of KIDs, find the first one in the 335 // list that has a corresponding secret key in the keyring file. 336 func (k *SKBKeyringFile) FindSecretKey(kids []keybase1.KID) (ret *SKB) { 337 k.Lock() 338 defer k.Unlock() 339 for _, kid := range kids { 340 if ret = k.lookupByKidLocked(kid); ret != nil { 341 return 342 } 343 } 344 return 345 } 346 347 func (k *SKBKeyringFile) LookupByKid(kid keybase1.KID) *SKB { 348 k.Lock() 349 defer k.Unlock() 350 return k.lookupByKidLocked(kid) 351 } 352 353 func (k *SKBKeyringFile) lookupByKidLocked(kid keybase1.KID) *SKB { 354 ret, ok := k.kidIndex[kid] 355 if !ok { 356 ret = nil 357 } 358 return ret 359 } 360 361 func (k *SKBKeyringFile) LoadAndIndex(ctx context.Context) error { 362 k.Lock() 363 defer k.Unlock() 364 err := k.loadLocked(ctx) 365 if err == nil { 366 err = k.indexLocked() 367 } 368 return err 369 } 370 371 // GetFilename is only called from within Save(), so it's called 372 // with a lock. Needs to be called GetFilename() to meet the interface 373 // required by SafeSaveToFile 374 func (k *SKBKeyringFile) GetFilename() string { return k.filename } 375 376 // WriteTo is similar to GetFilename described just above in terms of 377 // locking discipline. 378 func (k *SKBKeyringFile) WriteTo(w io.Writer) (n int64, err error) { 379 k.G().Log.Debug("+ SKBKeyringFile WriteTo") 380 defer k.G().Log.Debug("- SKBKeyringFile WriteTo") 381 b64 := base64.NewEncoder(base64.StdEncoding, w) 382 defer func() { 383 // explicitly check for error on Close: 384 if closeErr := b64.Close(); closeErr != nil { 385 k.G().Log.Warning("SKBKeyringFile: WriteTo b64.Close() error: %s", closeErr) 386 if err == nil { 387 n = 0 388 err = closeErr 389 return 390 } 391 } 392 k.G().Log.Debug("SKBKeyringFile: b64 stream closed successfully") 393 }() 394 395 if err := encodeSKBPacketList(k.Blocks, b64); err != nil { 396 k.G().Log.Warning("Encoding problem: %s", err) 397 return 0, err 398 } 399 400 return 0, nil 401 } 402 403 func (k *SKBKeyringFile) Bug3964Repair(m MetaContext, lks *LKSec, dkm DeviceKeyMap) (ret *SKBKeyringFile, serverHalfSet *LKSecServerHalfSet, err error) { 404 defer m.Trace("SKBKeyringFile#Bug3964Repair", &err)() 405 406 var newBlocks []*SKB 407 var hitBug3964 bool 408 409 m.Debug("| # of blocks=%d", len(k.Blocks)) 410 411 for i, b := range k.Blocks { 412 413 if b.Priv.Data == nil { 414 m.Debug("| Null private data at block=%d", i) 415 newBlocks = append(newBlocks, b) 416 continue 417 } 418 419 if b.Priv.Encryption != LKSecVersion { 420 m.Debug("| Skipping non-LKSec encryption (%d) at block=%d", b.Priv.Encryption, i) 421 newBlocks = append(newBlocks, b) 422 continue 423 } 424 425 var decryption, reencryption []byte 426 var badMask LKSecServerHalf 427 decryption, badMask, err = lks.decryptForBug3964Repair(m, b.Priv.Data, dkm) 428 if err != nil { 429 m.Debug("| Decryption failed at block=%d; keeping as is (%s)", i, err) 430 newBlocks = append(newBlocks, b) 431 continue 432 } 433 434 if badMask.IsNil() { 435 m.Debug("| Nil badmask at block=%d", i) 436 newBlocks = append(newBlocks, b) 437 continue 438 } 439 440 hitBug3964 = true 441 m.Debug("| Hit bug 3964 at SKB block=%d", i) 442 if serverHalfSet == nil { 443 serverHalfSet = NewLKSecServerHalfSet() 444 } 445 serverHalfSet.Add(badMask) 446 447 reencryption, err = lks.Encrypt(m, decryption) 448 if err != nil { 449 m.Debug("| reencryption bug at block=%d", i) 450 return nil, nil, err 451 } 452 453 newSKB := &SKB{ 454 Contextified: NewContextified(m.G()), 455 Pub: b.Pub, 456 Type: b.Type, 457 Priv: SKBPriv{ 458 Encryption: b.Priv.Encryption, 459 PassphraseGeneration: b.Priv.PassphraseGeneration, 460 Data: reencryption, 461 }, 462 } 463 464 newBlocks = append(newBlocks, newSKB) 465 } 466 if !hitBug3964 { 467 return nil, nil, nil 468 } 469 470 ret = NewSKBKeyringFile(k.G(), k.username) 471 ret.dirty = true 472 ret.Blocks = newBlocks 473 474 err = ret.Index() 475 if err != nil { 476 return nil, nil, err 477 } 478 479 return ret, serverHalfSet, nil 480 }