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	"io"
9
10	"github.com/keybase/client/go/kbcrypto"
11	"github.com/keybase/client/go/logger"
12	"github.com/keybase/saltpack"
13)
14
15// SaltpackVerifyContext is context for engine calls
16type SaltpackVerifyContext interface {
17	GetLog() logger.Logger
18}
19
20// Wraps kbcrypto.Verification error with libkb.VerificationError. You should
21// expect a libkb.VerificationError if exposing the error to the GUI.
22func getVerificationErrorWithStatusCode(kberr *kbcrypto.VerificationError) (err VerificationError) {
23	err.Cause.Err = kberr.Cause
24	switch err.Cause.Err.(type) {
25	case APINetError:
26		err.Cause.StatusCode = SCAPINetworkError
27	case saltpack.ErrNoSenderKey:
28		err.Cause.StatusCode = SCDecryptionKeyNotFound
29	case saltpack.ErrWrongMessageType:
30		err.Cause.StatusCode = SCWrongCryptoMsgType
31	}
32	return err
33}
34
35func SaltpackVerify(g SaltpackVerifyContext, source io.Reader, sink io.WriteCloser, checkSender func(saltpack.SigningPublicKey) error) (err error) {
36	defer func() {
37		if kbErr, ok := err.(kbcrypto.VerificationError); ok {
38			err = getVerificationErrorWithStatusCode(&kbErr)
39		}
40	}()
41
42	sc, newSource, err := ClassifyStream(source)
43	if err != nil {
44		return err
45	}
46	if sc.Format != CryptoMessageFormatSaltpack {
47		return WrongCryptoFormatError{
48			Wanted:    CryptoMessageFormatSaltpack,
49			Received:  sc.Format,
50			Operation: "verify",
51		}
52	}
53
54	if sc.Type != CryptoMessageTypeAttachedSignature {
55		return kbcrypto.NewVerificationError(
56			saltpack.ErrWrongMessageType{
57				Wanted:   saltpack.MessageType(CryptoMessageTypeAttachedSignature),
58				Received: saltpack.MessageType(sc.Type),
59			})
60	}
61
62	source = newSource
63	kr := echoKeyring{}
64
65	var skey saltpack.SigningPublicKey
66	var vs io.Reader
67	var brand string
68	if sc.Armored {
69		skey, vs, brand, err = saltpack.NewDearmor62VerifyStream(saltpack.CheckKnownMajorVersion, source, kr)
70	} else {
71		skey, vs, err = saltpack.NewVerifyStream(saltpack.CheckKnownMajorVersion, source, kr)
72	}
73	if err != nil {
74		g.GetLog().Debug("saltpack.NewDearmor62VerifyStream error: %s", err)
75		return kbcrypto.NewVerificationError(err)
76	}
77
78	if checkSender != nil {
79		if err = checkSender(skey); err != nil {
80			return kbcrypto.NewVerificationError(err)
81		}
82	}
83
84	n, err := io.Copy(sink, vs)
85	if err != nil {
86		return kbcrypto.NewVerificationError(err)
87	}
88
89	if sc.Armored {
90		if err = checkSaltpackBrand(brand); err != nil {
91			return kbcrypto.NewVerificationError(err)
92		}
93	}
94
95	g.GetLog().Debug("Verify: read %d bytes", n)
96
97	if err := sink.Close(); err != nil {
98		return kbcrypto.NewVerificationError(err)
99	}
100	return nil
101}
102
103func SaltpackVerifyDetached(g SaltpackVerifyContext, message io.Reader, signature []byte, checkSender func(saltpack.SigningPublicKey) error) (err error) {
104	defer func() {
105		if kbErr, ok := err.(kbcrypto.VerificationError); ok {
106			err = getVerificationErrorWithStatusCode(&kbErr)
107		}
108	}()
109
110	sc, _, err := ClassifyStream(bytes.NewReader(signature))
111	if err != nil {
112		return err
113	}
114	if sc.Format != CryptoMessageFormatSaltpack {
115		return WrongCryptoFormatError{
116			Wanted:    CryptoMessageFormatSaltpack,
117			Received:  sc.Format,
118			Operation: "verify detached",
119		}
120	}
121
122	kr := echoKeyring{}
123
124	var skey saltpack.SigningPublicKey
125	if sc.Armored {
126		var brand string
127		skey, brand, err = saltpack.Dearmor62VerifyDetachedReader(saltpack.CheckKnownMajorVersion, message, string(signature), kr)
128		if err != nil {
129			g.GetLog().Debug("saltpack.Dearmor62VerifyDetachedReader error: %s", err)
130			return kbcrypto.NewVerificationError(err)
131		}
132		if err = checkSaltpackBrand(brand); err != nil {
133			return kbcrypto.NewVerificationError(err)
134		}
135	} else {
136		skey, err = saltpack.VerifyDetachedReader(saltpack.CheckKnownMajorVersion, message, signature, kr)
137		if err != nil {
138			g.GetLog().Debug("saltpack.VerifyDetachedReader error: %s", err)
139			return kbcrypto.NewVerificationError(err)
140		}
141	}
142
143	if checkSender != nil {
144		if err = checkSender(skey); err != nil {
145			return kbcrypto.NewVerificationError(err)
146		}
147	}
148
149	return nil
150}
151
152type echoKeyring struct {
153	Contextified
154}
155
156func (e echoKeyring) LookupSigningPublicKey(kid []byte) saltpack.SigningPublicKey {
157	var k kbcrypto.NaclSigningKeyPublic
158	copy(k[:], kid)
159	return saltSignerPublic{key: k}
160}
161