...
1
16
17
18
19 package pubkeypin
20
21 import (
22 "crypto/sha256"
23 "crypto/x509"
24 "encoding/hex"
25 "strings"
26
27 "github.com/pkg/errors"
28 )
29
30 const (
31
32 formatSHA256 = "sha256"
33 )
34
35 var (
36
37 supportedFormats = strings.Join([]string{formatSHA256}, ", ")
38 )
39
40
41 type Set struct {
42 sha256Hashes map[string]bool
43 }
44
45
46 func NewSet() *Set {
47 return &Set{make(map[string]bool)}
48 }
49
50
51 func (s *Set) Allow(pubKeyHashes ...string) error {
52 for _, pubKeyHash := range pubKeyHashes {
53 parts := strings.Split(pubKeyHash, ":")
54 if len(parts) != 2 {
55 return errors.Errorf("invalid hash, expected \"format:hex-value\". "+
56 "Known format(s) are: %s", supportedFormats)
57 }
58 format, value := parts[0], parts[1]
59
60 switch strings.ToLower(format) {
61 case "sha256":
62 if err := s.allowSHA256(value); err != nil {
63 return errors.Errorf("invalid hash %q, %v", pubKeyHash, err)
64 }
65 default:
66 return errors.Errorf("unknown hash format %q. Known format(s) are: %s", format, supportedFormats)
67 }
68 }
69 return nil
70 }
71
72
73 func (s *Set) CheckAny(certificates []*x509.Certificate) error {
74 var hashes []string
75
76 for _, certificate := range certificates {
77 if s.checkSHA256(certificate) {
78 return nil
79 }
80
81 hashes = append(hashes, Hash(certificate))
82 }
83 return errors.Errorf("none of the public keys %q are pinned", strings.Join(hashes, ":"))
84 }
85
86
87 func (s *Set) Empty() bool {
88 return len(s.sha256Hashes) == 0
89 }
90
91
92
93
94 func Hash(certificate *x509.Certificate) string {
95 spkiHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
96 return formatSHA256 + ":" + strings.ToLower(hex.EncodeToString(spkiHash[:]))
97 }
98
99
100 func (s *Set) allowSHA256(hash string) error {
101
102 hashLength := hex.DecodedLen(len(hash))
103 if hashLength != sha256.Size {
104 return errors.Errorf("expected a %d byte SHA-256 hash, found %d bytes", sha256.Size, hashLength)
105 }
106
107
108 _, err := hex.DecodeString(hash)
109 if err != nil {
110 return errors.Wrap(err, "could not decode SHA-256 from hex")
111 }
112
113
114 s.sha256Hashes[strings.ToLower(hash)] = true
115 return nil
116 }
117
118
119 func (s *Set) checkSHA256(certificate *x509.Certificate) bool {
120 actualHash := sha256.Sum256(certificate.RawSubjectPublicKeyInfo)
121 actualHashHex := strings.ToLower(hex.EncodeToString(actualHash[:]))
122 return s.sha256Hashes[actualHashHex]
123 }
124
View as plain text