func (ckf *ComputedKeyFamily) revokeKids(kids []keybase1.KID, tcl TypedChainLink) (err error) {
	for _, k := range kids {
		if k.Exists() {
			if err = ckf.RevokeKid(k, tcl); err != nil {
				return
			}
		}
	}
	return
}
revokeKids operates on the per-kid revocations in the given
TypedChainLink and applies them accordingly.
package libkb
import (
	"fmt"
	"time"
	keybase1 "github.com/keybase/client/go/protocol"
	jsonw "github.com/keybase/go-jsonw"
)
type KeybaseTime struct {
	Unix  int64  
	Chain int    
}
type ComputedKeyInfo struct {
	Contextified
	Status KeyStatus
	Eldest bool
	Sibkey bool
	CTime int64  
	ETime int64  
	Parent keybase1.KID
	Subkey keybase1.KID
	Delegations map[keybase1.SigID]keybase1.KID
	DelegatedAt *KeybaseTime
	RevokedAt *KeybaseTime
	RevokedBy keybase1.KID
	ActivePGPHash string
}
func (cki ComputedKeyInfo) GetCTime() time.Time {
	return time.Unix(cki.CTime, 0)
}
func (cki ComputedKeyInfo) GetETime() time.Time {
	return UnixToTimeMappingZero(cki.ETime)
}
type ComputedKeyInfos struct {
	Contextified
	dirty bool  
	Infos map[keybase1.KID]*ComputedKeyInfo
	Sigs map[keybase1.SigID]*ComputedKeyInfo
	Devices map[keybase1.DeviceID]*Device
	KIDToDeviceID map[keybase1.KID]keybase1.DeviceID
}
type RawKeyFamily struct {
	AllBundles []string `json:"all_bundles"`
}
type PGPKeySet struct {
	Contextified
	PermissivelyMergedKey *PGPKeyBundle
	KeysByHash            map[string]*PGPKeyBundle
}
func (s *PGPKeySet) addKey(key *PGPKeyBundle) error {
	fullHash, err := key.FullHash()
	if err != nil {
		return err
	}
	s.G().Log.Debug("adding PGP kid %s with full hash %s", key.GetKID().String(), fullHash)
	s.KeysByHash[fullHash] = key
	strippedKey := key.StripRevocations()
	if s.PermissivelyMergedKey == nil {
		s.PermissivelyMergedKey = strippedKey
	} else {
		s.PermissivelyMergedKey.MergeKey(strippedKey)
	}
	return nil
}
type KeyFamily struct {
	pgp2kid map[PGPFingerprint]keybase1.KID
	kid2pgp map[keybase1.KID]PGPFingerprint
	AllKIDs map[keybase1.KID]bool
	PGPKeySets map[keybase1.KID]*PGPKeySet
	SingleKeys map[keybase1.KID]GenericKey  
	BundlesForTesting []string
	Contextified
}
type ComputedKeyFamily struct {
	Contextified
	kf  *KeyFamily
	cki *ComputedKeyInfos
}
func (cki *ComputedKeyInfos) Insert(k keybase1.KID, i *ComputedKeyInfo) {
	cki.Infos[k] = i
	cki.dirty = true
}
func (cki *ComputedKeyInfos) PaperDevices() []*Device {
	var d []*Device
	for _, v := range cki.Devices {
		if v.Status == nil {
			continue
		}
		if *v.Status != DeviceStatusActive {
			continue
		}
		if v.Type != DeviceTypePaper {
			continue
		}
		d = append(d, v)
	}
	return d
}
func (cki ComputedKeyInfos) ShallowCopy() *ComputedKeyInfos {
	ret := &ComputedKeyInfos{
		dirty:         cki.dirty,
		Infos:         make(map[keybase1.KID]*ComputedKeyInfo, len(cki.Infos)),
		Sigs:          make(map[keybase1.SigID]*ComputedKeyInfo, len(cki.Sigs)),
		Devices:       make(map[keybase1.DeviceID]*Device, len(cki.Devices)),
		KIDToDeviceID: make(map[keybase1.KID]keybase1.DeviceID, len(cki.KIDToDeviceID)),
	}
	for k, v := range cki.Infos {
		ret.Infos[k] = v
	}
	for k, v := range cki.Sigs {
		ret.Sigs[k] = v
	}
	for k, v := range cki.Devices {
		ret.Devices[k] = v
	}
	for k, v := range cki.KIDToDeviceID {
		ret.KIDToDeviceID[k] = v
	}
	return ret
}
func NewComputedKeyInfos(g *GlobalContext) *ComputedKeyInfos {
	return &ComputedKeyInfos{
		Contextified:  NewContextified(g),
		Infos:         make(map[keybase1.KID]*ComputedKeyInfo),
		Sigs:          make(map[keybase1.SigID]*ComputedKeyInfo),
		Devices:       make(map[keybase1.DeviceID]*Device),
		KIDToDeviceID: make(map[keybase1.KID]keybase1.DeviceID),
	}
}
func NewComputedKeyInfo(eldest, sibkey bool, status KeyStatus, ctime, etime int64, activePGPHash string) ComputedKeyInfo {
	return ComputedKeyInfo{
		Eldest:        eldest,
		Sibkey:        sibkey,
		Status:        status,
		CTime:         ctime,
		ETime:         etime,
		Delegations:   make(map[keybase1.SigID]keybase1.KID),
		ActivePGPHash: activePGPHash,
	}
}
func (cki ComputedKeyInfos) InsertLocalEldestKey(kid keybase1.KID) {
	eldestCki := NewComputedKeyInfo(true, true, KeyUncancelled, 0, 0, ""  )
	cki.Insert(kid, &eldestCki)
}
func (cki ComputedKeyInfos) InsertServerEldestKey(eldestKey GenericKey, un NormalizedUsername) error {
	kbid := KeybaseIdentity(un)
	if pgp, ok := eldestKey.(*PGPKeyBundle); ok {
		match, ctime, etime := pgp.CheckIdentity(kbid)
		if match {
			eldestCki := NewComputedKeyInfo(true, true, KeyUncancelled, ctime, etime, ""  )
			cki.Insert(eldestKey.GetKID(), &eldestCki)
			return nil
		}
		return KeyFamilyError{"InsertServerEldestKey found a non-matching eldest key."}
	}
	return KeyFamilyError{"InsertServerEldestKey found a non-PGP key."}
}
func (ckf ComputedKeyFamily) InsertEldestLink(tcl TypedChainLink, username NormalizedUsername) (err error) {
	kid := tcl.GetKID()
	key, err := ckf.FindKeyWithKIDUnsafe(kid)
	if err != nil {
		return
	}
	var ctimeKb, etimeKb int64 = 0, 0
	ctimeKb = tcl.GetCTime().Unix()
	etimeKb = tcl.GetETime().Unix()
	var ctimePGP, etimePGP int64 = -1, -1
	if pgp, ok := key.(*PGPKeyBundle); ok {
		kbid := KeybaseIdentity(username)
		_, ctimePGP, etimePGP = pgp.CheckIdentity(kbid)
	}
	var ctime int64
	if ctimePGP >= 0 {
		ctime = ctimePGP
	} else {
		ctime = ctimeKb
	}
	var etime int64
	if etimePGP >= 0 {
		etime = etimePGP
	} else {
		etime = etimeKb
	}
	eldestCki := NewComputedKeyInfo(true, true, KeyUncancelled, ctime, etime, tcl.GetPGPFullHash())
	ckf.cki.Insert(kid, &eldestCki)
	return nil
}
func ParseKeyFamily(g *GlobalContext, jw *jsonw.Wrapper) (ret *KeyFamily, err error) {
	defer g.Trace("ParseKeyFamily", func() error { return err })()
	if jw == nil || jw.IsNil() {
		err = KeyFamilyError{"nil record from server"}
		return
	}
	kf := KeyFamily{
		Contextified: NewContextified(g),
		pgp2kid:      make(map[PGPFingerprint]keybase1.KID),
		kid2pgp:      make(map[keybase1.KID]PGPFingerprint),
	}
	var rkf RawKeyFamily
	if err = jw.UnmarshalAgain(&rkf); err != nil {
		return
	}
	kf.BundlesForTesting = rkf.AllBundles
	kf.AllKIDs = make(map[keybase1.KID]bool)
	kf.PGPKeySets = make(map[keybase1.KID]*PGPKeySet)
	kf.SingleKeys = make(map[keybase1.KID]GenericKey)
	for i, bundle := range rkf.AllBundles {
		newKey, w, err := ParseGenericKey(bundle)
		if err != nil {
			g.Log.Notice("Failed to parse public key at position %d", i)
			g.Log.Debug("Key parsing error: %s", err)
			g.Log.Debug("Full key dump follows")
			g.Log.Debug(bundle)
			continue
		}
		w.Warn(g)
		kid := newKey.GetKID()
		if pgp, isPGP := newKey.(*PGPKeyBundle); isPGP {
			ks, ok := kf.PGPKeySets[kid]
			if !ok {
				ks = &PGPKeySet{NewContextified(g), nil, make(map[string]*PGPKeyBundle)}
				kf.PGPKeySets[kid] = ks
				fp := pgp.GetFingerprint()
				kf.pgp2kid[fp] = kid
				kf.kid2pgp[kid] = fp
			}
			ks.addKey(pgp)
		} else {
			kf.SingleKeys[kid] = newKey
		}
		kf.AllKIDs[kid] = true
	}
	ret = &kf
	return
}
func (ckf ComputedKeyFamily) FindKeyWithKIDUnsafe(kid keybase1.KID) (GenericKey, error) {
	if key, ok := ckf.kf.SingleKeys[kid]; ok {
		return key, nil
	}
	if ks, ok := ckf.kf.PGPKeySets[kid]; ok {
		if ckf.cki != nil {
			if cki, found := ckf.cki.Infos[kid]; found && cki.ActivePGPHash != "" {
				if key, ok := ks.KeysByHash[cki.ActivePGPHash]; ok {
					return key, nil
				}
				return nil, NoKeyError{fmt.Sprintf("Sigchain specified that KID %s is a PGP key with hash %s but no version of the key with that hash was found", kid, cki.ActivePGPHash)}
			}
		}
		return ks.PermissivelyMergedKey, nil
	}
	return nil, KeyFamilyError{fmt.Sprintf("No key found for %s", kid)}
}
func (ckf ComputedKeyFamily) getCkiIfActiveAtTime(kid keybase1.KID, t time.Time) (ret *ComputedKeyInfo, err error) {
	unixTime := t.Unix()
	if ki := ckf.cki.Infos[kid]; ki == nil {
		err = NoKeyError{fmt.Sprintf("The key '%s' wasn't found", kid)}
	} else if ki.Status != KeyUncancelled {
		err = KeyRevokedError{fmt.Sprintf("The key '%s' is no longer active", kid)}
	} else if ki.ETime > 0 && unixTime > ki.ETime {
		formatStr := "Mon Jan 2 15:04:05 -0700 MST 2006"
		ckf.G().Log.Warning("Checking status of key %s\n    with respect to time [%s],\n    found it had expired at [%s].",
			kid, t.Format(formatStr), time.Unix(ki.ETime, 0).Format(formatStr))
		err = KeyExpiredError{fmt.Sprintf("The key '%s' expired at %s", kid, time.Unix(ki.ETime, 0))}
	} else {
		ret = ki
	}
	return
}
func (ckf ComputedKeyFamily) getCkiIfActiveNow(kid keybase1.KID) (ret *ComputedKeyInfo, err error) {
	return ckf.getCkiIfActiveAtTime(kid, time.Now())
}
func (ckf ComputedKeyFamily) FindActiveSibkey(kid keybase1.KID) (key GenericKey, cki ComputedKeyInfo, err error) {
	return ckf.FindActiveSibkeyAtTime(kid, time.Now())
}
func (ckf ComputedKeyFamily) FindActiveSibkeyAtTime(kid keybase1.KID, t time.Time) (key GenericKey, cki ComputedKeyInfo, err error) {
	liveCki, err := ckf.getCkiIfActiveAtTime(kid, t)
	if liveCki == nil || err != nil {
	} else if !liveCki.Sibkey {
		err = BadKeyError{fmt.Sprintf("The key '%s' wasn't delegated as a sibkey", kid)}
	} else {
		key, err = ckf.FindKeyWithKIDUnsafe(kid)
		cki = *liveCki
	}
	return
}
func (ckf ComputedKeyFamily) FindActiveEncryptionSubkey(kid keybase1.KID) (GenericKey, error) {
	ki, err := ckf.getCkiIfActiveNow(kid)
	if err != nil {
		return nil, err
	}
	if ki.Sibkey {
		return nil, BadKeyError{fmt.Sprintf("The key '%s' was delegated as a sibkey", kid.String())}
	}
	key, err := ckf.FindKeyWithKIDUnsafe(kid)
	if err != nil {
		return nil, err
	}
	if !CanEncrypt(key) {
		return nil, BadKeyError{fmt.Sprintf("The key '%s' cannot encrypt", kid.String())}
	}
	return key, nil
}
func (ckf ComputedKeyFamily) FindKIDFromFingerprint(fp PGPFingerprint) (kid keybase1.KID, err error) {
	kid, ok := ckf.kf.pgp2kid[fp]
	if !ok {
		return kid, NoKeyError{fmt.Sprintf("No key found in key family for %q", fp)}
	}
	return kid, nil
}
func TclToKeybaseTime(tcl TypedChainLink) *KeybaseTime {
	return &KeybaseTime{
		Unix:  tcl.GetCTime().Unix(),
		Chain: tcl.GetMerkleSeqno(),
	}
}
func NowAsKeybaseTime(seqno int) *KeybaseTime {
	return &KeybaseTime{
		Unix:  time.Now().Unix(),
		Chain: seqno,
	}
}
func (ckf *ComputedKeyFamily) Delegate(tcl TypedChainLink) (err error) {
	kid := tcl.GetDelegatedKid()
	sigid := tcl.GetSigID()
	tm := TclToKeybaseTime(tcl)
	if _, err := ckf.FindKeyWithKIDUnsafe(kid); err != nil {
		return KeyFamilyError{fmt.Sprintf("Delegated KID %s is not in the key family", kid.String())}
	}
	err = ckf.cki.Delegate(kid, tm, sigid, tcl.GetKID(), tcl.GetParentKid(), tcl.GetPGPFullHash(), (tcl.GetRole() == DLGSibkey), tcl.GetCTime(), tcl.GetETime())
	return
}
func (cki *ComputedKeyInfos) Delegate(kid keybase1.KID, tm *KeybaseTime, sigid keybase1.SigID, signingKid, parentKID keybase1.KID, pgpHash string, isSibkey bool, ctime, etime time.Time) (err error) {
	cki.G().Log.Debug("ComputeKeyInfos::Delegate To %s with %s at sig %s", kid.String(), signingKid, sigid.ToDisplayString(true))
	info, found := cki.Infos[kid]
	if !found {
		newInfo := NewComputedKeyInfo(false, false, KeyUncancelled, ctime.Unix(), etime.Unix(), pgpHash)
		newInfo.DelegatedAt = tm
		info = &newInfo
		cki.Infos[kid] = info
	} else {
		info.Status = KeyUncancelled
		info.CTime = ctime.Unix()
		info.ETime = etime.Unix()
	}
	info.Delegations[sigid] = signingKid
	info.Sibkey = isSibkey
	cki.Sigs[sigid] = info
	if parentKID.Exists() {
		info.Parent = parentKID
		if parent, found := cki.Infos[parentKID]; found {
			parent.Subkey = kid
		}
	}
	return
}
func (ckf *ComputedKeyFamily) Revoke(tcl TypedChainLink) (err error) {
	err = ckf.revokeSigs(tcl.GetRevocations(), tcl)
	if err == nil {
		err = ckf.revokeKids(tcl.GetRevokeKids(), tcl)
	}
	return err
}
func (ckf *ComputedKeyFamily) SetActivePGPHash(kid keybase1.KID, hash string) {
	if _, ok := ckf.cki.Infos[kid]; ok {
		ckf.cki.Infos[kid].ActivePGPHash = hash
	} else {
		ckf.G().Log.Debug("| Skipped setting active hash, since key was never delegated")
	}
}
func (ckf *ComputedKeyFamily) revokeSigs(sigs []keybase1.SigID, tcl TypedChainLink) error {
	for _, s := range sigs {
		if len(s) == 0 {
			continue
		}
		if err := ckf.RevokeSig(s, tcl); err != nil {
			return err
		}
	}
	return nil
}