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  }