-
Notifications
You must be signed in to change notification settings - Fork 100
/
proof_verifier.go
169 lines (140 loc) · 4.41 KB
/
proof_verifier.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
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package goquic
import "C"
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/binary"
"errors"
"log"
"unsafe"
)
type ProofVerifier struct {
// Holds Job
jobs []*ProofVerifyJob
}
type ProofVerifyJob struct {
quicVersion int
hostname []byte
serverConfig []byte
chloHash []byte
certSct []byte
signature []byte
certs [][]byte
}
func CreateProofVerifier() *ProofVerifier {
return &ProofVerifier{
jobs: make([]*ProofVerifyJob, 0),
}
}
// Generate "proof of authenticity" (See "Quic Crypto" docs for details)
// Length of the prefix used to calculate the signature: length of label + 0x00 byte
var ProofSignatureLabelOld = []byte{'Q', 'U', 'I', 'C', ' ', 's', 'e', 'r', 'v', 'e', 'r', ' ', 'c', 'o', 'n', 'f', 'i', 'g', ' ', 's', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', 0x00}
var ProofSignatureLabel = []byte{'Q', 'U', 'I', 'C', ' ', 'C', 'H', 'L', 'O', ' ', 'a', 'n', 'd', ' ', 's', 'e', 'r', 'v', 'e', 'r', ' ', 'c', 'o', 'n', 'f', 'i', 'g', ' ', 's', 'i', 'g', 'n', 'a', 't', 'u', 'r', 'e', 0x00}
func (job *ProofVerifyJob) CheckSignature(cert *x509.Certificate) error {
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
// cert.CheckSignature() uses PKCS1v15, not PSS. So we cannot use that on RSA
h := sha256.New()
if job.quicVersion > 30 {
h.Write(ProofSignatureLabel)
bs := make([]byte, 4)
binary.LittleEndian.PutUint32(bs, uint32(len(job.chloHash)))
h.Write(bs)
h.Write(job.chloHash)
} else {
h.Write(ProofSignatureLabelOld)
}
h.Write(job.serverConfig)
if err := rsa.VerifyPSS(pub, crypto.SHA256, h.Sum(nil), job.signature, nil); err != nil {
return err
}
case *ecdsa.PublicKey:
// TODO(hodduc): TEST needed
if err := cert.CheckSignature(x509.ECDSAWithSHA256, job.serverConfig, job.signature); err != nil {
return err
}
default:
return errors.New("Unsupported Public key type")
}
return nil
}
func (job *ProofVerifyJob) Verify() bool {
leafcert, err := x509.ParseCertificate(job.certs[0])
if err != nil {
// TODO(hodduc) error chk, log chk
log.Fatal("Parsing leaf cert", err)
return false
}
if err := job.CheckSignature(leafcert); err != nil {
// TODO(hodduc) error chk, log chk
log.Fatal("Signature fail", err)
return false
}
buf := bytes.NewBuffer(nil)
for _, asn1cert := range job.certs {
buf.Write(asn1cert)
}
certs, err := x509.ParseCertificates(buf.Bytes())
if err != nil {
// TODO(hodduc) error chk, log chk
log.Fatal("Parsing cert chain", err)
return false
}
intmPool := x509.NewCertPool()
for i := 1; i < len(certs); i++ {
intmPool.AddCert(certs[i])
}
verifyOpt := x509.VerifyOptions{
DNSName: string(job.hostname),
Intermediates: intmPool,
}
if _, err := certs[0].Verify(verifyOpt); err != nil {
log.Fatal("Verify failed", err)
return false
}
return true
}
//export NewProofVerifyJob
func NewProofVerifyJob(proof_verifier_key int64, quicVersion int,
hostname_c unsafe.Pointer, hostname_sz C.size_t,
server_config_c unsafe.Pointer, server_config_sz C.size_t,
chlo_hash_c unsafe.Pointer, chlo_hash_sz C.size_t,
cert_sct_c unsafe.Pointer, cert_sct_sz C.size_t,
signature_c unsafe.Pointer, signature_sz C.size_t) int64 {
proofVerifier := proofVerifierPtr.Get(proof_verifier_key)
job := &ProofVerifyJob{
quicVersion: quicVersion,
hostname: C.GoBytes(hostname_c, C.int(hostname_sz)),
serverConfig: C.GoBytes(server_config_c, C.int(server_config_sz)),
chloHash: C.GoBytes(chlo_hash_c, C.int(chlo_hash_sz)),
certSct: C.GoBytes(cert_sct_c, C.int(cert_sct_sz)),
signature: C.GoBytes(signature_c, C.int(signature_sz)),
certs: make([][]byte, 0),
}
proofVerifier.jobs = append(proofVerifier.jobs, job)
return proofVerifyJobPtr.Set(job)
}
//export ProofVerifyJobAddCert
func ProofVerifyJobAddCert(job_key int64, cert_c unsafe.Pointer, cert_sz C.size_t) {
job := proofVerifyJobPtr.Get(job_key)
job.certs = append(job.certs, C.GoBytes(cert_c, C.int(cert_sz)))
}
//export ProofVerifyJobVerifyProof
func ProofVerifyJobVerifyProof(job_key int64) C.int {
job := proofVerifyJobPtr.Get(job_key)
// XXX(hodduc): Job has ended, so I will release job here. Job should not be referenced again
defer proofVerifyJobPtr.Del(job_key)
if ret := job.Verify(); ret {
return C.int(1)
} else {
return C.int(0)
}
}
//export ReleaseProofVerifier
func ReleaseProofVerifier(proof_verifier_key int64) {
proofVerifierPtr.Del(proof_verifier_key)
}