1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package libkb 5 6import ( 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 21type BucketDict struct { 22 d map[string][]*GpgPrimaryKey 23} 24 25func NewBuckDict() *BucketDict { 26 return &BucketDict{ 27 d: make(map[string][]*GpgPrimaryKey), 28 } 29} 30 31func (bd *BucketDict) Add(k string, v *GpgPrimaryKey) { 32 k = strings.ToLower(k) 33 bd.d[k] = append(bd.d[k], v) 34} 35 36func (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 45func (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 57func 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 71type 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 82func (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 95func (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 103func (k GpgBaseKey) CreatedString() string { 104 layout := "2006-01-02" 105 return time.Unix(k.Created, 0).Format(layout) 106} 107 108func (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 153type GpgFingerprinter interface { 154 SetFingerprint(pgp *PGPFingerprint) 155} 156 157type GpgPrimaryKey struct { 158 Contextified 159 GpgBaseKey 160 subkeys []*GpgSubKey 161 identities []*Identity 162 top GpgFingerprinter 163} 164 165func (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 } else { 174 expired := time.Now().After(time.Unix(k.Expires, 0)) 175 if expired { 176 var fp string 177 if k.fingerprint != nil { 178 fp = " (" + k.fingerprint.ToQuads() + ")" 179 } 180 mctx.Warning("Skipping expired primary key%s", fp) 181 } 182 return !expired 183 } 184} 185 186func (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 199func (k *GpgBaseKey) SetFingerprint(pgp *PGPFingerprint) { 200 k.fingerprint = pgp 201} 202 203func (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 210func NewGpgPrimaryKey(g *GlobalContext) *GpgPrimaryKey { 211 ret := &GpgPrimaryKey{Contextified: NewContextified(g)} 212 ret.top = ret 213 return ret 214} 215 216func ParseGpgPrimaryKey(g *GlobalContext, l *GpgIndexLine) (key *GpgPrimaryKey, err error) { 217 key = NewGpgPrimaryKey(g) 218 err = key.Parse(l) 219 return 220} 221 222func (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 235func (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 248func (k *GpgPrimaryKey) GetFingerprint() *PGPFingerprint { 249 return k.fingerprint 250} 251 252func (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 260func (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 268func (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 282func (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 291func (k *GpgPrimaryKey) ToKey() *GpgPrimaryKey { return k } 292 293func (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 317type GpgSubKey struct { 318 GpgBaseKey 319} 320 321func ParseGpgSubKey(l *GpgIndexLine) (sk *GpgSubKey, err error) { 322 sk = &GpgSubKey{} 323 err = sk.ParseBase(l) 324 return 325} 326 327//============================================================================= 328 329type GpgIndexElement interface { 330 ToKey() *GpgPrimaryKey 331} 332 333type GpgKeyIndex struct { 334 Keys []*GpgPrimaryKey 335 Emails, Fingerprints, ID64s *BucketDict 336} 337 338func (ki *GpgKeyIndex) Len() int { 339 return len(ki.Keys) 340} 341func (ki *GpgKeyIndex) Swap(i, j int) { 342 ki.Keys[i], ki.Keys[j] = ki.Keys[j], ki.Keys[i] 343} 344func (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 364func (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 376func (ki *GpgKeyIndex) Sort() { 377 sort.Sort(ki) 378} 379 380func NewGpgKeyIndex() *GpgKeyIndex { 381 return &GpgKeyIndex{ 382 Emails: NewBuckDict(), 383 Fingerprints: NewBuckDict(), 384 ID64s: NewBuckDict(), 385 } 386} 387 388func (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 401func (ki *GpgKeyIndex) PushElement(mctx MetaContext, e GpgIndexElement) { 402 if key := e.ToKey(); key.IsValid(mctx) { 403 ki.IndexKey(key) 404 } 405} 406 407func (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 419type GpgIndexLine struct { 420 v []string 421 lineno int 422} 423 424func (g GpgIndexLine) Len() int { return len(g.v) } 425func (g GpgIndexLine) At(i int) string { return g.v[i] } 426 427func 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 438func (g GpgIndexLine) IsNewKey() bool { 439 return len(g.v) > 0 && (g.v[0] == "sec" || g.v[0] == "pub") 440} 441 442//============================================================================= 443 444type GpgIndexParser struct { 445 Contextified 446 warnings Warnings 447 putback *GpgIndexLine 448 src *bufio.Reader 449 eof bool 450 lineno int 451} 452 453func NewGpgIndexParser(g *GlobalContext) *GpgIndexParser { 454 return &GpgIndexParser{ 455 Contextified: NewContextified(g), 456 eof: false, 457 lineno: 0, 458 putback: nil, 459 } 460} 461 462func (p *GpgIndexParser) Warn(w Warning) { 463 p.warnings.Push(w) 464} 465 466func (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 476func (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 493func (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 517func (p *GpgIndexParser) PutbackLine(line *GpgIndexLine) { 518 p.putback = line 519} 520 521func (p GpgIndexParser) isEOF() bool { return p.eof } 522 523func (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 538func 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 547func (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//============================================================================= 575