OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package webpagereplay | 5 package webpagereplay |
6 | 6 |
7 import ( | 7 import ( |
8 "crypto/rand" | 8 "crypto/rand" |
9 "crypto/tls" | 9 "crypto/tls" |
10 "crypto/x509" | 10 "crypto/x509" |
11 "fmt" | 11 "fmt" |
12 "io" | 12 "io" |
13 "time" | 13 "time" |
14 ) | 14 ) |
15 | 15 |
16 // Returns a TLS configuration that serves a recorded server leaf cert signed by | 16 // Returns a TLS configuration that serves a recorded server leaf cert signed by |
17 // root CA. | 17 // root CA. |
18 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { | 18 func ReplayTLSConfig(root tls.Certificate, a *Archive) (*tls.Config, error) { |
19 root_cert, err := getRootCert(root) | 19 root_cert, err := getRootCert(root) |
20 if err != nil { | 20 if err != nil { |
21 return nil, fmt.Errorf("bad local cert: %v", err) | 21 return nil, fmt.Errorf("bad local cert: %v", err) |
22 } | 22 } |
23 tp := &tlsProxy{&root, root_cert, a, nil} | 23 tp := &tlsProxy{&root, root_cert, a, nil} |
24 return &tls.Config{ | 24 return &tls.Config{ |
25 » » Certificates: []tls.Certificate{*tp.root}, | 25 » » GetConfigForClient: tp.getReplayConfigForClient, |
26 » » GetCertificate: tp.getReplayCertificate, | |
27 }, nil | 26 }, nil |
28 } | 27 } |
29 | 28 |
30 // Returns a TLS configuration that serves a server leaf cert fetched over the | 29 // Returns a TLS configuration that serves a server leaf cert fetched over the |
31 // network on demand. | 30 // network on demand. |
32 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { | 31 func RecordTLSConfig(root tls.Certificate, w *WritableArchive) (*tls.Config, err
or) { |
33 root_cert, err := getRootCert(root) | 32 root_cert, err := getRootCert(root) |
34 if err != nil { | 33 if err != nil { |
35 return nil, fmt.Errorf("bad local cert: %v", err) | 34 return nil, fmt.Errorf("bad local cert: %v", err) |
36 } | 35 } |
37 tp := &tlsProxy{&root, root_cert, nil, w} | 36 tp := &tlsProxy{&root, root_cert, nil, w} |
38 return &tls.Config{ | 37 return &tls.Config{ |
39 » » Certificates: []tls.Certificate{*tp.root}, | 38 » » GetConfigForClient: tp.getRecordConfigForClient, |
40 » » GetCertificate: tp.getCertificate, | |
41 }, nil | 39 }, nil |
42 } | 40 } |
43 | 41 |
44 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { | 42 func getRootCert(root tls.Certificate) (*x509.Certificate, error) { |
45 root_cert, err := x509.ParseCertificate(root.Certificate[0]) | 43 root_cert, err := x509.ParseCertificate(root.Certificate[0]) |
46 if err != nil { | 44 if err != nil { |
47 return nil, err | 45 return nil, err |
48 } | 46 } |
49 root_cert.IsCA = true | 47 root_cert.IsCA = true |
50 root_cert.BasicConstraintsValid = true | 48 root_cert.BasicConstraintsValid = true |
51 return root_cert, nil | 49 return root_cert, nil |
52 } | 50 } |
53 | 51 |
54 type tlsProxy struct { | 52 type tlsProxy struct { |
55 root *tls.Certificate | 53 root *tls.Certificate |
56 root_cert *x509.Certificate | 54 root_cert *x509.Certificate |
57 archive *Archive | 55 archive *Archive |
58 writable_archive *WritableArchive | 56 writable_archive *WritableArchive |
59 } | 57 } |
60 | 58 |
61 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. | 59 // TODO: For now, this just returns a self-signed cert using the given ServerNam
e. |
62 // In the future, for better HTTP/2 support, we may want to record host equivale
nce | 60 // In the future, for better HTTP/2 support, we may want to record host equivale
nce |
63 // classes in the archive, where an equivalence class contains all hosts that ca
n be | 61 // classes in the archive, where an equivalence class contains all hosts that ca
n be |
64 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the | 62 // served by the same IP. We can then run a DNS proxy that maps all hostnames in
the |
65 // same equivalence class to the same local port, which models the possibility t
hat | 63 // same equivalence class to the same local port, which models the possibility t
hat |
66 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. | 64 // every equivalence class of hostnames can be served over the same HTTP/2 conne
ction. |
67 // | 65 func (tp *tlsProxy) getReplayConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { |
68 // getCertificate implements a callback for tls.Config.GetCertificate. | |
69 func (tp *tlsProxy) getReplayCertificate(clientHello *tls.ClientHelloInfo) (*tls
.Certificate, error) { | |
70 h := clientHello.ServerName | 66 h := clientHello.ServerName |
71 if h == "" { | 67 if h == "" { |
72 » » return tp.root, nil | 68 » » return &tls.Config{ |
| 69 » » » Certificates: []tls.Certificate{*tp.root}, |
| 70 » » }, nil |
73 } | 71 } |
74 | 72 |
75 » der_bytes, err := tp.archive.FindHostCert(h) | 73 » der_bytes, negotiatedProtocol, err := tp.archive.FindHostTlsConfig(h) |
76 if err != nil || der_bytes == nil { | 74 if err != nil || der_bytes == nil { |
77 return nil, fmt.Errorf("No archived cert for %s", h) | 75 return nil, fmt.Errorf("No archived cert for %s", h) |
78 } | 76 } |
79 » return &tls.Certificate{ | 77 » return &tls.Config{ |
80 » » Certificate: [][]byte{der_bytes}, | 78 » » Certificates: []tls.Certificate{ |
81 » » PrivateKey: tp.root.PrivateKey, | 79 » » » tls.Certificate{ |
| 80 » » » » Certificate: [][]byte{der_bytes}, |
| 81 » » » » PrivateKey: tp.root.PrivateKey, |
| 82 » » » }}, |
| 83 » » NextProtos: []string{negotiatedProtocol}, |
82 }, nil | 84 }, nil |
83 } | 85 } |
84 | 86 |
85 func (tp *tlsProxy) getCertificate(clientHello *tls.ClientHelloInfo) (*tls.Certi
ficate, error) { | 87 func (tp *tlsProxy) getRecordConfigForClient(clientHello *tls.ClientHelloInfo) (
*tls.Config, error) { |
86 h := clientHello.ServerName | 88 h := clientHello.ServerName |
87 if h == "" { | 89 if h == "" { |
88 » » return tp.root, nil | 90 » » return &tls.Config{ |
| 91 » » » Certificates: []tls.Certificate{*tp.root}, |
| 92 » » }, nil |
89 } | 93 } |
90 | 94 » der_bytes, negotiatedProtocol, err := tp.writable_archive.Archive.FindHo
stTlsConfig(h) |
91 » der_bytes, err := tp.writable_archive.FindHostCert(h) | |
92 if err == nil && der_bytes != nil { | 95 if err == nil && der_bytes != nil { |
93 » » return &tls.Certificate{ | 96 » » return &tls.Config{ |
94 » » » Certificate: [][]byte{der_bytes}, | 97 » » » Certificates: []tls.Certificate{ |
95 » » » PrivateKey: tp.root.PrivateKey, | 98 » » » » tls.Certificate{ |
| 99 » » » » » Certificate: [][]byte{der_bytes}, |
| 100 » » » » » PrivateKey: tp.root.PrivateKey, |
| 101 » » » » }}, |
| 102 » » » NextProtos: []string{negotiatedProtocol}, |
96 }, nil | 103 }, nil |
97 } | 104 } |
98 | 105 |
99 » conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", h), nil) | 106 » conn, err := tls.Dial("tcp", fmt.Sprintf("%s:443", h), &tls.Config{ |
| 107 » » NextProtos: []string{"h2", "http/1.1"}, |
| 108 » }) |
100 if err != nil { | 109 if err != nil { |
101 return nil, fmt.Errorf("Couldn't reach host %s: %v", h, err) | 110 return nil, fmt.Errorf("Couldn't reach host %s: %v", h, err) |
102 } | 111 } |
103 defer conn.Close() | 112 defer conn.Close() |
104 conn.Handshake() | 113 conn.Handshake() |
105 template := conn.ConnectionState().PeerCertificates[0] | 114 template := conn.ConnectionState().PeerCertificates[0] |
106 | 115 |
107 template.Subject.CommonName = h | 116 template.Subject.CommonName = h |
108 template.NotBefore = time.Now() | 117 template.NotBefore = time.Now() |
109 template.NotAfter = template.NotBefore.Add(87658 * time.Hour) | 118 template.NotAfter = template.NotBefore.Add(87658 * time.Hour) |
110 template.PublicKey = tp.root_cert.PublicKey | 119 template.PublicKey = tp.root_cert.PublicKey |
111 var buf [20]byte | 120 var buf [20]byte |
112 if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil { | 121 if _, err := io.ReadFull(rand.Reader, buf[:]); err != nil { |
113 return nil, err | 122 return nil, err |
114 } | 123 } |
115 template.SerialNumber.SetBytes(buf[:]) | 124 template.SerialNumber.SetBytes(buf[:]) |
116 template.Issuer = tp.root_cert.Subject | 125 template.Issuer = tp.root_cert.Subject |
117 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment
| x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign | 126 template.KeyUsage = x509.KeyUsageCertSign | x509.KeyUsageKeyEncipherment
| x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign |
118 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x5
09.ExtKeyUsageServerAuth} | 127 template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x5
09.ExtKeyUsageServerAuth} |
119 | 128 |
120 der_bytes, err = x509.CreateCertificate(rand.Reader, template, tp.root_c
ert, template.PublicKey, tp.root.PrivateKey) | 129 der_bytes, err = x509.CreateCertificate(rand.Reader, template, tp.root_c
ert, template.PublicKey, tp.root.PrivateKey) |
121 if err != nil { | 130 if err != nil { |
122 return nil, fmt.Errorf("create cert failed: %v", err) | 131 return nil, fmt.Errorf("create cert failed: %v", err) |
123 } | 132 } |
124 » tp.writable_archive.RecordCert(h, der_bytes) | 133 |
125 » return &tls.Certificate{ | 134 » negotiatedProtocol = conn.ConnectionState().NegotiatedProtocol |
126 » » Certificate: [][]byte{der_bytes}, | 135 » tp.writable_archive.RecordTlsConfig(h, der_bytes, negotiatedProtocol) |
127 » » PrivateKey: tp.root.PrivateKey, | 136 |
| 137 » return &tls.Config{ |
| 138 » » Certificates: []tls.Certificate{ |
| 139 » » » tls.Certificate{ |
| 140 » » » » Certificate: [][]byte{der_bytes}, |
| 141 » » » » PrivateKey: tp.root.PrivateKey}}, |
| 142 » » NextProtos: []string{negotiatedProtocol}, |
128 }, nil | 143 }, nil |
129 } | 144 } |
OLD | NEW |