1package libkb
2
3// UPAK = "User Plus All Keys"
4
5import (
6	"fmt"
7
8	"github.com/keybase/client/go/kbcrypto"
9	keybase1 "github.com/keybase/client/go/protocol/keybase1"
10)
11
12// BaseProofSet creates a basic proof set for a user with their
13// keybase and uid proofs and any pgp fingerprint proofs.
14func BaseProofSet(u *keybase1.UserPlusKeysV2AllIncarnations) *ProofSet {
15	proofs := []Proof{
16		{Key: "keybase", Value: u.GetName()},
17		{Key: "uid", Value: u.GetUID().String()},
18	}
19	for _, key := range u.Current.PGPKeys {
20		proofs = append(proofs, Proof{Key: PGPAssertionKey, Value: key.Fingerprint.String()})
21	}
22	return NewProofSet(proofs)
23}
24
25// checkKIDPGP checks that the user has the given PGP KID valid *now*. Note that it doesn't
26// check for revoked PGP keys, and it also does not check key expiration.
27func checkKIDPGP(u *keybase1.UserPlusKeysV2AllIncarnations, kid keybase1.KID) (found bool) {
28	for _, key := range u.Current.PGPKeys {
29		if key.Base.Kid.Equal(kid) {
30			return true
31		}
32	}
33	return false
34}
35
36func checkKIDKeybase(u *keybase1.UserPlusKeysV2AllIncarnations, kid keybase1.KID) (found bool, revokedAt *keybase1.KeybaseTime, deleted bool) {
37	exportRevokedAt := func(key keybase1.PublicKeyV2NaCl) *keybase1.KeybaseTime {
38		if key.Base.Revocation != nil {
39			// This is the inverse of the marshalling we do in rpc_exim.go.
40			return &keybase1.KeybaseTime{
41				Unix:  key.Base.Revocation.Time,
42				Chain: key.Base.Revocation.PrevMerkleRootSigned.Seqno,
43			}
44		}
45		return nil
46	}
47	for _, key := range u.Current.DeviceKeys {
48		if key.Base.Kid.Equal(kid) {
49			found = true
50			revokedAt = exportRevokedAt(key)
51		}
52	}
53	for _, pastIncarnation := range u.PastIncarnations {
54		for _, key := range pastIncarnation.DeviceKeys {
55			if key.Base.Kid.Equal(kid) {
56				found = true
57				deleted = true
58				revokedAt = exportRevokedAt(key)
59			}
60		}
61	}
62	return
63}
64
65func CheckKID(u *keybase1.UserPlusKeysV2AllIncarnations, kid keybase1.KID) (found bool, revokedAt *keybase1.KeybaseTime, deleted bool) {
66	if IsPGPAlgo(kbcrypto.AlgoType(kid.GetKeyType())) {
67		found = checkKIDPGP(u, kid)
68		return found, nil, false
69	}
70	return checkKIDKeybase(u, kid)
71}
72
73func GetRemoteChainLinkFor(m MetaContext, follower *keybase1.UserPlusKeysV2AllIncarnations, followeeUsername NormalizedUsername, followeeUID keybase1.UID) (ret *TrackChainLink, err error) {
74	defer m.Trace(fmt.Sprintf("UPAK#GetRemoteChainLinkFor(%s,%s,%s)", follower.Current.GetUID(), followeeUsername, followeeUID), &err)()
75	m.VLogf(VLog1, "| Full user: %+v\n", *follower)
76	rtl := follower.GetRemoteTrack(followeeUID)
77	if rtl == nil {
78		m.VLogf(VLog0, "| no remote track found")
79		return nil, nil
80	}
81	if !NewNormalizedUsername(rtl.Username).Eq(followeeUsername) {
82		return nil, UIDMismatchError{Msg: fmt.Sprintf("Usernames didn't match for (%s,%q); got %s", followeeUID, followeeUsername.String(), rtl.Uid)}
83	}
84	if !rtl.Uid.Equal(followeeUID) {
85		return nil, UIDMismatchError{Msg: fmt.Sprintf("UIDs didn't match for (%s,%q); got %s", followeeUID, followeeUsername.String(), rtl.Uid)}
86	}
87	var lid LinkID
88	m.VLogf(VLog0, "| remote track found with linkID=%s", rtl.LinkID)
89	lid, err = ImportLinkID(rtl.LinkID)
90	if err != nil {
91		m.Debug("| Failed to import link ID")
92		return nil, err
93	}
94	var link *ChainLink
95	link, err = ImportLinkFromStorage(m, lid, follower.GetUID())
96	if err != nil {
97		m.Debug("| failed to import link from storage")
98		return nil, err
99	}
100	if link == nil {
101		m.Debug("| no cached chainlink found")
102		// Such a bug is only possible if the DB cache was reset after
103		// this user was originally loaded in; otherwise, all of this
104		// UPAK's chain links should be available on disk.
105		return nil, InconsistentCacheStateError{}
106	}
107	ret, err = ParseTrackChainLink(GenericChainLink{link})
108	m.VLogf(VLog0, "| ParseTrackChainLink -> found=%v", (ret != nil))
109	return ret, err
110}
111
112func TrackChainLinkFromUPK2AI(m MetaContext, follower *keybase1.UserPlusKeysV2AllIncarnations, followeeUsername NormalizedUsername, followeeUID keybase1.UID) (*TrackChainLink, error) {
113	tcl, err := GetRemoteChainLinkFor(m, follower, followeeUsername, followeeUID)
114	if err != nil {
115		return nil, err
116	}
117	tcl, err = TrackChainLinkFor(m, follower.Current.Uid, followeeUID, tcl, err)
118	return tcl, err
119}
120
121func NormalizedUsernameFromUPK2(u keybase1.UserPlusKeysV2) NormalizedUsername {
122	return NewNormalizedUsername(u.Username)
123}
124