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	"crypto/sha256"
9	"hash"
10	"io"
11
12	keybase1 "github.com/keybase/client/go/protocol/keybase1"
13	"github.com/keybase/go-crypto/openpgp"
14	"github.com/keybase/go-crypto/openpgp/armor"
15	"github.com/keybase/go-crypto/openpgp/packet"
16)
17
18// SimpleSign signs the given data stream, outputs an armored string which is
19// the attached signature of the input data
20func SimpleSign(payload []byte, key PGPKeyBundle) (out string, id keybase1.SigIDBase, err error) {
21	var outb bytes.Buffer
22	var in io.WriteCloser
23	var h HashSummer
24	if !key.HasSecretKey() {
25		err = NoSecretKeyError{}
26		return
27	}
28	if in, h, err = ArmoredAttachedSign(NopWriteCloser{&outb}, *key.Entity, nil, nil); err != nil {
29		return
30	}
31	if _, err = in.Write(payload); err != nil {
32		return
33	}
34	if err = in.Close(); err != nil {
35		return
36	}
37	out = outb.String()
38	if id, err = keybase1.SigIDBaseFromSlice(h()); err != nil {
39		return
40	}
41	return
42}
43
44type HashingWriteCloser struct {
45	targ   io.WriteCloser
46	hasher hash.Hash
47}
48
49func (h HashingWriteCloser) Write(buf []byte) (int, error) {
50	n, err := h.targ.Write(buf)
51	if err == nil {
52		_, err = h.hasher.Write(buf)
53	}
54	return n, err
55}
56
57func (h HashingWriteCloser) Close() error {
58	err := h.targ.Close()
59	return err
60}
61
62type HashSummer func() []byte
63
64func ArmoredAttachedSign(out io.WriteCloser, signed openpgp.Entity, hints *openpgp.FileHints, config *packet.Config) (in io.WriteCloser, h HashSummer, err error) {
65
66	var aout io.WriteCloser
67
68	aout, err = armor.Encode(out, "PGP MESSAGE", PGPArmorHeaders)
69	if err != nil {
70		return
71	}
72
73	hwc := HashingWriteCloser{aout, sha256.New()}
74	in, err = openpgp.AttachedSign(hwc, signed, hints, config)
75	h = func() []byte { return hwc.hasher.Sum(nil) }
76
77	return in, h, err
78}
79
80func AttachedSignWrapper(out io.WriteCloser, key PGPKeyBundle, armored bool) (
81	in io.WriteCloser, err error) {
82
83	if armored {
84		in, _, err = ArmoredAttachedSign(out, *key.Entity, nil, nil)
85	} else {
86		in, err = openpgp.AttachedSign(out, *key.Entity, nil, nil)
87	}
88	return
89}
90
91// NopWriteCloser is like an ioutil.NopCloser, but for an io.Writer.
92// TODO: we have two of these in OpenPGP packages alone. This probably needs
93// to be promoted somewhere more common.
94//
95// From here:
96//     https://code.google.com/p/go/source/browse/openpgp/write.go?repo=crypto&r=1e7a3e301825bf9cb32e0535f3761d62d2d369d1#364
97//
98type NopWriteCloser struct {
99	W io.Writer
100}
101
102func (c NopWriteCloser) Write(data []byte) (n int, err error) {
103	return c.W.Write(data)
104}
105
106func (c NopWriteCloser) Close() error {
107	return nil
108}
109