1package libkb
2
3import (
4	"bytes"
5	"crypto"
6	"fmt"
7	"io"
8
9	"github.com/keybase/go-crypto/openpgp/armor"
10
11	"github.com/keybase/go-crypto/openpgp"
12	"github.com/keybase/go-crypto/openpgp/errors"
13	"github.com/keybase/go-crypto/openpgp/packet"
14)
15
16func ExtractPGPSignatureHashMethod(keyring openpgp.KeyRing, sig []byte) (crypto.Hash, uint64, error) {
17	var (
18		rd io.Reader
19
20		p                 packet.Packet
21		hashFunc          crypto.Hash
22		issuerFingerprint []byte
23		issuerKeyID       uint64
24		err               error
25	)
26
27	if IsArmored(sig) {
28		armored, err := armor.Decode(bytes.NewReader(sig))
29		if err != nil {
30			return 0, 0, err
31		}
32		rd = armored.Body
33	} else {
34		rd = bytes.NewReader(sig)
35	}
36
37	packets := packet.NewReader(rd)
38	for {
39		p, err = packets.Next()
40		if err == io.EOF {
41			if hashFunc != 0 {
42				return hashFunc, 0, nil
43			}
44
45			return 0, 0, errors.ErrUnknownIssuer
46		}
47		if err != nil {
48			return 0, 0, err
49		}
50
51		switch sig := p.(type) {
52		case *packet.Signature:
53			if sig.IssuerKeyId == nil {
54				return 0, 0, errors.StructuralError("signature doesn't have an issuer")
55			}
56			issuerKeyID = *sig.IssuerKeyId
57			hashFunc = sig.Hash
58			issuerFingerprint = sig.IssuerFingerprint
59		case *packet.SignatureV3:
60			issuerKeyID = sig.IssuerKeyId
61			hashFunc = sig.Hash
62		default:
63			return 0, 0, errors.StructuralError("non signature packet found")
64		}
65
66		if keyring != nil {
67			keys := keyring.KeysByIdUsage(issuerKeyID, issuerFingerprint, packet.KeyFlagSign)
68			if len(keys) > 0 {
69				return hashFunc, issuerKeyID, nil
70			}
71		}
72	}
73}
74
75var HashToName = map[crypto.Hash]string{
76	crypto.MD4:         "MD4",
77	crypto.MD5:         "MD5",
78	crypto.SHA1:        "SHA1",
79	crypto.SHA224:      "SHA2-224",
80	crypto.SHA256:      "SHA2-256",
81	crypto.SHA384:      "SHA2-384",
82	crypto.SHA512:      "SHA2-512",
83	crypto.RIPEMD160:   "RIPEMD-160",
84	crypto.SHA3_224:    "SHA3-224",
85	crypto.SHA3_256:    "SHA3-256",
86	crypto.SHA3_384:    "SHA3-384",
87	crypto.SHA3_512:    "SHA3-512",
88	crypto.SHA512_224:  "SHA2-512/224",
89	crypto.SHA512_256:  "SHA2-512/256",
90	crypto.BLAKE2s_256: "BLAKE2s-256",
91	crypto.BLAKE2b_256: "BLAKE2b-256",
92	crypto.BLAKE2b_384: "BLAKE2b-384",
93	crypto.BLAKE2b_512: "BLAKE2b-512",
94}
95
96func IsHashSecure(hash crypto.Hash) bool {
97	switch hash {
98	case crypto.SHA224,
99		crypto.SHA256,
100		crypto.SHA384,
101		crypto.SHA512,
102		crypto.SHA3_224,
103		crypto.SHA3_256,
104		crypto.SHA3_384,
105		crypto.SHA3_512,
106		crypto.SHA512_224,
107		crypto.SHA512_256,
108		crypto.BLAKE2s_256,
109		crypto.BLAKE2b_256,
110		crypto.BLAKE2b_384,
111		crypto.BLAKE2b_512:
112		return true
113	default:
114		return false
115	}
116}
117
118type HashSecurityWarningType uint8
119
120const (
121	HashSecurityWarningUnknown HashSecurityWarningType = iota
122	HashSecurityWarningSignatureHash
123	HashSecurityWarningSignersIdentityHash
124	HashSecurityWarningRecipientsIdentityHash
125	HashSecurityWarningOurIdentityHash
126)
127
128type HashSecurityWarning struct {
129	kind        HashSecurityWarningType
130	hash        crypto.Hash
131	fingerprint *PGPFingerprint
132}
133
134func NewHashSecurityWarning(kind HashSecurityWarningType, hash crypto.Hash, fp *PGPFingerprint) HashSecurityWarning {
135	return HashSecurityWarning{kind: kind, hash: hash, fingerprint: fp}
136}
137
138func (h HashSecurityWarning) String() string {
139	switch h.kind {
140	case HashSecurityWarningSignatureHash:
141		return fmt.Sprintf("Message was signed using an insecure hash scheme (%s)", HashToName[h.hash])
142	case HashSecurityWarningSignersIdentityHash:
143		return fmt.Sprintf("Signer's key %s uses an insecure hash scheme (%s)", h.fingerprint.String(), HashToName[h.hash])
144	case HashSecurityWarningRecipientsIdentityHash:
145		return fmt.Sprintf("Recipient's key %s uses an insecure hash scheme (%s)", h.fingerprint.String(), HashToName[h.hash])
146	case HashSecurityWarningOurIdentityHash:
147		return fmt.Sprintf("Our PGP key %s uses an insecure hash scheme (%s)", h.fingerprint.String(), HashToName[h.hash])
148	default:
149		return fmt.Sprintf("Hash security warning was passed an incorrect kind, got %d", h.kind)
150	}
151}
152
153type HashSecurityWarnings []HashSecurityWarning
154
155func (hs HashSecurityWarnings) Strings() (res []string) {
156	for _, h := range hs {
157		res = append(res, h.String())
158	}
159	return
160}
161