-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpinner.go
More file actions
78 lines (62 loc) · 1.37 KB
/
Copy pathpinner.go
File metadata and controls
78 lines (62 loc) · 1.37 KB
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
package tlsclient
import (
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"slices"
"sync"
tls "github.com/nukilabs/utls"
)
type Pinner struct {
sync.RWMutex
auto bool
pins map[string][]string
}
func NewPinner(auto bool) *Pinner {
return &Pinner{
auto: auto,
pins: make(map[string][]string),
}
}
func (p *Pinner) AddPins(hostname string, pins []string) {
p.Lock()
defer p.Unlock()
p.pins[hostname] = pins
}
func (p *Pinner) Pin(certs []*x509.Certificate, addr string) error {
p.RLock()
defer p.RUnlock()
if _, ok := p.pins[addr]; !ok && p.auto {
p.RUnlock()
p.AutoPin(addr)
p.RLock()
} else if !ok {
return nil
}
for _, cert := range certs {
fingerprint := p.Fingerprint(cert)
if slices.Contains(p.pins[addr], fingerprint) {
return nil
}
}
return ErrCertificatePinningFailed
}
func (p *Pinner) Fingerprint(cert *x509.Certificate) string {
digest := sha256.Sum256(cert.RawSubjectPublicKeyInfo)
return base64.StdEncoding.EncodeToString(digest[:])
}
func (p *Pinner) AutoPin(addr string) {
p.Lock()
defer p.Unlock()
conn, err := tls.Dial("tcp", addr, &tls.Config{InsecureSkipVerify: true})
if err != nil {
return
}
state := conn.ConnectionState()
conn.Close()
pins := make([]string, 0, len(state.PeerCertificates))
for _, cert := range state.PeerCertificates {
pins = append(pins, p.Fingerprint(cert))
}
p.pins[addr] = pins
}