-
Notifications
You must be signed in to change notification settings - Fork 164
/
pinner.go
99 lines (75 loc) · 1.96 KB
/
pinner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package tls_client
import (
"errors"
"fmt"
"strings"
http "github.com/bogdanfinn/fhttp"
tls "github.com/bogdanfinn/utls"
"github.com/tam7t/hpkp"
)
var DefaultBadPinHandler = func(req *http.Request) {
fmt.Println("this is the default bad pin handler")
}
var ErrBadPinDetected = errors.New("bad ssl pin detected")
var certificatePinStorage = hpkp.NewMemStorage()
type certificatePinner struct {
certificatePins map[string][]string
}
type CertificatePinner interface {
Pin(conn *tls.UConn, host string) error
}
func NewCertificatePinner(certificatePins map[string][]string) (CertificatePinner, error) {
pinner := &certificatePinner{
certificatePins: certificatePins,
}
err := pinner.init()
if err != nil {
return nil, fmt.Errorf("failed to instantiate certificate pinner: %w", err)
}
return pinner, nil
}
func (cp *certificatePinner) init() error {
if len(cp.certificatePins) == 0 {
return nil
}
for host, pinsByHost := range cp.certificatePins {
includeSubdomains := false
if strings.Contains(host, "*.") {
includeSubdomains = true
host = strings.ReplaceAll(host, "*.", "")
}
pinnedHost := certificatePinStorage.Lookup(host)
if pinnedHost != nil {
// another pinner instance already initialized the host. we do not need to pin again
continue
}
certificatePinStorage.Add(host, &hpkp.Header{
Permanent: true,
Sha256Pins: pinsByHost,
IncludeSubDomains: includeSubdomains,
})
}
return nil
}
func (cp *certificatePinner) Pin(conn *tls.UConn, host string) error {
validPin := false
if len(cp.certificatePins) == 0 {
return nil
}
pinnedHost := certificatePinStorage.Lookup(host)
if pinnedHost == nil {
// host is not pinned, we treat it as valid
return nil
}
for _, peerCert := range conn.ConnectionState().PeerCertificates {
peerPin := hpkp.Fingerprint(peerCert)
if pinnedHost.Matches(peerPin) {
validPin = true
break
}
}
if !validPin {
return ErrBadPinDetected
}
return nil
}