github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/gpg_index.go (about) 1 // Copyright 2015 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package libkb 5 6 import ( 7 "bufio" 8 "fmt" 9 "io" 10 "sort" 11 "strconv" 12 "strings" 13 "time" 14 15 keybase1 "github.com/keybase/client/go/protocol/keybase1" 16 "github.com/keybase/go-crypto/openpgp/packet" 17 ) 18 19 // ============================================================================= 20 21 type BucketDict struct { 22 d map[string][]*GpgPrimaryKey 23 } 24 25 func NewBuckDict() *BucketDict { 26 return &BucketDict{ 27 d: make(map[string][]*GpgPrimaryKey), 28 } 29 } 30 31 func (bd *BucketDict) Add(k string, v *GpgPrimaryKey) { 32 k = strings.ToLower(k) 33 bd.d[k] = append(bd.d[k], v) 34 } 35 36 func (bd BucketDict) Get(k string) []*GpgPrimaryKey { 37 k = strings.ToLower(k) 38 ret, found := bd.d[k] 39 if !found { 40 ret = nil 41 } 42 return ret 43 } 44 45 func (bd BucketDict) Get0Or1(k string) (ret *GpgPrimaryKey, err error) { 46 v := bd.Get(k) 47 if len(v) > 1 { 48 err = GpgError{fmt.Sprintf("Wanted a unique lookup but got %d objects for key %s", len(v), k)} 49 } else if len(v) == 1 { 50 ret = v[0] 51 } 52 return 53 } 54 55 // ============================================================================= 56 57 func Uniquify(inp []string) []string { 58 m := make(map[string]bool) 59 for _, s := range inp { 60 m[strings.ToLower(s)] = true 61 } 62 ret := make([]string, 0, len(m)) 63 for k := range m { 64 ret = append(ret, k) 65 } 66 return ret 67 } 68 69 // ============================================================================= 70 71 type GpgBaseKey struct { 72 Type string 73 Trust string 74 Bits int 75 Algo int 76 ID64 string 77 Created int64 78 Expires int64 79 fingerprint *PGPFingerprint 80 } 81 82 func (k GpgBaseKey) AlgoString() string { 83 switch packet.PublicKeyAlgorithm(k.Algo) { 84 case packet.PubKeyAlgoDSA: 85 return "D" 86 case packet.PubKeyAlgoRSA: 87 return "R" 88 case packet.PubKeyAlgoECDSA: 89 return "E" 90 default: 91 return "?" 92 } 93 } 94 95 func (k GpgBaseKey) ExpirationString() string { 96 if k.Expires == 0 { 97 return "never" 98 } 99 layout := "2006-01-02" 100 return time.Unix(k.Expires, 0).Format(layout) 101 } 102 103 func (k GpgBaseKey) CreatedString() string { 104 layout := "2006-01-02" 105 return time.Unix(k.Created, 0).Format(layout) 106 } 107 108 func (k *GpgBaseKey) ParseBase(line *GpgIndexLine) (err error) { 109 if line.Len() < 12 { 110 err = GpgIndexError{line.lineno, "Not enough fields (need 12)"} 111 return 112 } 113 114 k.Type = line.At(0) 115 k.Trust = line.At(1) 116 k.ID64 = line.At(4) 117 118 parseTimeStamp := func(s string) (ret int64, err error) { 119 // No date was specified 120 if len(s) == 0 { 121 return 122 } 123 // GPG 2.0+ format 124 if ret, err = strconv.ParseInt(s, 10, 0); err == nil { 125 return 126 } 127 var tmp time.Time 128 if tmp, err = time.Parse("2006-01-02", s); err != nil { 129 return 130 } 131 ret = tmp.Unix() 132 return 133 } 134 135 if k.Bits, err = strconv.Atoi(line.At(2)); err != nil { 136 return 137 } 138 if k.Algo, err = strconv.Atoi(line.At(3)); err != nil { 139 return 140 } 141 if k.Created, err = parseTimeStamp(line.At(5)); err != nil { 142 return 143 } 144 if k.Expires, err = parseTimeStamp(line.At(6)); err != nil { 145 return 146 } 147 148 return 149 } 150 151 // ============================================================================= 152 153 type GpgFingerprinter interface { 154 SetFingerprint(pgp *PGPFingerprint) 155 } 156 157 type GpgPrimaryKey struct { 158 Contextified 159 GpgBaseKey 160 subkeys []*GpgSubKey 161 identities []*Identity 162 top GpgFingerprinter 163 } 164 165 func (k *GpgPrimaryKey) IsValid(mctx MetaContext) bool { 166 if k == nil { 167 return false 168 } 169 if k.Trust == "r" { 170 return false 171 } else if k.Expires == 0 { 172 return true 173 } 174 expiresAt := time.Unix(k.Expires, 0) 175 expired := time.Now().After(expiresAt) 176 if expired { 177 var fp string 178 if k.fingerprint != nil { 179 fp = " (" + k.fingerprint.ToQuads() + ")" 180 } 181 mctx.Warning("Skipping expired primary key%s, expiration: %s", fp, expiresAt) 182 } 183 return !expired 184 } 185 186 func (k *GpgPrimaryKey) ToRow(i int) []string { 187 v := []string{ 188 fmt.Sprintf("(%d)", i), 189 fmt.Sprintf("%d%s", k.Bits, k.AlgoString()), 190 k.fingerprint.ToKeyID(), 191 k.ExpirationString(), 192 } 193 for _, i := range k.identities { 194 v = append(v, i.Email) 195 } 196 return v 197 } 198 199 func (k *GpgBaseKey) SetFingerprint(pgp *PGPFingerprint) { 200 k.fingerprint = pgp 201 } 202 203 func (k *GpgPrimaryKey) Parse(l *GpgIndexLine) error { 204 if err := k.ParseBase(l); err != nil { 205 return err 206 } 207 return k.AddUID(l) 208 } 209 210 func NewGpgPrimaryKey(g *GlobalContext) *GpgPrimaryKey { 211 ret := &GpgPrimaryKey{Contextified: NewContextified(g)} 212 ret.top = ret 213 return ret 214 } 215 216 func ParseGpgPrimaryKey(g *GlobalContext, l *GpgIndexLine) (key *GpgPrimaryKey, err error) { 217 key = NewGpgPrimaryKey(g) 218 err = key.Parse(l) 219 return 220 } 221 222 func (k *GpgPrimaryKey) AddUID(l *GpgIndexLine) (err error) { 223 var id *Identity 224 if f := l.At(9); len(f) == 0 { 225 } else if id, err = ParseIdentity(f); err != nil { 226 } else if l.At(1) != "r" { // is not revoked 227 k.identities = append(k.identities, id) 228 } 229 if err != nil { 230 err = ErrorToGpgIndexError(l.lineno, err) 231 } 232 return 233 } 234 235 func (k *GpgPrimaryKey) AddFingerprint(l *GpgIndexLine) (err error) { 236 var fp *PGPFingerprint 237 if f := l.At(9); len(f) == 0 { 238 err = fmt.Errorf("no fingerprint given") 239 } else if fp, err = PGPFingerprintFromHex(f); err == nil { 240 k.top.SetFingerprint(fp) 241 } 242 if err != nil { 243 err = ErrorToGpgIndexError(l.lineno, err) 244 } 245 return 246 } 247 248 func (k *GpgPrimaryKey) GetFingerprint() *PGPFingerprint { 249 return k.fingerprint 250 } 251 252 func (k *GpgPrimaryKey) GetPGPIdentities() []keybase1.PGPIdentity { 253 ret := make([]keybase1.PGPIdentity, len(k.identities)) 254 for i, ident := range k.identities { 255 ret[i] = ident.Export() 256 } 257 return ret 258 } 259 260 func (k *GpgPrimaryKey) GetEmails() []string { 261 ret := make([]string, len(k.identities)) 262 for i, id := range k.identities { 263 ret[i] = id.Email 264 } 265 return ret 266 } 267 268 func (k *GpgPrimaryKey) GetAllID64s() []string { 269 var ret []string 270 add := func(fp *PGPFingerprint) { 271 if fp != nil { 272 ret = append(ret, fp.ToKeyID()) 273 } 274 } 275 add(k.GetFingerprint()) 276 for _, sk := range k.subkeys { 277 add(sk.fingerprint) 278 } 279 return ret 280 } 281 282 func (k *GpgPrimaryKey) AddSubkey(l *GpgIndexLine) (err error) { 283 var sk *GpgSubKey 284 if sk, err = ParseGpgSubKey(l); err == nil { 285 k.subkeys = append(k.subkeys, sk) 286 k.top = sk 287 } 288 return 289 } 290 291 func (k *GpgPrimaryKey) ToKey() *GpgPrimaryKey { return k } 292 293 func (k *GpgPrimaryKey) AddLine(l *GpgIndexLine) (err error) { 294 if l.Len() < 2 { 295 err = GpgIndexError{l.lineno, "too few fields"} 296 } else { 297 f := l.At(0) 298 switch f { 299 case "fpr": 300 err = k.AddFingerprint(l) 301 case "uid": 302 err = k.AddUID(l) 303 case "uat", "grp": // ignore 304 case "sub", "ssb": 305 err = k.AddSubkey(l) 306 case "rvk": // designated revoker (ignore) 307 default: 308 err = GpgIndexError{l.lineno, fmt.Sprintf("Unknown subfield: %s", f)} 309 } 310 311 } 312 return err 313 } 314 315 // ============================================================================= 316 317 type GpgSubKey struct { 318 GpgBaseKey 319 } 320 321 func ParseGpgSubKey(l *GpgIndexLine) (sk *GpgSubKey, err error) { 322 sk = &GpgSubKey{} 323 err = sk.ParseBase(l) 324 return 325 } 326 327 // ============================================================================= 328 329 type GpgIndexElement interface { 330 ToKey() *GpgPrimaryKey 331 } 332 333 type GpgKeyIndex struct { 334 Keys []*GpgPrimaryKey 335 Emails, Fingerprints, ID64s *BucketDict 336 } 337 338 func (ki *GpgKeyIndex) Len() int { 339 return len(ki.Keys) 340 } 341 func (ki *GpgKeyIndex) Swap(i, j int) { 342 ki.Keys[i], ki.Keys[j] = ki.Keys[j], ki.Keys[i] 343 } 344 func (ki *GpgKeyIndex) Less(i, j int) bool { 345 a, b := ki.Keys[i], ki.Keys[j] 346 if len(a.identities) > len(b.identities) { 347 return true 348 } 349 if len(a.identities) < len(b.identities) { 350 return false 351 } 352 if a.Expires == 0 { 353 return true 354 } 355 if b.Expires == 0 { 356 return false 357 } 358 if a.Expires > b.Expires { 359 return true 360 } 361 return false 362 } 363 364 func (ki *GpgKeyIndex) GetRowFunc() func() []string { 365 i := 0 366 return func() []string { 367 if i >= len(ki.Keys) { 368 return nil 369 } 370 ret := ki.Keys[i].ToRow(i + 1) 371 i++ 372 return ret 373 } 374 } 375 376 func (ki *GpgKeyIndex) Sort() { 377 sort.Sort(ki) 378 } 379 380 func NewGpgKeyIndex() *GpgKeyIndex { 381 return &GpgKeyIndex{ 382 Emails: NewBuckDict(), 383 Fingerprints: NewBuckDict(), 384 ID64s: NewBuckDict(), 385 } 386 } 387 388 func (ki *GpgKeyIndex) IndexKey(k *GpgPrimaryKey) { 389 ki.Keys = append(ki.Keys, k) 390 if fp := k.GetFingerprint(); fp != nil { 391 ki.Fingerprints.Add(fp.String(), k) 392 } 393 for _, e := range Uniquify(k.GetEmails()) { 394 ki.Emails.Add(e, k) 395 } 396 for _, i := range Uniquify(k.GetAllID64s()) { 397 ki.ID64s.Add(i, k) 398 } 399 } 400 401 func (ki *GpgKeyIndex) PushElement(mctx MetaContext, e GpgIndexElement) { 402 if key := e.ToKey(); key.IsValid(mctx) { 403 ki.IndexKey(key) 404 } 405 } 406 407 func (ki *GpgKeyIndex) AllFingerprints() []PGPFingerprint { 408 var ret []PGPFingerprint 409 for _, k := range ki.Keys { 410 if fp := k.GetFingerprint(); fp != nil { 411 ret = append(ret, *fp) 412 } 413 } 414 return ret 415 } 416 417 // ============================================================================= 418 419 type GpgIndexLine struct { 420 v []string 421 lineno int 422 } 423 424 func (g GpgIndexLine) Len() int { return len(g.v) } 425 func (g GpgIndexLine) At(i int) string { return g.v[i] } 426 427 func ParseLine(s string, i int) (ret *GpgIndexLine, err error) { 428 s = strings.TrimSpace(s) 429 v := strings.Split(s, ":") 430 if v == nil { 431 err = GpgError{fmt.Sprintf("%d: Bad line; split failed", i)} 432 } else { 433 ret = &GpgIndexLine{v, i} 434 } 435 return 436 } 437 438 func (g GpgIndexLine) IsNewKey() bool { 439 return len(g.v) > 0 && (g.v[0] == "sec" || g.v[0] == "pub") 440 } 441 442 // ============================================================================= 443 444 type GpgIndexParser struct { 445 Contextified 446 warnings Warnings 447 putback *GpgIndexLine 448 src *bufio.Reader 449 eof bool 450 lineno int 451 } 452 453 func NewGpgIndexParser(g *GlobalContext) *GpgIndexParser { 454 return &GpgIndexParser{ 455 Contextified: NewContextified(g), 456 eof: false, 457 lineno: 0, 458 putback: nil, 459 } 460 } 461 462 func (p *GpgIndexParser) Warn(w Warning) { 463 p.warnings.Push(w) 464 } 465 466 func (p *GpgIndexParser) ParseElement() (ret GpgIndexElement, err error) { 467 var line *GpgIndexLine 468 line, err = p.GetLine() 469 if err != nil || line == nil { 470 } else if line.IsNewKey() { 471 ret, err = p.ParseKey(line) 472 } 473 return 474 } 475 476 func (p *GpgIndexParser) ParseKey(l *GpgIndexLine) (ret *GpgPrimaryKey, err error) { 477 var line *GpgIndexLine 478 ret, err = ParseGpgPrimaryKey(p.G(), l) 479 done := false 480 for !done && err == nil && !p.isEOF() { 481 if line, err = p.GetLine(); line == nil || err != nil { 482 } else if line.IsNewKey() { 483 p.PutbackLine(line) 484 done = true 485 } else if e2 := ret.AddLine(line); e2 == nil { 486 } else { 487 p.warnings.Push(ErrorToWarning(e2)) 488 } 489 } 490 return 491 } 492 493 func (p *GpgIndexParser) GetLine() (ret *GpgIndexLine, err error) { 494 if p.putback != nil { 495 ret = p.putback 496 p.putback = nil 497 return 498 } 499 500 if p.isEOF() { 501 return 502 } 503 504 s, e2 := p.src.ReadString(byte('\n')) 505 if e2 == io.EOF { 506 p.eof = true 507 return 508 } 509 if e2 != nil { 510 return nil, e2 511 } 512 513 p.lineno++ 514 return ParseLine(s, p.lineno) 515 } 516 517 func (p *GpgIndexParser) PutbackLine(line *GpgIndexLine) { 518 p.putback = line 519 } 520 521 func (p GpgIndexParser) isEOF() bool { return p.eof } 522 523 func (p *GpgIndexParser) Parse(mctx MetaContext, stream io.Reader) (ki *GpgKeyIndex, err error) { 524 p.src = bufio.NewReader(stream) 525 ki = NewGpgKeyIndex() 526 for err == nil && !p.isEOF() { 527 var el GpgIndexElement 528 if el, err = p.ParseElement(); err == nil && el != nil { 529 ki.PushElement(mctx, el) 530 } 531 } 532 ki.Sort() 533 return 534 } 535 536 // ============================================================================= 537 538 func ParseGpgIndexStream(mctx MetaContext, stream io.Reader) (ki *GpgKeyIndex, w Warnings, err error) { 539 eng := NewGpgIndexParser(mctx.G()) 540 ki, err = eng.Parse(mctx, stream) 541 w = eng.warnings 542 return 543 } 544 545 // ============================================================================= 546 547 func (g *GpgCLI) Index(mctx MetaContext, secret bool, query string) (ki *GpgKeyIndex, w Warnings, err error) { 548 var k string 549 if secret { 550 k = "-K" 551 } else { 552 k = "-k" 553 } 554 args := []string{"--with-colons", "--fingerprint", k} 555 if len(query) > 0 { 556 args = append(args, query) 557 } 558 garg := RunGpg2Arg{ 559 Arguments: args, 560 Stdout: true, 561 } 562 res := g.Run2(mctx, garg) 563 if res.Err != nil { 564 err = res.Err 565 return 566 } 567 if ki, w, err = ParseGpgIndexStream(mctx, res.Stdout); err != nil { 568 return 569 } 570 err = res.Wait() 571 return 572 } 573 574 // =============================================================================