1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package engine
5
6import (
7	"errors"
8	"fmt"
9
10	"github.com/keybase/client/go/libkb"
11	keybase1 "github.com/keybase/client/go/protocol/keybase1"
12)
13
14type RevokeSigsEngine struct {
15	libkb.Contextified
16	sigIDQueries []string
17}
18
19func NewRevokeSigsEngine(g *libkb.GlobalContext, sigIDQueries []string) *RevokeSigsEngine {
20	return &RevokeSigsEngine{
21		sigIDQueries: sigIDQueries,
22		Contextified: libkb.NewContextified(g),
23	}
24}
25
26func (e *RevokeSigsEngine) Name() string {
27	return "RevokeSigs"
28}
29
30func (e *RevokeSigsEngine) Prereqs() Prereqs {
31	return Prereqs{
32		Device: true,
33	}
34}
35
36func (e *RevokeSigsEngine) RequiredUIs() []libkb.UIKind {
37	return []libkb.UIKind{
38		libkb.LogUIKind,
39		libkb.SecretUIKind,
40	}
41}
42
43func (e *RevokeSigsEngine) SubConsumers() []libkb.UIConsumer {
44	return []libkb.UIConsumer{}
45}
46
47func (e *RevokeSigsEngine) getSigIDsToRevoke(me *libkb.User) ([]keybase1.SigID, error) {
48	ret := make([]keybase1.SigID, len(e.sigIDQueries))
49	for i, query := range e.sigIDQueries {
50		if len(query) < keybase1.SigIDQueryMin {
51			return nil, errors.New("sigID query too short")
52		}
53		sigID, err := me.SigIDSearch(query)
54		if err != nil {
55			return nil, err
56		}
57		ret[i] = sigID
58	}
59	return ret, nil
60}
61
62func (e *RevokeSigsEngine) Run(m libkb.MetaContext) error {
63	m.G().LocalSigchainGuard().Set(m.Ctx(), "RevokeSigsEngine")
64	defer m.G().LocalSigchainGuard().Clear(m.Ctx(), "RevokeSigsEngine")
65
66	me, err := libkb.LoadMe(libkb.NewLoadUserArgWithMetaContext(m))
67	if err != nil {
68		return err
69	}
70
71	sigIDsToRevoke, err := e.getSigIDsToRevoke(me)
72	if err != nil {
73		return err
74	}
75
76	lease, merkleRoot, err := libkb.RequestDowngradeLeaseBySigIDs(m.Ctx(), m.G(), sigIDsToRevoke)
77	if err != nil {
78		return err
79	}
80
81	ska := libkb.SecretKeyArg{
82		Me:      me,
83		KeyType: libkb.DeviceSigningKeyType,
84	}
85	sigKey, err := e.G().Keyrings.GetSecretKeyWithPrompt(m, m.SecretKeyPromptArg(ska, "to revoke a signature"))
86	if err != nil {
87		return err
88	}
89	if sigKey == nil {
90		return fmt.Errorf("Revocation signing key is nil.")
91	}
92	if err = sigKey.CheckSecretKey(); err != nil {
93		return err
94	}
95	proof, err := me.RevokeSigsProof(m, sigKey, sigIDsToRevoke, merkleRoot)
96	if err != nil {
97		return err
98	}
99	sig, _, linkID, err := libkb.SignJSON(proof.J, sigKey)
100	if err != nil {
101		return err
102	}
103	kid := sigKey.GetKID()
104	_, err = m.G().API.Post(m, libkb.APIArg{
105		Endpoint:    "sig/revoke",
106		SessionType: libkb.APISessionTypeREQUIRED,
107		Args: libkb.HTTPArgs{
108			"signing_kid":        libkb.S{Val: kid.String()},
109			"sig":                libkb.S{Val: sig},
110			"downgrade_lease_id": libkb.S{Val: string(lease.LeaseID)},
111		},
112	})
113	if err != nil {
114		return err
115	}
116
117	return libkb.MerkleCheckPostedUserSig(m, me.GetUID(), proof.Seqno, linkID)
118}
119