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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package main
import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"errors"
"math/big"
"os"
"time"
)
func tlsConfig(state *BrowserState) *tls.Config {
if ident := state.Identities.Get(state.Url); ident != nil {
return ident
}
return anonymousTLS
}
var tofuStore map[string]string
var ErrTOFUViolation = errors.New("certificate for this domain has changed")
var anonymousTLS = &tls.Config{
InsecureSkipVerify: true,
VerifyConnection: tofuVerify,
}
func tofuVerify(connState tls.ConnectionState) error {
certhash, err := hashCert(connState.PeerCertificates[0])
if err != nil {
return err
}
expected, ok := tofuStore[connState.ServerName]
if !ok {
tofuStore[connState.ServerName] = certhash
return saveTofuStore(tofuStore)
}
if certhash != expected {
return ErrTOFUViolation
}
return nil
}
func hashCert(cert *x509.Certificate) (string, error) {
pubkeybytes, err := x509.MarshalPKIXPublicKey(cert.PublicKey)
if err != nil {
return "", err
}
hash := sha256.Sum256(pubkeybytes)
return hex.EncodeToString(hash[:]), nil
}
func createIdentity(state *BrowserState, name string) (*tls.Config, error) {
pubkey, privkey, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
return nil, err
}
rawprivkey, err := x509.MarshalPKCS8PrivateKey(privkey)
if err != nil {
return nil, err
}
commonName := name
state.Readline.SetPrompt("Common Name [" + name + "]: ")
if line, err := state.Readline.Readline(); err != nil {
return nil, err
} else if line != "" {
commonName = line
}
expiration := time.Date(9999, 12, 31, 0, 0, 0, 0, time.UTC)
state.Readline.SetPrompt("Expiration (yyyy-mm-dd) [9999-12-31]: ")
if line, err := state.Readline.Readline(); err != nil {
return nil, err
} else if line != "" {
expiration, err = time.ParseInLocation(time.DateOnly, line, time.UTC)
if err != nil {
return nil, err
}
}
snLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, snLimit)
if err != nil {
return nil, err
}
template := &x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{CommonName: commonName},
NotAfter: expiration,
KeyUsage: x509.KeyUsageDigitalSignature,
BasicConstraintsValid: true,
}
rawcert, err := x509.CreateCertificate(rand.Reader, template, template, pubkey, privkey)
if err != nil {
return nil, err
}
identFile, err := saveIdentity(name, rawprivkey, rawcert)
if err != nil {
return nil, err
}
cert, err := tls.LoadX509KeyPair(identFile, identFile)
if err != nil {
_ = os.Remove(identFile)
return nil, err
}
return identityForCert(cert), nil
}
func identityForCert(cert tls.Certificate) *tls.Config {
return &tls.Config{
Certificates: []tls.Certificate{cert},
InsecureSkipVerify: true,
VerifyConnection: tofuVerify,
}
}
|