1// Copyright 2015 Keybase, Inc. All rights reserved. Use of
2// this source code is governed by the included BSD license.
3
4package libkb
5
6import (
7	"bytes"
8	"encoding/hex"
9	"fmt"
10	"io/ioutil"
11	"strings"
12
13	"github.com/keybase/client/go/kbcrypto"
14	keybase1 "github.com/keybase/client/go/protocol/keybase1"
15	"github.com/keybase/go-crypto/openpgp"
16	"github.com/keybase/go-crypto/openpgp/armor"
17	jsonw "github.com/keybase/go-jsonw"
18)
19
20func GetSigID(w *jsonw.Wrapper) (keybase1.SigID, error) {
21	s, err := w.GetString()
22	if err != nil {
23		return "", err
24	}
25	return keybase1.SigIDFromString(s)
26}
27
28func GetSigIDBase(w *jsonw.Wrapper) (keybase1.SigIDBase, error) {
29	s, err := w.GetString()
30	if err != nil {
31		return "", err
32	}
33	return keybase1.SigIDBaseFromString(s)
34}
35
36type ParsedSig struct {
37	Block       *armor.Block
38	SigBody     []byte
39	MD          *openpgp.MessageDetails
40	LiteralData []byte
41}
42
43func PGPOpenSig(armored string) (ps *ParsedSig, err error) {
44	pso := ParsedSig{}
45	pso.Block, err = armor.Decode(strings.NewReader(cleanPGPInput(armored)))
46	if err != nil {
47		return
48	}
49	pso.SigBody, err = ioutil.ReadAll(pso.Block.Body)
50	if err != nil {
51		return
52	}
53	ps = &pso
54	return
55}
56
57// OpenSig takes an armored PGP or Keybase signature and opens
58// the armor.  It will return the body of the signature, the
59// sigID of the body, or an error if it didn't work out.
60func OpenSig(armored string) (ret []byte, id keybase1.SigIDBase, err error) {
61	if isPGPBundle(armored) {
62		var ps *ParsedSig
63		if ps, err = PGPOpenSig(armored); err == nil {
64			ret = ps.SigBody
65			id = ps.ID()
66		}
67	} else {
68		if ret, err = KbOpenSig(armored); err == nil {
69			id = kbcrypto.ComputeSigIDFromSigBody(ret)
70		}
71	}
72	return
73}
74
75// SigExtractPayloadAndKID extracts the payload and KID of the key that
76// was supposedly used to sign this message. A KID will only be returned
77// for KB messages, and not for PGP messages
78func SigExtractPayloadAndKID(armored string) (payload []byte, kid keybase1.KID, sigID keybase1.SigIDBase, err error) {
79	if isPGPBundle(armored) {
80		payload, sigID, err = SigExtractPGPPayload(armored)
81	} else {
82		payload, kid, sigID, err = SigExtractKbPayloadAndKID(armored)
83	}
84	return payload, kid, sigID, err
85}
86
87func SigAssertPayload(armored string, expected []byte) (sigID keybase1.SigIDBase, err error) {
88	if isPGPBundle(armored) {
89		return SigAssertPGPPayload(armored, expected)
90	}
91	return SigAssertKbPayload(armored, expected)
92}
93
94func SigAssertPGPPayload(armored string, expected []byte) (sigID keybase1.SigIDBase, err error) {
95	var ps *ParsedSig
96	ps, err = PGPOpenSig(armored)
97	if err != nil {
98		return
99	}
100	if err = ps.AssertPayload(expected); err != nil {
101		ps = nil
102		return
103	}
104	sigID = ps.ID()
105	return
106}
107
108func SigExtractPGPPayload(armored string) (payload []byte, sigID keybase1.SigIDBase, err error) {
109	var ps *ParsedSig
110	ps, err = PGPOpenSig(armored)
111	if err != nil {
112		return nil, sigID, err
113	}
114	payload, err = ps.ExtractPayload()
115	if err != nil {
116		return nil, sigID, err
117	}
118	return payload, ps.ID(), nil
119}
120
121func (ps *ParsedSig) ExtractPayload() (payload []byte, err error) {
122
123	ring := EmptyKeyRing{}
124	md, err := openpgp.ReadMessage(bytes.NewReader(ps.SigBody), ring, nil, nil)
125	if err != nil {
126		return nil, err
127	}
128	data, err := ioutil.ReadAll(md.UnverifiedBody)
129	if err != nil {
130		return nil, err
131	}
132	return data, nil
133}
134
135func (ps *ParsedSig) AssertPayload(expected []byte) error {
136
137	data, err := ps.ExtractPayload()
138	if err != nil {
139		return err
140	}
141
142	if !FastByteArrayEq(data, expected) {
143		err = fmt.Errorf("Signature did not contain expected text")
144		return err
145	}
146	return nil
147}
148
149func (ps *ParsedSig) Verify(k PGPKeyBundle) (err error) {
150	ps.MD, err = openpgp.ReadMessage(bytes.NewReader(ps.SigBody), k, nil, nil)
151	if err != nil {
152		return
153	}
154	if !ps.MD.IsSigned || ps.MD.SignedBy == nil {
155		err = fmt.Errorf("Message wasn't signed")
156		return
157	}
158	if !k.MatchesKey(ps.MD.SignedBy) {
159		err = fmt.Errorf("Got wrong SignedBy key %v",
160			hex.EncodeToString(ps.MD.SignedBy.PublicKey.Fingerprint[:]))
161		return
162	}
163	if ps.MD.UnverifiedBody == nil {
164		err = fmt.Errorf("no signed material found")
165		return
166	}
167
168	ps.LiteralData, err = ioutil.ReadAll(ps.MD.UnverifiedBody)
169	if err != nil {
170		return
171	}
172
173	// We'll see a sig error here after reading in the UnverifiedBody above,
174	// if there was one to see.
175	if err = ps.MD.SignatureError; err != nil {
176		return
177	}
178
179	if ps.MD.Signature == nil && ps.MD.SignatureV3 == nil {
180		err = fmt.Errorf("No available signature after checking signature")
181		return
182	}
183
184	// Hopefully by here we've covered all of our bases.
185	return nil
186}
187
188func (ps *ParsedSig) ID() keybase1.SigIDBase {
189	return kbcrypto.ComputeSigIDFromSigBody(ps.SigBody)
190}
191
192func IsPGPSig(s string) bool {
193	return strings.HasPrefix(s, "-----BEGIN PGP MESSAGE-----")
194}
195