github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/assertion.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 "encoding/hex" 8 "fmt" 9 "regexp" 10 "sort" 11 "strconv" 12 "strings" 13 "time" 14 15 keybase1 "github.com/keybase/client/go/protocol/keybase1" 16 ) 17 18 type AssertionExpression interface { 19 String() string 20 MatchSet(ps ProofSet) bool 21 HasOr() bool 22 NeedsParens() bool 23 CollectUrls([]AssertionURL) []AssertionURL 24 ToSocialAssertion() (keybase1.SocialAssertion, error) 25 } 26 27 type AssertionOr struct { 28 symbol string // the divider symbol used e.g. "," or "||" 29 terms []AssertionExpression 30 } 31 32 func (a AssertionOr) HasOr() bool { return true } 33 34 func (a AssertionOr) MatchSet(ps ProofSet) bool { 35 for _, t := range a.terms { 36 if t.MatchSet(ps) { 37 return true 38 } 39 } 40 return false 41 } 42 43 func (a AssertionOr) NeedsParens() bool { 44 for _, t := range a.terms { 45 if t.NeedsParens() { 46 return true 47 } 48 } 49 return false 50 } 51 52 func (a AssertionOr) CollectUrls(v []AssertionURL) []AssertionURL { 53 for _, t := range a.terms { 54 v = t.CollectUrls(v) 55 } 56 return v 57 } 58 59 func (a AssertionOr) String() string { 60 v := make([]string, len(a.terms)) 61 for i, t := range a.terms { 62 v[i] = t.String() 63 } 64 return strings.Join(v, ",") 65 } 66 67 func (a AssertionOr) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 68 return sa, fmt.Errorf("cannot convert OR expression to single social assertion") 69 } 70 71 type AssertionAnd struct { 72 factors []AssertionExpression 73 } 74 75 func (a AssertionAnd) Len() int { 76 return len(a.factors) 77 } 78 79 func (a AssertionAnd) HasOr() bool { 80 for _, f := range a.factors { 81 if f.HasOr() { 82 return true 83 } 84 } 85 return false 86 } 87 88 func (a AssertionAnd) NeedsParens() bool { 89 for _, f := range a.factors { 90 if f.HasOr() { 91 return true 92 } 93 } 94 return false 95 } 96 97 func (a AssertionAnd) CollectUrls(v []AssertionURL) []AssertionURL { 98 for _, t := range a.factors { 99 v = t.CollectUrls(v) 100 } 101 return v 102 } 103 104 func (a AssertionAnd) MatchSet(ps ProofSet) bool { 105 for _, f := range a.factors { 106 if !f.MatchSet(ps) { 107 return false 108 } 109 } 110 return true 111 } 112 113 func (a AssertionAnd) HasFactor(pf Proof) bool { 114 ps := NewProofSet([]Proof{pf}) 115 for _, f := range a.factors { 116 if f.MatchSet(*ps) { 117 return true 118 } 119 } 120 return false 121 } 122 123 func (a AssertionAnd) String() string { 124 v := make([]string, len(a.factors)) 125 for i, f := range a.factors { 126 v[i] = f.String() 127 if _, ok := f.(AssertionOr); ok { 128 v[i] = "(" + v[i] + ")" 129 } 130 } 131 return strings.Join(v, "+") 132 } 133 134 func (a AssertionAnd) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 135 return sa, fmt.Errorf("cannot convert AND expression to single social assertion") 136 } 137 138 type AssertionURL interface { 139 AssertionExpression 140 Keys() []string 141 CheckAndNormalize(ctx AssertionContext) (AssertionURL, error) 142 IsKeybase() bool 143 IsUID() bool 144 IsTeamID() bool 145 IsTeamName() bool 146 IsSocial() bool 147 IsRemote() bool 148 IsFingerprint() bool 149 IsEmail() bool 150 MatchProof(p Proof) bool 151 ToUID() keybase1.UID 152 ToTeamID() keybase1.TeamID 153 ToTeamName() keybase1.TeamName 154 ToKeyValuePair() (string, string) 155 CacheKey() string 156 GetValue() string 157 GetKey() string 158 ToLookup() (string, string, error) 159 IsServerTrust() bool 160 } 161 162 type AssertionURLBase struct { 163 Key, Value string 164 } 165 166 func (b AssertionURLBase) ToKeyValuePair() (string, string) { 167 return b.Key, b.Value 168 } 169 func (b AssertionURLBase) GetKey() string { return b.Key } 170 171 func (b AssertionURLBase) CacheKey() string { 172 return b.Key + ":" + b.Value 173 } 174 175 func (b AssertionURLBase) GetValue() string { 176 return b.Value 177 } 178 179 func (b AssertionURLBase) matchSet(v AssertionURL, ps ProofSet) bool { 180 proofs := ps.Get(v.Keys()) 181 for _, proof := range proofs { 182 if v.MatchProof(proof) { 183 return true 184 } 185 } 186 return false 187 } 188 189 func (b AssertionURLBase) NeedsParens() bool { return false } 190 func (b AssertionURLBase) HasOr() bool { return false } 191 192 func (a AssertionUID) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 193 func (a AssertionTeamID) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 194 func (a AssertionTeamName) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 195 func (a AssertionKeybase) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 196 func (a AssertionWeb) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 197 func (a AssertionSocial) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 198 func (a AssertionHTTP) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 199 func (a AssertionHTTPS) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 200 func (a AssertionDNS) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 201 func (a AssertionFingerprint) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 202 func (a AssertionPhoneNumber) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 203 func (a AssertionEmail) MatchSet(ps ProofSet) bool { return a.matchSet(a, ps) } 204 func (a AssertionWeb) Keys() []string { 205 return []string{"dns", "http", "https"} 206 } 207 func (a AssertionHTTP) Keys() []string { return []string{"http", "https"} } 208 func (b AssertionURLBase) Keys() []string { return []string{b.Key} } 209 func (b AssertionURLBase) IsKeybase() bool { return false } 210 func (b AssertionURLBase) IsSocial() bool { return false } 211 func (b AssertionURLBase) IsRemote() bool { return false } 212 func (b AssertionURLBase) IsFingerprint() bool { return false } 213 func (b AssertionURLBase) IsUID() bool { return false } 214 func (b AssertionURLBase) IsEmail() bool { return b.Key == "email" } 215 func (b AssertionURLBase) ToUID() (ret keybase1.UID) { return ret } 216 func (b AssertionURLBase) IsTeamID() bool { return false } 217 func (b AssertionURLBase) IsTeamName() bool { return false } 218 func (b AssertionURLBase) ToTeamID() (ret keybase1.TeamID) { return ret } 219 func (b AssertionURLBase) ToTeamName() (ret keybase1.TeamName) { return ret } 220 func (b AssertionURLBase) MatchProof(proof Proof) bool { 221 return (strings.ToLower(proof.Value) == b.Value) 222 } 223 func (b AssertionURLBase) IsServerTrust() bool { return false } 224 225 func (b AssertionURLBase) ToSocialAssertionHelper() (sa keybase1.SocialAssertion, err error) { 226 return keybase1.SocialAssertion{ 227 User: b.GetValue(), 228 Service: keybase1.SocialAssertionService(b.GetKey()), 229 }, nil 230 } 231 func (a AssertionUID) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 232 return sa, fmt.Errorf("cannot convert AssertionUID to social assertion") 233 } 234 func (a AssertionTeamID) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 235 return sa, fmt.Errorf("cannot convert AssertionTeamID to social assertion") 236 } 237 func (a AssertionTeamName) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 238 return sa, fmt.Errorf("cannot convert AssertionTeamName to social assertion") 239 } 240 func (a AssertionKeybase) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 241 return sa, fmt.Errorf("cannot convert AssertionKeybase to social assertion") 242 } 243 func (a AssertionWeb) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 244 return a.ToSocialAssertionHelper() 245 } 246 func (a AssertionSocial) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 247 return a.ToSocialAssertionHelper() 248 } 249 func (a AssertionHTTP) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 250 return a.ToSocialAssertionHelper() 251 } 252 func (a AssertionHTTPS) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 253 return a.ToSocialAssertionHelper() 254 } 255 func (a AssertionDNS) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 256 return a.ToSocialAssertionHelper() 257 } 258 func (a AssertionFingerprint) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 259 return a.ToSocialAssertionHelper() 260 } 261 func (a AssertionPhoneNumber) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 262 // Phone number is not "social" like facebook or twitter, and there are no 263 // public prooofs, but it still conforms to keybase1.SocialAssertion type 264 // used in implicit team handling code. 265 return a.ToSocialAssertionHelper() 266 } 267 func (a AssertionEmail) ToSocialAssertion() (sa keybase1.SocialAssertion, err error) { 268 // Email have no public proofs, but can still be converted to 269 // keybase1.SocialAssertion, used in implicit team handling code. 270 return a.ToSocialAssertionHelper() 271 } 272 func (a AssertionEmail) String() string { 273 return fmt.Sprintf("[%s]@email", a.Value) 274 } 275 276 func (a AssertionSocial) GetValue() string { 277 return a.Value 278 } 279 280 // Fingerprint matching is on the suffixes. If the assertion matches 281 // any suffix of the proof, then we're OK 282 func (a AssertionFingerprint) MatchProof(proof Proof) bool { 283 v1, v2 := strings.ToLower(proof.Value), a.Value 284 l1, l2 := len(v1), len(v2) 285 if l2 > l1 { 286 return false 287 } 288 // Match the suffixes of the fingerprint 289 return (v1[(l1-l2):] == v2) 290 } 291 292 func (a AssertionUID) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 293 func (a AssertionTeamID) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 294 func (a AssertionTeamName) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 295 func (a AssertionKeybase) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 296 func (a AssertionWeb) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 297 func (a AssertionSocial) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 298 func (a AssertionHTTP) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 299 func (a AssertionHTTPS) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 300 func (a AssertionDNS) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 301 func (a AssertionFingerprint) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 302 func (a AssertionPhoneNumber) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 303 func (a AssertionPhoneNumber) IsServerTrust() bool { return true } 304 305 func (a AssertionEmail) CollectUrls(v []AssertionURL) []AssertionURL { return append(v, a) } 306 func (a AssertionEmail) IsServerTrust() bool { return true } 307 308 type AssertionSocial struct{ AssertionURLBase } 309 type AssertionWeb struct{ AssertionURLBase } 310 type AssertionKeybase struct{ AssertionURLBase } 311 type AssertionUID struct { 312 AssertionURLBase 313 uid keybase1.UID 314 } 315 type AssertionTeamID struct { 316 AssertionURLBase 317 tid keybase1.TeamID 318 } 319 type AssertionTeamName struct { 320 AssertionURLBase 321 name keybase1.TeamName 322 } 323 324 type AssertionHTTP struct{ AssertionURLBase } 325 type AssertionHTTPS struct{ AssertionURLBase } 326 type AssertionDNS struct{ AssertionURLBase } 327 type AssertionFingerprint struct{ AssertionURLBase } 328 type AssertionPhoneNumber struct{ AssertionURLBase } 329 type AssertionEmail struct{ AssertionURLBase } 330 331 func (a AssertionHTTP) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 332 if err := a.checkAndNormalizeHost(); err != nil { 333 return nil, err 334 } 335 return a, nil 336 } 337 func (a AssertionHTTPS) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 338 if err := a.checkAndNormalizeHost(); err != nil { 339 return nil, err 340 } 341 return a, nil 342 } 343 func (a AssertionDNS) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 344 if err := a.checkAndNormalizeHost(); err != nil { 345 return nil, err 346 } 347 return a, nil 348 } 349 func (a AssertionWeb) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 350 if err := a.checkAndNormalizeHost(); err != nil { 351 return nil, err 352 } 353 return a, nil 354 } 355 356 func (a AssertionKeybase) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 357 a.Value = strings.ToLower(a.Value) 358 if !CheckUsername.F(a.Value) { 359 return nil, NewAssertionCheckError("bad keybase username '%s': %s", a.Value, CheckUsername.Hint) 360 } 361 return a, nil 362 } 363 364 func (a AssertionFingerprint) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 365 a.Value = strings.ToLower(a.Value) 366 if _, err := hex.DecodeString(a.Value); err != nil { 367 return nil, NewAssertionCheckError("bad hex string: '%s'", a.Value) 368 } 369 return a, nil 370 } 371 372 func (b *AssertionURLBase) checkAndNormalizeHost() error { 373 374 if len(b.Value) == 0 { 375 return NewAssertionCheckError("Bad assertion, no value given (key=%s)", b.Key) 376 } 377 378 b.Value = strings.ToLower(b.Value) 379 380 if !IsValidHostname(b.Value) { 381 return NewAssertionCheckError("Invalid hostname: %s", b.Value) 382 } 383 384 return nil 385 } 386 387 func (b AssertionURLBase) String() string { 388 return fmt.Sprintf("%s@%s", b.Value, b.Key) 389 } 390 func (a AssertionKeybase) String() string { 391 return a.Value 392 } 393 394 func (a AssertionWeb) ToLookup() (key, value string, err error) { 395 return "web", a.Value, nil 396 } 397 func (a AssertionHTTP) ToLookup() (key, value string, err error) { 398 return "http", a.Value, nil 399 } 400 func (a AssertionHTTPS) ToLookup() (key, value string, err error) { 401 return "https", a.Value, nil 402 } 403 func (a AssertionDNS) ToLookup() (key, value string, err error) { 404 return "dns", a.Value, nil 405 } 406 func (a AssertionFingerprint) ToLookup() (key, value string, err error) { 407 cmp := len(a.Value) - PGPFingerprintHexLen 408 value = a.Value 409 if len(a.Value) < 4 { 410 err = fmt.Errorf("fingerprint queries must be at least 2 bytes long") 411 } else if cmp == 0 { 412 key = "key_fingerprint" 413 } else if cmp < 0 { 414 key = "key_suffix" 415 } else { 416 err = fmt.Errorf("bad fingerprint; too long: %s", a.Value) 417 } 418 return 419 } 420 421 var assertionBracketNameRxx = regexp.MustCompile(`^\[[-_a-zA-Z0-9.@+]+\]$`) 422 var assertionNameRxx = regexp.MustCompile(`^[-_a-zA-Z0-9.]+$`) 423 var assertionServiceRxx = regexp.MustCompile(`^[a-zA-Z.-]+$`) 424 425 func parseToKVPair(s string) (key string, value string, err error) { 426 // matchNameAndService runs regexp against potential name and service 427 // strings extracted from assertion. 428 matchNameAndService := func(name, service string) bool { 429 var k, v string // temp variables for key and value 430 if !assertionServiceRxx.MatchString(service) { 431 return false 432 } 433 434 // Normalize service name at parser level. 435 k = strings.ToLower(service) 436 437 if name == "" { 438 // We are fine with matching just the service. "dns:" is a valid 439 // assertion at parser level (but is rejected later in the 440 // process). 441 key = k 442 return true 443 } 444 445 var hasBrackets bool 446 if assertionNameRxx.MatchString(name) { 447 v = name 448 } else if assertionBracketNameRxx.MatchString(name) { 449 v = name[1 : len(name)-1] 450 hasBrackets = true 451 } else { 452 return false 453 } 454 455 // Set err in outer scope if find invalid square bracket syntax. 456 // Still return `true` because it's a successful match. 457 if k == "email" && !hasBrackets { 458 err = fmt.Errorf("expected bracket syntax for email assertion") 459 } else if k != "email" && hasBrackets { 460 err = fmt.Errorf("unexpected bracket syntax for assertion: %s", k) 461 } 462 463 // Finally pass back temp variables to outer scope. 464 key = k 465 value = v 466 return true 467 } 468 469 if atIndex := strings.LastIndex(s, "@"); atIndex != -1 { 470 name := s[:atIndex] 471 service := s[atIndex+1:] 472 473 if matchNameAndService(name, service) { 474 return key, value, err 475 } 476 } 477 478 if colIndex := strings.Index(s, ":"); colIndex != -1 { 479 service := s[:colIndex] 480 name := s[colIndex+1:] 481 482 // "dns://keybase.io" syntax. 483 name = strings.TrimPrefix(name, "//") 484 485 if matchNameAndService(name, service) { 486 return key, value, err 487 } 488 } 489 490 if assertionNameRxx.MatchString(s) { 491 key = "" 492 value = s 493 return key, value, nil 494 } 495 496 // We've exhausted our options, it's not a valid assertion we can parse. 497 return "", "", fmt.Errorf("Invalid key-value identity: %s", s) 498 } 499 500 func (a AssertionKeybase) IsKeybase() bool { return true } 501 func (a AssertionSocial) IsSocial() bool { return true } 502 func (a AssertionSocial) IsRemote() bool { return true } 503 func (a AssertionWeb) IsRemote() bool { return true } 504 func (a AssertionFingerprint) IsFingerprint() bool { return true } 505 func (a AssertionUID) IsUID() bool { return true } 506 func (a AssertionTeamID) IsTeamID() bool { return true } 507 func (a AssertionTeamName) IsTeamName() bool { return true } 508 func (a AssertionHTTP) IsRemote() bool { return true } 509 func (a AssertionHTTPS) IsRemote() bool { return true } 510 func (a AssertionDNS) IsRemote() bool { return true } 511 func (a AssertionPhoneNumber) IsRemote() bool { return true } 512 func (a AssertionEmail) IsRemote() bool { return true } 513 514 func (a AssertionUID) ToUID() keybase1.UID { 515 if a.uid.IsNil() { 516 if tmp, err := UIDFromHex(a.Value); err == nil { 517 a.uid = tmp 518 } 519 } 520 return a.uid 521 } 522 523 func (a AssertionTeamID) ToTeamID() keybase1.TeamID { 524 if a.tid.IsNil() { 525 if tmp, err := keybase1.TeamIDFromString(a.Value); err == nil { 526 a.tid = tmp 527 } 528 } 529 return a.tid 530 } 531 532 func (a AssertionTeamName) ToTeamName() keybase1.TeamName { 533 if a.name.IsNil() { 534 if tmp, err := keybase1.TeamNameFromString(a.Value); err != nil { 535 a.name = tmp 536 } 537 } 538 return a.name 539 } 540 541 func (a AssertionKeybase) ToLookup() (key, value string, err error) { 542 return "username", a.Value, nil 543 } 544 545 func (a AssertionUID) ToLookup() (key, value string, err error) { 546 return "uid", a.Value, nil 547 } 548 549 func (a AssertionUID) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 550 var err error 551 a.uid, err = UIDFromHex(a.Value) 552 a.Value = strings.ToLower(a.Value) 553 return a, err 554 } 555 556 func (a AssertionTeamID) ToLookup() (key, value string, err error) { 557 return "tid", a.Value, nil 558 } 559 560 func (a AssertionTeamName) ToLookup() (key, value string, err error) { 561 return "team", a.Value, nil 562 } 563 564 func (a AssertionTeamID) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 565 var err error 566 a.tid, err = keybase1.TeamIDFromString(a.Value) 567 a.Value = strings.ToLower(a.Value) 568 return a, err 569 } 570 571 func (a AssertionTeamName) CheckAndNormalize(_ AssertionContext) (AssertionURL, error) { 572 var err error 573 a.name, err = keybase1.TeamNameFromString(a.Value) 574 a.Value = a.name.String() 575 return a, err 576 } 577 578 func (a AssertionSocial) CheckAndNormalize(ctx AssertionContext) (AssertionURL, error) { 579 var err error 580 a.Value, err = ctx.NormalizeSocialName(a.Key, a.Value) 581 return a, err 582 } 583 584 func (a AssertionPhoneNumber) CheckAndNormalize(ctx AssertionContext) (AssertionURL, error) { 585 if !IsPossiblePhoneNumberAssertion(a.Value) { 586 return nil, NewAssertionCheckError("Invalid phone number: %s", a.Value) 587 } 588 return a, nil 589 } 590 591 func (a AssertionEmail) CheckAndNormalize(ctx AssertionContext) (AssertionURL, error) { 592 if strings.Count(a.Value, "@") != 1 { 593 return nil, NewAssertionCheckError("Invalid email address: %s", a.Value) 594 } 595 return a, nil 596 } 597 598 func (a AssertionSocial) ToLookup() (key, value string, err error) { 599 return a.Key, a.Value, nil 600 } 601 602 func (a AssertionPhoneNumber) ToLookup() (key, value string, err error) { 603 return "phone", "+" + a.Value, nil 604 } 605 606 func (a AssertionEmail) ToLookup() (key, value string, err error) { 607 return "email", a.Value, nil 608 } 609 610 func ParseAssertionURL(ctx AssertionContext, s string, strict bool) (ret AssertionURL, err error) { 611 key, val, err := parseToKVPair(s) 612 613 if err != nil { 614 return 615 } 616 return ParseAssertionURLKeyValue(ctx, key, val, strict) 617 } 618 619 func ParseAssertionURLKeyValue(ctx AssertionContext, key string, val string, strict bool) (ret AssertionURL, err error) { 620 621 if len(key) == 0 { 622 if strict { 623 err = fmt.Errorf("Bad assertion, no 'type' given: %s", val) 624 return nil, err 625 } 626 key = "keybase" 627 } 628 629 base := AssertionURLBase{key, val} 630 switch key { 631 case "keybase": 632 ret = AssertionKeybase{base} 633 case "uid": 634 ret = AssertionUID{AssertionURLBase: base} 635 case "tid": 636 ret = AssertionTeamID{AssertionURLBase: base} 637 case "team": 638 ret = AssertionTeamName{AssertionURLBase: base} 639 case "web": 640 ret = AssertionWeb{base} 641 case "http": 642 ret = AssertionHTTP{base} 643 case "https": 644 ret = AssertionHTTPS{base} 645 case "dns": 646 ret = AssertionDNS{base} 647 case PGPAssertionKey: 648 ret = AssertionFingerprint{base} 649 case "phone": 650 ret = AssertionPhoneNumber{base} 651 case "email": 652 ret = AssertionEmail{base} 653 default: 654 ret = AssertionSocial{base} 655 } 656 return ret.CheckAndNormalize(ctx) 657 } 658 659 type Proof struct { 660 Key, Value string 661 } 662 663 type ProofSet struct { 664 proofs map[string][]Proof 665 } 666 667 func NewProofSet(proofs []Proof) *ProofSet { 668 ret := &ProofSet{ 669 proofs: make(map[string][]Proof), 670 } 671 for _, proof := range proofs { 672 ret.Add(proof) 673 } 674 return ret 675 } 676 677 func (ps *ProofSet) Add(p Proof) { 678 ps.proofs[p.Key] = append(ps.proofs[p.Key], p) 679 } 680 681 func (ps ProofSet) Get(keys []string) (ret []Proof) { 682 for _, key := range keys { 683 if v, ok := ps.proofs[key]; ok { 684 ret = append(ret, v...) 685 } 686 } 687 return ret 688 } 689 690 func FindBestIdentifyComponentURL(e AssertionExpression) AssertionURL { 691 urls := e.CollectUrls(nil) 692 if len(urls) == 0 { 693 return nil 694 } 695 696 var uid, tid, kb, team, soc, fp, rooter AssertionURL 697 698 for _, u := range urls { 699 if u.IsUID() { 700 uid = u 701 break 702 } 703 if u.IsTeamID() { 704 tid = u 705 break 706 } 707 708 if u.IsKeybase() { 709 kb = u 710 } else if u.IsTeamName() { 711 team = u 712 } else if u.IsFingerprint() && fp == nil { 713 fp = u 714 } else if u.IsSocial() { 715 k, _ := u.ToKeyValuePair() 716 if k == "rooter" { 717 rooter = u 718 } else if soc == nil { 719 soc = u 720 } 721 } 722 } 723 724 order := []AssertionURL{uid, tid, kb, team, fp, rooter, soc, urls[0]} 725 for _, p := range order { 726 if p != nil { 727 return p 728 } 729 } 730 return nil 731 } 732 733 func FindBestIdentifyComponent(e AssertionExpression) string { 734 u := FindBestIdentifyComponentURL(e) 735 if u == nil { 736 return "" 737 } 738 return u.String() 739 } 740 741 func CollectAssertions(e AssertionExpression) (remotes AssertionAnd, locals AssertionAnd) { 742 urls := e.CollectUrls(nil) 743 for _, u := range urls { 744 if u.IsRemote() { 745 remotes.factors = append(remotes.factors, u) 746 } else { 747 locals.factors = append(locals.factors, u) 748 } 749 } 750 return remotes, locals 751 } 752 753 func AssertionIsTeam(au AssertionURL) bool { 754 return au != nil && (au.IsTeamID() || au.IsTeamName()) 755 } 756 757 func parseImplicitTeamPart(ctx AssertionContext, s string) (typ string, name string, err error) { 758 nColons := strings.Count(s, ":") 759 nAts := strings.Count(s, "@") 760 nDelimiters := nColons + nAts 761 if nDelimiters > 1 { 762 return "", "", fmt.Errorf("Invalid implicit team part, can have at most one ':' xor '@': %v", s) 763 } 764 if nDelimiters == 0 { 765 if CheckUsername.F(s) { 766 return "keybase", strings.ToLower(s), nil 767 } 768 769 return "", "", fmt.Errorf("Parsed part as keybase username, but invalid username (%q)", s) 770 } 771 assertion, err := ParseAssertionURL(ctx, s, true) 772 if err != nil { 773 return "", "", fmt.Errorf("Could not parse part as SBS assertion") 774 } 775 return assertion.GetKey(), assertion.GetValue(), nil 776 } 777 778 func FormatImplicitTeamDisplayNameSuffix(conflict keybase1.ImplicitTeamConflictInfo) string { 779 return fmt.Sprintf("(conflicted copy %v #%v)", 780 conflict.Time.Time().UTC().Format("2006-01-02"), 781 conflict.Generation) 782 } 783 784 // Parse a name like "mlsteele,malgorithms@twitter#bot (conflicted copy 2017-03-04 #2)" 785 func ParseImplicitTeamDisplayName(ctx AssertionContext, s string, isPublic bool) (ret keybase1.ImplicitTeamDisplayName, err error) { 786 // Turn the whole string tolower 787 s = strings.ToLower(s) 788 789 split1 := strings.SplitN(s, " ", 2) // split1: [assertions, ?conflict] 790 split2 := strings.Split(split1[0], "#") // split2: [writers, ?readers] 791 if len(split2) > 2 { 792 return ret, NewImplicitTeamDisplayNameError("can have at most one '#' separator") 793 } 794 795 seen := make(map[string]bool) 796 var readers, writers keybase1.ImplicitTeamUserSet 797 writers, err = parseImplicitTeamUserSet(ctx, split2[0], seen) 798 if err != nil { 799 return ret, err 800 } 801 802 if writers.NumTotalUsers() == 0 { 803 return ret, NewImplicitTeamDisplayNameError("need at least one writer") 804 } 805 806 if len(split2) == 2 { 807 readers, err = parseImplicitTeamUserSet(ctx, split2[1], seen) 808 if err != nil { 809 return ret, err 810 } 811 } 812 813 var conflictInfo *keybase1.ImplicitTeamConflictInfo 814 if len(split1) > 1 { 815 suffix := split1[1] 816 if len(suffix) == 0 { 817 return ret, NewImplicitTeamDisplayNameError("empty suffix") 818 } 819 conflictInfo, err = ParseImplicitTeamDisplayNameSuffix(suffix) 820 if err != nil { 821 return ret, err 822 } 823 } 824 825 ret = keybase1.ImplicitTeamDisplayName{ 826 IsPublic: isPublic, 827 ConflictInfo: conflictInfo, 828 Writers: writers, 829 Readers: readers, 830 } 831 return ret, nil 832 } 833 834 var implicitTeamDisplayNameConflictRxx = regexp.MustCompile(`^\(conflicted copy (\d{4}-\d{2}-\d{2})( #(\d+))?\)$`) 835 836 func ParseImplicitTeamDisplayNameSuffix(suffix string) (ret *keybase1.ImplicitTeamConflictInfo, err error) { 837 if len(suffix) == 0 { 838 return ret, NewImplicitTeamDisplayNameError("cannot parse empty suffix") 839 } 840 matches := implicitTeamDisplayNameConflictRxx.FindStringSubmatch(suffix) 841 if len(matches) == 0 { 842 return ret, NewImplicitTeamDisplayNameError("malformed suffix: '%s'", suffix) 843 } 844 if len(matches) != 4 { 845 return ret, NewImplicitTeamDisplayNameError("malformed suffix; bad number of matches: %d", len(matches)) 846 } 847 848 conflictTime, err := time.Parse("2006-01-02", matches[1]) 849 if err != nil { 850 return ret, NewImplicitTeamDisplayNameError("malformed suffix time: %v", conflictTime) 851 } 852 853 var generation int 854 if len(matches[3]) == 0 { 855 generation = 1 856 } else { 857 generation, err = strconv.Atoi(matches[3]) 858 if err != nil || generation <= 0 { 859 return ret, NewImplicitTeamDisplayNameError("malformed suffix generation: %v", matches[3]) 860 } 861 } 862 863 return &keybase1.ImplicitTeamConflictInfo{ 864 Generation: keybase1.ConflictGeneration(generation), 865 Time: keybase1.ToTime(conflictTime.UTC()), 866 }, nil 867 } 868 869 func parseImplicitTeamUserSet(ctx AssertionContext, s string, seen map[string]bool) (ret keybase1.ImplicitTeamUserSet, err error) { 870 871 for _, part := range strings.Split(s, ",") { 872 typ, name, err := parseImplicitTeamPart(ctx, part) 873 if err != nil { 874 return keybase1.ImplicitTeamUserSet{}, err 875 } 876 sa := keybase1.SocialAssertion{User: name, Service: keybase1.SocialAssertionService(typ)} 877 idx := sa.String() 878 if seen[idx] { 879 continue 880 } 881 seen[idx] = true 882 if typ == "keybase" { 883 ret.KeybaseUsers = append(ret.KeybaseUsers, name) 884 } else { 885 ret.UnresolvedUsers = append(ret.UnresolvedUsers, sa) 886 } 887 } 888 sort.Strings(ret.KeybaseUsers) 889 sort.Slice(ret.UnresolvedUsers, func(i, j int) bool { return ret.UnresolvedUsers[i].String() < ret.UnresolvedUsers[j].String() }) 890 return ret, nil 891 } 892 893 // Parse a name like "/keybase/private/mlsteele,malgorithms@twitter#bot (conflicted copy 2017-03-04 #2)" 894 func ParseImplicitTeamTLFName(ctx AssertionContext, s string) (keybase1.ImplicitTeamDisplayName, error) { 895 ret := keybase1.ImplicitTeamDisplayName{} 896 s = strings.ToLower(s) 897 parts := strings.Split(s, "/") 898 if len(parts) != 4 { 899 return ret, fmt.Errorf("Invalid team TLF name, must have four parts") 900 } 901 if parts[0] != "" || parts[1] != "keybase" || (parts[2] != "private" && parts[2] != "public") { 902 return ret, fmt.Errorf("Invalid team TLF name") 903 } 904 isPublic := parts[2] == "public" 905 return ParseImplicitTeamDisplayName(ctx, parts[3], isPublic) 906 } 907 908 // Parse a name like "/keybase/team/happy.toucans" 909 func ParseTeamPrivateKBFSPath(s string) (ret keybase1.TeamName, err error) { 910 s = strings.ToLower(s) 911 parts := strings.Split(s, "/") 912 if len(parts) != 4 { 913 return ret, fmt.Errorf("Invalid team TLF name, must have four parts") 914 } 915 if parts[0] != "" || parts[1] != "keybase" || parts[2] != "team" { 916 return ret, fmt.Errorf("Invalid team TLF name") 917 } 918 return keybase1.TeamNameFromString(parts[3]) 919 } 920 921 type ResolvedAssertion struct { 922 UID keybase1.UID 923 Assertion AssertionExpression 924 ResolveResult ResolveResult 925 }