mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-11-04 09:17:32 -05:00
Merge pull request #1955 from v2fly/nonext
migrate to go mod to maintain 3rd party libs
This commit is contained in:
commit
98fd5eafbf
7
external/README.md
vendored
Normal file
7
external/README.md
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
# Note
|
||||
|
||||
The `external` dir in the project exists for historical reasons. We will migrate to use go mod to maintain 3rd party libraries.
|
||||
|
||||
Fow now, all modules under external are used by "quic-go" to support quic protocol, and can't migrate without a breaking change.
|
||||
|
||||
The plan is that we will remove the whole `external` dir when `quic-go` is tested to be matured enough in production.
|
@ -1,9 +0,0 @@
|
||||
# This is the official list of Gorilla WebSocket authors for copyright
|
||||
# purposes.
|
||||
#
|
||||
# Please keep the list sorted.
|
||||
|
||||
Gary Burd <gary@beagledreams.com>
|
||||
Google LLC (https://opensource.google.com/)
|
||||
Joachim Bauch <mail@joachim-bauch.de>
|
||||
|
22
external/github.com/gorilla/websocket/LICENSE
vendored
22
external/github.com/gorilla/websocket/LICENSE
vendored
@ -1,22 +0,0 @@
|
||||
Copyright (c) 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
64
external/github.com/gorilla/websocket/README.md
vendored
64
external/github.com/gorilla/websocket/README.md
vendored
@ -1,64 +0,0 @@
|
||||
# Gorilla WebSocket
|
||||
|
||||
Gorilla WebSocket is a [Go](http://golang.org/) implementation of the
|
||||
[WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol.
|
||||
|
||||
[![Build Status](https://travis-ci.org/gorilla/websocket.svg?branch=master)](https://travis-ci.org/gorilla/websocket)
|
||||
[![GoDoc](https://godoc.org/github.com/gorilla/websocket?status.svg)](https://godoc.org/github.com/gorilla/websocket)
|
||||
|
||||
### Documentation
|
||||
|
||||
* [API Reference](http://godoc.org/github.com/gorilla/websocket)
|
||||
* [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat)
|
||||
* [Command example](https://github.com/gorilla/websocket/tree/master/examples/command)
|
||||
* [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo)
|
||||
* [File watch example](https://github.com/gorilla/websocket/tree/master/examples/filewatch)
|
||||
|
||||
### Status
|
||||
|
||||
The Gorilla WebSocket package provides a complete and tested implementation of
|
||||
the [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. The
|
||||
package API is stable.
|
||||
|
||||
### Installation
|
||||
|
||||
go get github.com/gorilla/websocket
|
||||
|
||||
### Protocol Compliance
|
||||
|
||||
The Gorilla WebSocket package passes the server tests in the [Autobahn Test
|
||||
Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn
|
||||
subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn).
|
||||
|
||||
### Gorilla WebSocket compared with other packages
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><a href="http://godoc.org/github.com/gorilla/websocket">github.com/gorilla</a></th>
|
||||
<th><a href="http://godoc.org/golang.org/x/net/websocket">golang.org/x/net</a></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr>
|
||||
<tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr>
|
||||
<tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr>
|
||||
<tr><td>Get the <a href="https://tools.ietf.org/html/rfc6455#section-5.6">type</a> of a received data message</td><td>Yes</td><td>Yes, see note 2</td></tr>
|
||||
<tr><td colspan="3">Other Features</tr></td>
|
||||
<tr><td><a href="https://tools.ietf.org/html/rfc7692">Compression Extensions</a></td><td>Experimental</td><td>No</td></tr>
|
||||
<tr><td>Read message using io.Reader</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextReader">Yes</a></td><td>No, see note 3</td></tr>
|
||||
<tr><td>Write message using io.WriteCloser</td><td><a href="http://godoc.org/github.com/gorilla/websocket#Conn.NextWriter">Yes</a></td><td>No, see note 3</td></tr>
|
||||
</table>
|
||||
|
||||
Notes:
|
||||
|
||||
1. Large messages are fragmented in [Chrome's new WebSocket implementation](http://www.ietf.org/mail-archive/web/hybi/current/msg10503.html).
|
||||
2. The application can get the type of a received data message by implementing
|
||||
a [Codec marshal](http://godoc.org/golang.org/x/net/websocket#Codec.Marshal)
|
||||
function.
|
||||
3. The go.net io.Reader and io.Writer operate across WebSocket frame boundaries.
|
||||
Read returns when the input buffer is full or a frame boundary is
|
||||
encountered. Each call to Write sends a single frame message. The Gorilla
|
||||
io.Reader and io.WriteCloser operate on a single WebSocket message.
|
||||
|
396
external/github.com/gorilla/websocket/client.go
vendored
396
external/github.com/gorilla/websocket/client.go
vendored
@ -1,396 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrBadHandshake is returned when the server response to opening handshake is
|
||||
// invalid.
|
||||
var ErrBadHandshake = errors.New("websocket: bad handshake")
|
||||
|
||||
var errInvalidCompression = errors.New("websocket: invalid compression negotiation")
|
||||
|
||||
// NewClient creates a new client connection using the given net connection.
|
||||
// The URL u specifies the host and request URI. Use requestHeader to specify
|
||||
// the origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies
|
||||
// (Cookie). Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etc.
|
||||
//
|
||||
// Deprecated: Use Dialer instead.
|
||||
func NewClient(netConn net.Conn, u *url.URL, requestHeader http.Header, readBufSize, writeBufSize int) (c *Conn, response *http.Response, err error) {
|
||||
d := Dialer{
|
||||
ReadBufferSize: readBufSize,
|
||||
WriteBufferSize: writeBufSize,
|
||||
NetDial: func(net, addr string) (net.Conn, error) {
|
||||
return netConn, nil
|
||||
},
|
||||
}
|
||||
return d.Dial(u.String(), requestHeader)
|
||||
}
|
||||
|
||||
// A Dialer contains options for connecting to WebSocket server.
|
||||
type Dialer struct {
|
||||
// NetDial specifies the dial function for creating TCP connections. If
|
||||
// NetDial is nil, net.Dial is used.
|
||||
NetDial func(network, addr string) (net.Conn, error)
|
||||
|
||||
// NetDialContext specifies the dial function for creating TCP connections. If
|
||||
// NetDialContext is nil, net.DialContext is used.
|
||||
NetDialContext func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
|
||||
// Proxy specifies a function to return a proxy for a given
|
||||
// Request. If the function returns a non-nil error, the
|
||||
// request is aborted with the provided error.
|
||||
// If Proxy is nil or returns a nil *URL, no proxy is used.
|
||||
// Proxy func(*http.Request) (*url.URL, error)
|
||||
|
||||
// TLSClientConfig specifies the TLS configuration to use with tls.Client.
|
||||
// If nil, the default configuration is used.
|
||||
TLSClientConfig *tls.Config
|
||||
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
|
||||
// size is zero, then a useful default size is used. The I/O buffer sizes
|
||||
// do not limit the size of the messages that can be sent or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// WriteBufferPool is a pool of buffers for write operations. If the value
|
||||
// is not set, then write buffers are allocated to the connection for the
|
||||
// lifetime of the connection.
|
||||
//
|
||||
// A pool is most useful when the application has a modest volume of writes
|
||||
// across a large number of connections.
|
||||
//
|
||||
// Applications should use a single pool for each unique value of
|
||||
// WriteBufferSize.
|
||||
WriteBufferPool BufferPool
|
||||
|
||||
// Subprotocols specifies the client's requested subprotocols.
|
||||
Subprotocols []string
|
||||
|
||||
// EnableCompression specifies if the client should attempt to negotiate
|
||||
// per message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
// EnableCompression bool
|
||||
|
||||
// Jar specifies the cookie jar.
|
||||
// If Jar is nil, cookies are not sent in requests and ignored
|
||||
// in responses.
|
||||
Jar http.CookieJar
|
||||
}
|
||||
|
||||
// Dial creates a new client connection by calling DialContext with a background context.
|
||||
func (d *Dialer) Dial(urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
return d.DialContext(context.Background(), urlStr, requestHeader)
|
||||
}
|
||||
|
||||
var errMalformedURL = errors.New("malformed ws or wss URL")
|
||||
|
||||
func hostPortNoPort(u *url.URL) (hostPort, hostNoPort string) {
|
||||
hostPort = u.Host
|
||||
hostNoPort = u.Host
|
||||
if i := strings.LastIndex(u.Host, ":"); i > strings.LastIndex(u.Host, "]") {
|
||||
hostNoPort = hostNoPort[:i]
|
||||
} else {
|
||||
switch u.Scheme {
|
||||
case "wss":
|
||||
hostPort += ":443"
|
||||
case "https":
|
||||
hostPort += ":443"
|
||||
default:
|
||||
hostPort += ":80"
|
||||
}
|
||||
}
|
||||
return hostPort, hostNoPort
|
||||
}
|
||||
|
||||
// DefaultDialer is a dialer with all fields set to the default values.
|
||||
var DefaultDialer = &Dialer{
|
||||
// Proxy: http.ProxyFromEnvironment,
|
||||
HandshakeTimeout: 45 * time.Second,
|
||||
}
|
||||
|
||||
// nilDialer is dialer to use when receiver is nil.
|
||||
var nilDialer = *DefaultDialer
|
||||
|
||||
// DialContext creates a new client connection. Use requestHeader to specify the
|
||||
// origin (Origin), subprotocols (Sec-WebSocket-Protocol) and cookies (Cookie).
|
||||
// Use the response.Header to get the selected subprotocol
|
||||
// (Sec-WebSocket-Protocol) and cookies (Set-Cookie).
|
||||
//
|
||||
// The context will be used in the request and in the Dialer.
|
||||
//
|
||||
// If the WebSocket handshake fails, ErrBadHandshake is returned along with a
|
||||
// non-nil *http.Response so that callers can handle redirects, authentication,
|
||||
// etcetera. The response body may not contain the entire response and does not
|
||||
// need to be closed by the application.
|
||||
func (d *Dialer) DialContext(ctx context.Context, urlStr string, requestHeader http.Header) (*Conn, *http.Response, error) {
|
||||
if d == nil {
|
||||
d = &nilDialer
|
||||
}
|
||||
|
||||
challengeKey, err := generateChallengeKey()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
u, err := url.Parse(urlStr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "ws":
|
||||
u.Scheme = "http"
|
||||
case "wss":
|
||||
u.Scheme = "https"
|
||||
default:
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
if u.User != nil {
|
||||
// User name and password are not allowed in websocket URIs.
|
||||
return nil, nil, errMalformedURL
|
||||
}
|
||||
|
||||
req := &http.Request{
|
||||
Method: "GET",
|
||||
URL: u,
|
||||
Proto: "HTTP/1.1",
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
Host: u.Host,
|
||||
}
|
||||
req = req.WithContext(ctx)
|
||||
|
||||
// Set the cookies present in the cookie jar of the dialer
|
||||
if d.Jar != nil {
|
||||
for _, cookie := range d.Jar.Cookies(u) {
|
||||
req.AddCookie(cookie)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the request headers using the capitalization for names and values in
|
||||
// RFC examples. Although the capitalization shouldn't matter, there are
|
||||
// servers that depend on it. The Header.Set method is not used because the
|
||||
// method canonicalizes the header names.
|
||||
req.Header["Upgrade"] = []string{"websocket"}
|
||||
req.Header["Connection"] = []string{"Upgrade"}
|
||||
req.Header["Sec-WebSocket-Key"] = []string{challengeKey}
|
||||
req.Header["Sec-WebSocket-Version"] = []string{"13"}
|
||||
if len(d.Subprotocols) > 0 {
|
||||
req.Header["Sec-WebSocket-Protocol"] = []string{strings.Join(d.Subprotocols, ", ")}
|
||||
}
|
||||
for k, vs := range requestHeader {
|
||||
switch {
|
||||
case k == "Host":
|
||||
if len(vs) > 0 {
|
||||
req.Host = vs[0]
|
||||
}
|
||||
case k == "Upgrade" ||
|
||||
k == "Connection" ||
|
||||
k == "Sec-Websocket-Key" ||
|
||||
k == "Sec-Websocket-Version" ||
|
||||
k == "Sec-Websocket-Extensions" ||
|
||||
(k == "Sec-Websocket-Protocol" && len(d.Subprotocols) > 0):
|
||||
return nil, nil, errors.New("websocket: duplicate header not allowed: " + k)
|
||||
case k == "Sec-Websocket-Protocol":
|
||||
req.Header["Sec-WebSocket-Protocol"] = vs
|
||||
default:
|
||||
req.Header[k] = vs
|
||||
}
|
||||
}
|
||||
|
||||
//if d.EnableCompression {
|
||||
// req.Header["Sec-WebSocket-Extensions"] = []string{"permessage-deflate; server_no_context_takeover; client_no_context_takeover"}
|
||||
//}
|
||||
|
||||
if d.HandshakeTimeout != 0 {
|
||||
var cancel func()
|
||||
ctx, cancel = context.WithTimeout(ctx, d.HandshakeTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
// Get network dial function.
|
||||
var netDial func(network, add string) (net.Conn, error)
|
||||
|
||||
if d.NetDialContext != nil {
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return d.NetDialContext(ctx, network, addr)
|
||||
}
|
||||
} else if d.NetDial != nil {
|
||||
netDial = d.NetDial
|
||||
} else {
|
||||
netDialer := &net.Dialer{}
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
return netDialer.DialContext(ctx, network, addr)
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to set the connection deadline.
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
forwardDial := netDial
|
||||
netDial = func(network, addr string) (net.Conn, error) {
|
||||
c, err := forwardDial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = c.SetDeadline(deadline)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If needed, wrap the dial function to connect through a proxy.
|
||||
/*
|
||||
if d.Proxy != nil {
|
||||
proxyURL, err := d.Proxy(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if proxyURL != nil {
|
||||
dialer, err := proxy_FromURL(proxyURL, netDialerFunc(netDial))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
netDial = dialer.Dial
|
||||
}
|
||||
}*/
|
||||
|
||||
hostPort, hostNoPort := hostPortNoPort(u)
|
||||
//trace := httptrace.ContextClientTrace(ctx)
|
||||
//if trace != nil && trace.GetConn != nil {
|
||||
// trace.GetConn(hostPort)
|
||||
//}
|
||||
|
||||
netConn, err := netDial("tcp", hostPort)
|
||||
//if trace != nil && trace.GotConn != nil {
|
||||
// trace.GotConn(httptrace.GotConnInfo{
|
||||
// Conn: netConn,
|
||||
// })
|
||||
//}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if netConn != nil {
|
||||
netConn.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
if u.Scheme == "https" {
|
||||
cfg := cloneTLSConfig(d.TLSClientConfig)
|
||||
if cfg.ServerName == "" {
|
||||
cfg.ServerName = hostNoPort
|
||||
}
|
||||
tlsConn := tls.Client(netConn, cfg)
|
||||
netConn = tlsConn
|
||||
|
||||
var err error
|
||||
//if trace != nil {
|
||||
// err = doHandshakeWithTrace(trace, tlsConn, cfg)
|
||||
//} else {
|
||||
err = doHandshake(tlsConn, cfg)
|
||||
//}
|
||||
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
conn := newConn(netConn, false, d.ReadBufferSize, d.WriteBufferSize, d.WriteBufferPool, nil, nil)
|
||||
|
||||
if err := req.Write(netConn); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
//if trace != nil && trace.GotFirstResponseByte != nil {
|
||||
// if peek, err := conn.br.Peek(1); err == nil && len(peek) == 1 {
|
||||
// trace.GotFirstResponseByte()
|
||||
// }
|
||||
//}
|
||||
|
||||
resp, err := http.ReadResponse(conn.br, req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if d.Jar != nil {
|
||||
if rc := resp.Cookies(); len(rc) > 0 {
|
||||
d.Jar.SetCookies(u, rc)
|
||||
}
|
||||
}
|
||||
|
||||
if resp.StatusCode != 101 ||
|
||||
!strings.EqualFold(resp.Header.Get("Upgrade"), "websocket") ||
|
||||
!strings.EqualFold(resp.Header.Get("Connection"), "upgrade") ||
|
||||
resp.Header.Get("Sec-Websocket-Accept") != computeAcceptKey(challengeKey) {
|
||||
// Before closing the network connection on return from this
|
||||
// function, slurp up some of the response to aid application
|
||||
// debugging.
|
||||
buf := make([]byte, 1024)
|
||||
n, _ := io.ReadFull(resp.Body, buf)
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader(buf[:n]))
|
||||
return nil, resp, ErrBadHandshake
|
||||
}
|
||||
|
||||
/*
|
||||
for _, ext := range parseExtensions(resp.Header) {
|
||||
if ext[""] != "permessage-deflate" {
|
||||
continue
|
||||
}
|
||||
_, snct := ext["server_no_context_takeover"]
|
||||
_, cnct := ext["client_no_context_takeover"]
|
||||
if !snct || !cnct {
|
||||
return nil, resp, errInvalidCompression
|
||||
}
|
||||
conn.newCompressionWriter = compressNoContextTakeover
|
||||
conn.newDecompressionReader = decompressNoContextTakeover
|
||||
break
|
||||
}*/
|
||||
|
||||
resp.Body = ioutil.NopCloser(bytes.NewReader([]byte{}))
|
||||
conn.subprotocol = resp.Header.Get("Sec-Websocket-Protocol")
|
||||
|
||||
netConn.SetDeadline(time.Time{})
|
||||
netConn = nil // to avoid close in defer.
|
||||
return conn, resp, nil
|
||||
}
|
||||
|
||||
func doHandshake(tlsConn *tls.Conn, cfg *tls.Config) error {
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !cfg.InsecureSkipVerify {
|
||||
if err := tlsConn.VerifyHostname(cfg.ServerName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "crypto/tls"
|
||||
|
||||
func cloneTLSConfig(cfg *tls.Config) *tls.Config {
|
||||
if cfg == nil {
|
||||
return &tls.Config{}
|
||||
}
|
||||
return cfg.Clone()
|
||||
}
|
1181
external/github.com/gorilla/websocket/conn.go
vendored
1181
external/github.com/gorilla/websocket/conn.go
vendored
File diff suppressed because it is too large
Load Diff
@ -1,15 +0,0 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.8
|
||||
|
||||
package websocket
|
||||
|
||||
import "net"
|
||||
|
||||
func (c *Conn) writeBufs(bufs ...[]byte) error {
|
||||
b := net.Buffers(bufs)
|
||||
_, err := b.WriteTo(c.conn)
|
||||
return err
|
||||
}
|
180
external/github.com/gorilla/websocket/doc.go
vendored
180
external/github.com/gorilla/websocket/doc.go
vendored
@ -1,180 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package websocket implements the WebSocket protocol defined in RFC 6455.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// The Conn type represents a WebSocket connection. A server application calls
|
||||
// the Upgrader.Upgrade method from an HTTP request handler to get a *Conn:
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// ReadBufferSize: 1024,
|
||||
// WriteBufferSize: 1024,
|
||||
// }
|
||||
//
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// conn, err := upgrader.Upgrade(w, r, nil)
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// ... Use conn to send and receive messages.
|
||||
// }
|
||||
//
|
||||
// Call the connection's WriteMessage and ReadMessage methods to send and
|
||||
// receive messages as a slice of bytes. This snippet of code shows how to echo
|
||||
// messages using these methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, p, err := conn.ReadMessage()
|
||||
// if err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// if err := conn.WriteMessage(messageType, p); err != nil {
|
||||
// log.Println(err)
|
||||
// return
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// In above snippet of code, p is a []byte and messageType is an int with value
|
||||
// websocket.BinaryMessage or websocket.TextMessage.
|
||||
//
|
||||
// An application can also send and receive messages using the io.WriteCloser
|
||||
// and io.Reader interfaces. To send a message, call the connection NextWriter
|
||||
// method to get an io.WriteCloser, write the message to the writer and close
|
||||
// the writer when done. To receive a message, call the connection NextReader
|
||||
// method to get an io.Reader and read until io.EOF is returned. This snippet
|
||||
// shows how to echo messages using the NextWriter and NextReader methods:
|
||||
//
|
||||
// for {
|
||||
// messageType, r, err := conn.NextReader()
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
// w, err := conn.NextWriter(messageType)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := io.Copy(w, r); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := w.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Data Messages
|
||||
//
|
||||
// The WebSocket protocol distinguishes between text and binary data messages.
|
||||
// Text messages are interpreted as UTF-8 encoded text. The interpretation of
|
||||
// binary messages is left to the application.
|
||||
//
|
||||
// This package uses the TextMessage and BinaryMessage integer constants to
|
||||
// identify the two data message types. The ReadMessage and NextReader methods
|
||||
// return the type of the received message. The messageType argument to the
|
||||
// WriteMessage and NextWriter methods specifies the type of a sent message.
|
||||
//
|
||||
// It is the application's responsibility to ensure that text messages are
|
||||
// valid UTF-8 encoded text.
|
||||
//
|
||||
// Control Messages
|
||||
//
|
||||
// The WebSocket protocol defines three types of control messages: close, ping
|
||||
// and pong. Call the connection WriteControl, WriteMessage or NextWriter
|
||||
// methods to send a control message to the peer.
|
||||
//
|
||||
// Connections handle received close messages by calling the handler function
|
||||
// set with the SetCloseHandler method and by returning a *CloseError from the
|
||||
// NextReader, ReadMessage or the message Read method. The default close
|
||||
// handler sends a close message to the peer.
|
||||
//
|
||||
// Connections handle received ping messages by calling the handler function
|
||||
// set with the SetPingHandler method. The default ping handler sends a pong
|
||||
// message to the peer.
|
||||
//
|
||||
// Connections handle received pong messages by calling the handler function
|
||||
// set with the SetPongHandler method. The default pong handler does nothing.
|
||||
// If an application sends ping messages, then the application should set a
|
||||
// pong handler to receive the corresponding pong.
|
||||
//
|
||||
// The control message handler functions are called from the NextReader,
|
||||
// ReadMessage and message reader Read methods. The default close and ping
|
||||
// handlers can block these methods for a short time when the handler writes to
|
||||
// the connection.
|
||||
//
|
||||
// The application must read the connection to process close, ping and pong
|
||||
// messages sent from the peer. If the application is not otherwise interested
|
||||
// in messages from the peer, then the application should start a goroutine to
|
||||
// read and discard messages from the peer. A simple example is:
|
||||
//
|
||||
// func readLoop(c *websocket.Conn) {
|
||||
// for {
|
||||
// if _, _, err := c.NextReader(); err != nil {
|
||||
// c.Close()
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Concurrency
|
||||
//
|
||||
// Connections support one concurrent reader and one concurrent writer.
|
||||
//
|
||||
// Applications are responsible for ensuring that no more than one goroutine
|
||||
// calls the write methods (NextWriter, SetWriteDeadline, WriteMessage,
|
||||
// WriteJSON, EnableWriteCompression, SetCompressionLevel) concurrently and
|
||||
// that no more than one goroutine calls the read methods (NextReader,
|
||||
// SetReadDeadline, ReadMessage, ReadJSON, SetPongHandler, SetPingHandler)
|
||||
// concurrently.
|
||||
//
|
||||
// The Close and WriteControl methods can be called concurrently with all other
|
||||
// methods.
|
||||
//
|
||||
// Origin Considerations
|
||||
//
|
||||
// Web browsers allow Javascript applications to open a WebSocket connection to
|
||||
// any host. It's up to the server to enforce an origin policy using the Origin
|
||||
// request header sent by the browser.
|
||||
//
|
||||
// The Upgrader calls the function specified in the CheckOrigin field to check
|
||||
// the origin. If the CheckOrigin function returns false, then the Upgrade
|
||||
// method fails the WebSocket handshake with HTTP status 403.
|
||||
//
|
||||
// If the CheckOrigin field is nil, then the Upgrader uses a safe default: fail
|
||||
// the handshake if the Origin request header is present and the Origin host is
|
||||
// not equal to the Host request header.
|
||||
//
|
||||
// The deprecated package-level Upgrade function does not perform origin
|
||||
// checking. The application is responsible for checking the Origin header
|
||||
// before calling the Upgrade function.
|
||||
//
|
||||
// Compression EXPERIMENTAL
|
||||
//
|
||||
// Per message compression extensions (RFC 7692) are experimentally supported
|
||||
// by this package in a limited capacity. Setting the EnableCompression option
|
||||
// to true in Dialer or Upgrader will attempt to negotiate per message deflate
|
||||
// support.
|
||||
//
|
||||
// var upgrader = websocket.Upgrader{
|
||||
// EnableCompression: true,
|
||||
// }
|
||||
//
|
||||
// If compression was successfully negotiated with the connection's peer, any
|
||||
// message received in compressed form will be automatically decompressed.
|
||||
// All Read methods will return uncompressed bytes.
|
||||
//
|
||||
// Per message compression of messages written to a connection can be enabled
|
||||
// or disabled by calling the corresponding Conn method:
|
||||
//
|
||||
// conn.EnableWriteCompression(false)
|
||||
//
|
||||
// Currently this package does not support compression with "context takeover".
|
||||
// This means that messages must be compressed and decompressed in isolation,
|
||||
// without retaining sliding window or dictionary state across messages. For
|
||||
// more details refer to RFC 7692.
|
||||
//
|
||||
// Use of compression is experimental and may result in decreased performance.
|
||||
package websocket
|
54
external/github.com/gorilla/websocket/mask.go
vendored
54
external/github.com/gorilla/websocket/mask.go
vendored
@ -1,54 +0,0 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package websocket
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const wordSize = int(unsafe.Sizeof(uintptr(0)))
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
// Mask one byte at a time for small buffers.
|
||||
if len(b) < 2*wordSize {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
||||
|
||||
// Mask one byte at a time to word boundary.
|
||||
if n := int(uintptr(unsafe.Pointer(&b[0]))) % wordSize; n != 0 {
|
||||
n = wordSize - n
|
||||
for i := range b[:n] {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
b = b[n:]
|
||||
}
|
||||
|
||||
// Create aligned word size key.
|
||||
var k [wordSize]byte
|
||||
for i := range k {
|
||||
k[i] = key[(pos+i)&3]
|
||||
}
|
||||
kw := *(*uintptr)(unsafe.Pointer(&k))
|
||||
|
||||
// Mask one word at a time.
|
||||
n := (len(b) / wordSize) * wordSize
|
||||
for i := 0; i < n; i += wordSize {
|
||||
*(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(i))) ^= kw
|
||||
}
|
||||
|
||||
// Mask one byte at a time for remaining bytes.
|
||||
b = b[n:]
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
|
||||
return pos & 3
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
// Copyright 2016 The Gorilla WebSocket Authors. All rights reserved. Use of
|
||||
// this source code is governed by a BSD-style license that can be found in the
|
||||
// LICENSE file.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package websocket
|
||||
|
||||
func maskBytes(key [4]byte, pos int, b []byte) int {
|
||||
for i := range b {
|
||||
b[i] ^= key[pos&3]
|
||||
pos++
|
||||
}
|
||||
return pos & 3
|
||||
}
|
363
external/github.com/gorilla/websocket/server.go
vendored
363
external/github.com/gorilla/websocket/server.go
vendored
@ -1,363 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// HandshakeError describes an error with the handshake from the peer.
|
||||
type HandshakeError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e HandshakeError) Error() string { return e.message }
|
||||
|
||||
// Upgrader specifies parameters for upgrading an HTTP connection to a
|
||||
// WebSocket connection.
|
||||
type Upgrader struct {
|
||||
// HandshakeTimeout specifies the duration for the handshake to complete.
|
||||
HandshakeTimeout time.Duration
|
||||
|
||||
// ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer
|
||||
// size is zero, then buffers allocated by the HTTP server are used. The
|
||||
// I/O buffer sizes do not limit the size of the messages that can be sent
|
||||
// or received.
|
||||
ReadBufferSize, WriteBufferSize int
|
||||
|
||||
// WriteBufferPool is a pool of buffers for write operations. If the value
|
||||
// is not set, then write buffers are allocated to the connection for the
|
||||
// lifetime of the connection.
|
||||
//
|
||||
// A pool is most useful when the application has a modest volume of writes
|
||||
// across a large number of connections.
|
||||
//
|
||||
// Applications should use a single pool for each unique value of
|
||||
// WriteBufferSize.
|
||||
WriteBufferPool BufferPool
|
||||
|
||||
// Subprotocols specifies the server's supported protocols in order of
|
||||
// preference. If this field is not nil, then the Upgrade method negotiates a
|
||||
// subprotocol by selecting the first match in this list with a protocol
|
||||
// requested by the client. If there's no match, then no protocol is
|
||||
// negotiated (the Sec-Websocket-Protocol header is not included in the
|
||||
// handshake response).
|
||||
Subprotocols []string
|
||||
|
||||
// Error specifies the function for generating HTTP error responses. If Error
|
||||
// is nil, then http.Error is used to generate the HTTP response.
|
||||
Error func(w http.ResponseWriter, r *http.Request, status int, reason error)
|
||||
|
||||
// CheckOrigin returns true if the request Origin header is acceptable. If
|
||||
// CheckOrigin is nil, then a safe default is used: return false if the
|
||||
// Origin request header is present and the origin host is not equal to
|
||||
// request Host header.
|
||||
//
|
||||
// A CheckOrigin function should carefully validate the request origin to
|
||||
// prevent cross-site request forgery.
|
||||
CheckOrigin func(r *http.Request) bool
|
||||
|
||||
// EnableCompression specify if the server should attempt to negotiate per
|
||||
// message compression (RFC 7692). Setting this value to true does not
|
||||
// guarantee that compression will be supported. Currently only "no context
|
||||
// takeover" modes are supported.
|
||||
// EnableCompression bool
|
||||
}
|
||||
|
||||
func (u *Upgrader) returnError(w http.ResponseWriter, r *http.Request, status int, reason string) (*Conn, error) {
|
||||
err := HandshakeError{reason}
|
||||
if u.Error != nil {
|
||||
u.Error(w, r, status, err)
|
||||
} else {
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
http.Error(w, http.StatusText(status), status)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// checkSameOrigin returns true if the origin is not set or is equal to the request host.
|
||||
func checkSameOrigin(r *http.Request) bool {
|
||||
origin := r.Header["Origin"]
|
||||
if len(origin) == 0 {
|
||||
return true
|
||||
}
|
||||
u, err := url.Parse(origin[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return equalASCIIFold(u.Host, r.Host)
|
||||
}
|
||||
|
||||
func (u *Upgrader) selectSubprotocol(r *http.Request, responseHeader http.Header) string {
|
||||
if u.Subprotocols != nil {
|
||||
clientProtocols := Subprotocols(r)
|
||||
for _, serverProtocol := range u.Subprotocols {
|
||||
for _, clientProtocol := range clientProtocols {
|
||||
if clientProtocol == serverProtocol {
|
||||
return clientProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if responseHeader != nil {
|
||||
return responseHeader.Get("Sec-Websocket-Protocol")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// application negotiated subprotocol (Sec-WebSocket-Protocol).
|
||||
//
|
||||
// If the upgrade fails, then Upgrade replies to the client with an HTTP error
|
||||
// response.
|
||||
func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header) (*Conn, error) {
|
||||
const badHandshake = "websocket: the client is not using the websocket protocol: "
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Connection", "upgrade") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'upgrade' token not found in 'Connection' header")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Upgrade", "websocket") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, badHandshake+"'websocket' token not found in 'Upgrade' header")
|
||||
}
|
||||
|
||||
if r.Method != "GET" {
|
||||
return u.returnError(w, r, http.StatusMethodNotAllowed, badHandshake+"request method is not GET")
|
||||
}
|
||||
|
||||
if !tokenListContainsValue(r.Header, "Sec-Websocket-Version", "13") {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: unsupported version: 13 not found in 'Sec-Websocket-Version' header")
|
||||
}
|
||||
|
||||
if _, ok := responseHeader["Sec-Websocket-Extensions"]; ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: application specific 'Sec-WebSocket-Extensions' headers are unsupported")
|
||||
}
|
||||
|
||||
checkOrigin := u.CheckOrigin
|
||||
if checkOrigin == nil {
|
||||
checkOrigin = checkSameOrigin
|
||||
}
|
||||
if !checkOrigin(r) {
|
||||
return u.returnError(w, r, http.StatusForbidden, "websocket: request origin not allowed by Upgrader.CheckOrigin")
|
||||
}
|
||||
|
||||
challengeKey := r.Header.Get("Sec-Websocket-Key")
|
||||
if challengeKey == "" {
|
||||
return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank")
|
||||
}
|
||||
|
||||
subprotocol := u.selectSubprotocol(r, responseHeader)
|
||||
|
||||
// Negotiate PMCE
|
||||
const compress bool = false
|
||||
//if u.EnableCompression {
|
||||
// for _, ext := range parseExtensions(r.Header) {
|
||||
// if ext[""] != "permessage-deflate" {
|
||||
// continue
|
||||
// }
|
||||
// compress = true
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
|
||||
h, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, "websocket: response does not implement http.Hijacker")
|
||||
}
|
||||
var brw *bufio.ReadWriter
|
||||
netConn, brw, err := h.Hijack()
|
||||
if err != nil {
|
||||
return u.returnError(w, r, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
if brw.Reader.Buffered() > 0 {
|
||||
netConn.Close()
|
||||
return nil, errors.New("websocket: client sent data before handshake is complete")
|
||||
}
|
||||
|
||||
var br *bufio.Reader
|
||||
if u.ReadBufferSize == 0 && bufioReaderSize(netConn, brw.Reader) > 256 {
|
||||
// Reuse hijacked buffered reader as connection reader.
|
||||
br = brw.Reader
|
||||
}
|
||||
|
||||
buf := bufioWriterBuffer(netConn, brw.Writer)
|
||||
|
||||
var writeBuf []byte
|
||||
if u.WriteBufferPool == nil && u.WriteBufferSize == 0 && len(buf) >= maxFrameHeaderSize+256 {
|
||||
// Reuse hijacked write buffer as connection buffer.
|
||||
writeBuf = buf
|
||||
}
|
||||
|
||||
c := newConn(netConn, true, u.ReadBufferSize, u.WriteBufferSize, u.WriteBufferPool, br, writeBuf)
|
||||
c.subprotocol = subprotocol
|
||||
|
||||
//if compress {
|
||||
// c.newCompressionWriter = compressNoContextTakeover
|
||||
// c.newDecompressionReader = decompressNoContextTakeover
|
||||
//}
|
||||
|
||||
// Use larger of hijacked buffer and connection write buffer for header.
|
||||
p := buf
|
||||
if len(c.writeBuf) > len(p) {
|
||||
p = c.writeBuf
|
||||
}
|
||||
p = p[:0]
|
||||
|
||||
p = append(p, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "...)
|
||||
p = append(p, computeAcceptKey(challengeKey)...)
|
||||
p = append(p, "\r\n"...)
|
||||
if c.subprotocol != "" {
|
||||
p = append(p, "Sec-WebSocket-Protocol: "...)
|
||||
p = append(p, c.subprotocol...)
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
//if compress {
|
||||
// p = append(p, "Sec-WebSocket-Extensions: permessage-deflate; server_no_context_takeover; client_no_context_takeover\r\n"...)
|
||||
//}
|
||||
for k, vs := range responseHeader {
|
||||
if k == "Sec-Websocket-Protocol" {
|
||||
continue
|
||||
}
|
||||
for _, v := range vs {
|
||||
p = append(p, k...)
|
||||
p = append(p, ": "...)
|
||||
for i := 0; i < len(v); i++ {
|
||||
b := v[i]
|
||||
if b <= 31 {
|
||||
// prevent response splitting.
|
||||
b = ' '
|
||||
}
|
||||
p = append(p, b)
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
}
|
||||
}
|
||||
p = append(p, "\r\n"...)
|
||||
|
||||
// Clear deadlines set by HTTP server.
|
||||
netConn.SetDeadline(time.Time{})
|
||||
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Now().Add(u.HandshakeTimeout))
|
||||
}
|
||||
if _, err = netConn.Write(p); err != nil {
|
||||
netConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
if u.HandshakeTimeout > 0 {
|
||||
netConn.SetWriteDeadline(time.Time{})
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Upgrade upgrades the HTTP server connection to the WebSocket protocol.
|
||||
//
|
||||
// Deprecated: Use websocket.Upgrader instead.
|
||||
//
|
||||
// Upgrade does not perform origin checking. The application is responsible for
|
||||
// checking the Origin header before calling Upgrade. An example implementation
|
||||
// of the same origin policy check is:
|
||||
//
|
||||
// if req.Header.Get("Origin") != "http://"+req.Host {
|
||||
// http.Error(w, "Origin not allowed", http.StatusForbidden)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// If the endpoint supports subprotocols, then the application is responsible
|
||||
// for negotiating the protocol used on the connection. Use the Subprotocols()
|
||||
// function to get the subprotocols requested by the client. Use the
|
||||
// Sec-Websocket-Protocol response header to specify the subprotocol selected
|
||||
// by the application.
|
||||
//
|
||||
// The responseHeader is included in the response to the client's upgrade
|
||||
// request. Use the responseHeader to specify cookies (Set-Cookie) and the
|
||||
// negotiated subprotocol (Sec-Websocket-Protocol).
|
||||
//
|
||||
// The connection buffers IO to the underlying network connection. The
|
||||
// readBufSize and writeBufSize parameters specify the size of the buffers to
|
||||
// use. Messages can be larger than the buffers.
|
||||
//
|
||||
// If the request is not a valid WebSocket handshake, then Upgrade returns an
|
||||
// error of type HandshakeError. Applications should handle this error by
|
||||
// replying to the client with an HTTP error response.
|
||||
func Upgrade(w http.ResponseWriter, r *http.Request, responseHeader http.Header, readBufSize, writeBufSize int) (*Conn, error) {
|
||||
u := Upgrader{ReadBufferSize: readBufSize, WriteBufferSize: writeBufSize}
|
||||
u.Error = func(w http.ResponseWriter, r *http.Request, status int, reason error) {
|
||||
// don't return errors to maintain backwards compatibility
|
||||
}
|
||||
u.CheckOrigin = func(r *http.Request) bool {
|
||||
// allow all connections by default
|
||||
return true
|
||||
}
|
||||
return u.Upgrade(w, r, responseHeader)
|
||||
}
|
||||
|
||||
// Subprotocols returns the subprotocols requested by the client in the
|
||||
// Sec-Websocket-Protocol header.
|
||||
func Subprotocols(r *http.Request) []string {
|
||||
h := strings.TrimSpace(r.Header.Get("Sec-Websocket-Protocol"))
|
||||
if h == "" {
|
||||
return nil
|
||||
}
|
||||
protocols := strings.Split(h, ",")
|
||||
for i := range protocols {
|
||||
protocols[i] = strings.TrimSpace(protocols[i])
|
||||
}
|
||||
return protocols
|
||||
}
|
||||
|
||||
// IsWebSocketUpgrade returns true if the client requested upgrade to the
|
||||
// WebSocket protocol.
|
||||
func IsWebSocketUpgrade(r *http.Request) bool {
|
||||
return tokenListContainsValue(r.Header, "Connection", "upgrade") &&
|
||||
tokenListContainsValue(r.Header, "Upgrade", "websocket")
|
||||
}
|
||||
|
||||
// bufioReaderSize size returns the size of a bufio.Reader.
|
||||
func bufioReaderSize(originalReader io.Reader, br *bufio.Reader) int {
|
||||
// This code assumes that peek on a reset reader returns
|
||||
// bufio.Reader.buf[:0].
|
||||
// TODO: Use bufio.Reader.Size() after Go 1.10
|
||||
br.Reset(originalReader)
|
||||
if p, err := br.Peek(0); err == nil {
|
||||
return cap(p)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// writeHook is an io.Writer that records the last slice passed to it vio
|
||||
// io.Writer.Write.
|
||||
type writeHook struct {
|
||||
p []byte
|
||||
}
|
||||
|
||||
func (wh *writeHook) Write(p []byte) (int, error) {
|
||||
wh.p = p
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// bufioWriterBuffer grabs the buffer from a bufio.Writer.
|
||||
func bufioWriterBuffer(originalWriter io.Writer, bw *bufio.Writer) []byte {
|
||||
// This code assumes that bufio.Writer.buf[:1] is passed to the
|
||||
// bufio.Writer's underlying writer.
|
||||
var wh writeHook
|
||||
bw.Reset(&wh)
|
||||
bw.WriteByte(0)
|
||||
bw.Flush()
|
||||
|
||||
bw.Reset(originalWriter)
|
||||
|
||||
return wh.p[:cap(wh.p)]
|
||||
}
|
283
external/github.com/gorilla/websocket/util.go
vendored
283
external/github.com/gorilla/websocket/util.go
vendored
@ -1,283 +0,0 @@
|
||||
// Copyright 2013 The Gorilla WebSocket Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||
|
||||
func computeAcceptKey(challengeKey string) string {
|
||||
h := sha1.New()
|
||||
h.Write([]byte(challengeKey))
|
||||
h.Write(keyGUID)
|
||||
return base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func generateChallengeKey() (string, error) {
|
||||
p := make([]byte, 16)
|
||||
if _, err := io.ReadFull(rand.Reader, p); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(p), nil
|
||||
}
|
||||
|
||||
// Token octets per RFC 2616.
|
||||
var isTokenOctet = [256]bool{
|
||||
'!': true,
|
||||
'#': true,
|
||||
'$': true,
|
||||
'%': true,
|
||||
'&': true,
|
||||
'\'': true,
|
||||
'*': true,
|
||||
'+': true,
|
||||
'-': true,
|
||||
'.': true,
|
||||
'0': true,
|
||||
'1': true,
|
||||
'2': true,
|
||||
'3': true,
|
||||
'4': true,
|
||||
'5': true,
|
||||
'6': true,
|
||||
'7': true,
|
||||
'8': true,
|
||||
'9': true,
|
||||
'A': true,
|
||||
'B': true,
|
||||
'C': true,
|
||||
'D': true,
|
||||
'E': true,
|
||||
'F': true,
|
||||
'G': true,
|
||||
'H': true,
|
||||
'I': true,
|
||||
'J': true,
|
||||
'K': true,
|
||||
'L': true,
|
||||
'M': true,
|
||||
'N': true,
|
||||
'O': true,
|
||||
'P': true,
|
||||
'Q': true,
|
||||
'R': true,
|
||||
'S': true,
|
||||
'T': true,
|
||||
'U': true,
|
||||
'W': true,
|
||||
'V': true,
|
||||
'X': true,
|
||||
'Y': true,
|
||||
'Z': true,
|
||||
'^': true,
|
||||
'_': true,
|
||||
'`': true,
|
||||
'a': true,
|
||||
'b': true,
|
||||
'c': true,
|
||||
'd': true,
|
||||
'e': true,
|
||||
'f': true,
|
||||
'g': true,
|
||||
'h': true,
|
||||
'i': true,
|
||||
'j': true,
|
||||
'k': true,
|
||||
'l': true,
|
||||
'm': true,
|
||||
'n': true,
|
||||
'o': true,
|
||||
'p': true,
|
||||
'q': true,
|
||||
'r': true,
|
||||
's': true,
|
||||
't': true,
|
||||
'u': true,
|
||||
'v': true,
|
||||
'w': true,
|
||||
'x': true,
|
||||
'y': true,
|
||||
'z': true,
|
||||
'|': true,
|
||||
'~': true,
|
||||
}
|
||||
|
||||
// skipSpace returns a slice of the string s with all leading RFC 2616 linear
|
||||
// whitespace removed.
|
||||
func skipSpace(s string) (rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if b := s[i]; b != ' ' && b != '\t' {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[i:]
|
||||
}
|
||||
|
||||
// nextToken returns the leading RFC 2616 token of s and the string following
|
||||
// the token.
|
||||
func nextToken(s string) (token, rest string) {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
if !isTokenOctet[s[i]] {
|
||||
break
|
||||
}
|
||||
}
|
||||
return s[:i], s[i:]
|
||||
}
|
||||
|
||||
// nextTokenOrQuoted returns the leading token or quoted string per RFC 2616
|
||||
// and the string following the token or quoted string.
|
||||
func nextTokenOrQuoted(s string) (value string, rest string) {
|
||||
if !strings.HasPrefix(s, "\"") {
|
||||
return nextToken(s)
|
||||
}
|
||||
s = s[1:]
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"':
|
||||
return s[:i], s[i+1:]
|
||||
case '\\':
|
||||
p := make([]byte, len(s)-1)
|
||||
j := copy(p, s[:i])
|
||||
escape := true
|
||||
for i = i + 1; i < len(s); i++ {
|
||||
b := s[i]
|
||||
switch {
|
||||
case escape:
|
||||
escape = false
|
||||
p[j] = b
|
||||
j++
|
||||
case b == '\\':
|
||||
escape = true
|
||||
case b == '"':
|
||||
return string(p[:j]), s[i+1:]
|
||||
default:
|
||||
p[j] = b
|
||||
j++
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
}
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// equalASCIIFold returns true if s is equal to t with ASCII case folding as
|
||||
// defined in RFC 4790.
|
||||
func equalASCIIFold(s, t string) bool {
|
||||
for s != "" && t != "" {
|
||||
sr, size := utf8.DecodeRuneInString(s)
|
||||
s = s[size:]
|
||||
tr, size := utf8.DecodeRuneInString(t)
|
||||
t = t[size:]
|
||||
if sr == tr {
|
||||
continue
|
||||
}
|
||||
if 'A' <= sr && sr <= 'Z' {
|
||||
sr = sr + 'a' - 'A'
|
||||
}
|
||||
if 'A' <= tr && tr <= 'Z' {
|
||||
tr = tr + 'a' - 'A'
|
||||
}
|
||||
if sr != tr {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return s == t
|
||||
}
|
||||
|
||||
// tokenListContainsValue returns true if the 1#token header with the given
|
||||
// name contains a token equal to value with ASCII case folding.
|
||||
func tokenListContainsValue(header http.Header, name string, value string) bool {
|
||||
headers:
|
||||
for _, s := range header[name] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
if equalASCIIFold(t, value) {
|
||||
return true
|
||||
}
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseExtensions parses WebSocket extensions from a header.
|
||||
func parseExtensions(header http.Header) []map[string]string {
|
||||
// From RFC 6455:
|
||||
//
|
||||
// Sec-WebSocket-Extensions = extension-list
|
||||
// extension-list = 1#extension
|
||||
// extension = extension-token *( ";" extension-param )
|
||||
// extension-token = registered-token
|
||||
// registered-token = token
|
||||
// extension-param = token [ "=" (token | quoted-string) ]
|
||||
// ;When using the quoted-string syntax variant, the value
|
||||
// ;after quoted-string unescaping MUST conform to the
|
||||
// ;'token' ABNF.
|
||||
|
||||
var result []map[string]string
|
||||
headers:
|
||||
for _, s := range header["Sec-Websocket-Extensions"] {
|
||||
for {
|
||||
var t string
|
||||
t, s = nextToken(skipSpace(s))
|
||||
if t == "" {
|
||||
continue headers
|
||||
}
|
||||
ext := map[string]string{"": t}
|
||||
for {
|
||||
s = skipSpace(s)
|
||||
if !strings.HasPrefix(s, ";") {
|
||||
break
|
||||
}
|
||||
var k string
|
||||
k, s = nextToken(skipSpace(s[1:]))
|
||||
if k == "" {
|
||||
continue headers
|
||||
}
|
||||
s = skipSpace(s)
|
||||
var v string
|
||||
if strings.HasPrefix(s, "=") {
|
||||
v, s = nextTokenOrQuoted(skipSpace(s[1:]))
|
||||
s = skipSpace(s)
|
||||
}
|
||||
if s != "" && s[0] != ',' && s[0] != ';' {
|
||||
continue headers
|
||||
}
|
||||
ext[k] = v
|
||||
}
|
||||
if s != "" && s[0] != ',' {
|
||||
continue headers
|
||||
}
|
||||
result = append(result, ext)
|
||||
if s == "" {
|
||||
continue headers
|
||||
}
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
# How to Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution,
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult
|
||||
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
||||
information on using pull requests.
|
@ -1,69 +0,0 @@
|
||||
# How this package works
|
||||
### Chapter 1: [Making private things public](./u_public.go)
|
||||
There are numerous handshake-related structs in crypto/tls, most of which are either private or have private fields.
|
||||
One of them — `clientHandshakeState` — has private function `handshake()`,
|
||||
which is called in the beginning of default handshake.
|
||||
Unfortunately, user will not be able to directly access this struct outside of tls package.
|
||||
As a result, we decided to employ following workaround: declare public copies of private structs.
|
||||
Now user is free to manipulate fields of public `ClientHandshakeState`.
|
||||
Then, right before handshake, we can shallow-copy public state into private `clientHandshakeState`,
|
||||
call `handshake()` on it and carry on with default Golang handshake process.
|
||||
After handshake is done we shallow-copy private state back to public, allowing user to read results of handshake.
|
||||
|
||||
### Chapter 2: [TLSExtension](./u_tls_extensions.go)
|
||||
The way we achieve reasonable flexibilty with extensions is inspired by
|
||||
[ztls'](https://github.com/zmap/zcrypto/blob/master/tls/handshake_extensions.go) design.
|
||||
However, our design has several differences, so we wrote it from scratch.
|
||||
This design allows us to have an array of `TLSExtension` objects and then marshal them in order:
|
||||
```Golang
|
||||
type TLSExtension interface {
|
||||
writeToUConn(*UConn) error
|
||||
|
||||
Len() int // includes header
|
||||
|
||||
// Read reads up to len(p) bytes into p.
|
||||
// It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
|
||||
Read(p []byte) (n int, err error) // implements io.Reader
|
||||
}
|
||||
```
|
||||
`writeToUConn()` applies appropriate per-extension changes to `UConn`.
|
||||
|
||||
`Len()` provides the size of marshaled extension, so we can allocate appropriate buffer beforehand,
|
||||
catch out-of-bound errors easily and guide size-dependent extensions such as padding.
|
||||
|
||||
`Read(buffer []byte)` _writes(see: io.Reader interface)_ marshaled extensions into provided buffer.
|
||||
This avoids extra allocations.
|
||||
|
||||
### Chapter 3: [UConn](./u_conn.go)
|
||||
`UConn` extends standard `tls.Conn`. Most notably, it stores slice with `TLSExtension`s and public
|
||||
`ClientHandshakeState`.
|
||||
Whenever `UConn.BuildHandshakeState()` gets called (happens automatically in `UConn.Handshake()`
|
||||
or could be called manually), config will be applied according to chosen `ClientHelloID`.
|
||||
From contributor's view there are 2 main behaviors:
|
||||
* `HelloGolang` simply calls default Golang's [`makeClientHello()`](./handshake_client.go)
|
||||
and directly stores it into `HandshakeState.Hello`. utls-specific stuff is ignored.
|
||||
* Other ClientHelloIDs fill `UConn.Hello.{Random, CipherSuites, CompressionMethods}` and `UConn.Extensions` with
|
||||
per-parrot setup, which then gets applied to appropriate standard tls structs,
|
||||
and then marshaled by utls into `HandshakeState.Hello`.
|
||||
|
||||
### Chapter 4: Tests
|
||||
|
||||
Tests exist, but coverage is very limited. What's covered is a conjunction of
|
||||
* TLS 1.2
|
||||
* Working parrots without any unsupported extensions (only Android 5.1 at this time)
|
||||
* Ciphersuites offered by parrot.
|
||||
* Ciphersuites supported by Golang
|
||||
* Simple conversation with reference implementation of OpenSSL.
|
||||
(e.g. no automatic checks for renegotiations, parroting quality and such)
|
||||
|
||||
plus we test some other minor things.
|
||||
Basically, current tests aim to provide a sanity check.
|
||||
|
||||
# Merging upstream
|
||||
```Bash
|
||||
git remote add -f golang git@github.com:golang/go.git
|
||||
git checkout -b golang-upstream golang/master
|
||||
git subtree split -P src/crypto/tls/ -b golang-tls-upstream
|
||||
git checkout master
|
||||
git merge --no-commit golang-tls-upstream
|
||||
```
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,161 +0,0 @@
|
||||
# uTLS
|
||||
[![Build Status](https://travis-ci.org/refraction-networking/utls.svg?branch=master)](https://travis-ci.org/refraction-networking/utls)
|
||||
[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/refraction-networking/utls#UConn)
|
||||
---
|
||||
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance, low-level access to handshake, fake session tickets and some other features. Handshake is still performed by "crypto/tls", this library merely changes ClientHello part of it and provides low-level access.
|
||||
Golang 1.11+ is required.
|
||||
If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu
|
||||
# Features
|
||||
## Low-level access to handshake
|
||||
* Read/write access to all bits of client hello message.
|
||||
* Read access to fields of ClientHandshakeState, which, among other things, includes ServerHello and MasterSecret.
|
||||
* Read keystream. Can be used, for example, to "write" something in ciphertext.
|
||||
## ClientHello fingerprinting resistance
|
||||
Golang's ClientHello has a very unique fingerprint, which especially sticks out on mobile clients,
|
||||
where Golang is not too popular yet.
|
||||
Some members of anti-censorship community are concerned that their tools could be trivially blocked based on
|
||||
ClientHello with relatively small collateral damage. There are multiple solutions to this issue.
|
||||
### Randomized handshake
|
||||
This package can generate randomized ClientHello using only extensions and cipherSuites "crypto/tls" already supports.
|
||||
This provides a solid moving target without any compatibility or parrot-is-dead attack risks.
|
||||
**Feedback about opinionated implementation details of randomized handshake is appreciated.**
|
||||
### Parroting
|
||||
This package can be used to parrot ClientHello of popular browsers.
|
||||
There are some caveats to this parroting:
|
||||
* We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls.
|
||||
This is not a problem, if you fully control the server and turn unsupported things off on server side.
|
||||
* Parroting could be imperfect, and there is no parroting beyond ClientHello.
|
||||
#### Compatibility risks of available parrots
|
||||
|
||||
| Parrot | Ciphers* | Signature* | Unsupported extensions | TLS Fingerprint ID |
|
||||
| ------------- | -------- | ---------- | ---------------------- | --------------------------------------------- |
|
||||
| Chrome 62 | no | no | ChannelID | [0a4a74aeebd1bb66](https://tlsfingerprint.io/id/0a4a74aeebd1bb66) |
|
||||
| Chrome 70 | no | no | ChannelID, Encrypted Certs | [bc4c7e42f4961cd7](https://tlsfingerprint.io/id/bc4c7e42f4961cd7) |
|
||||
| Firefox 56 | very low | no | None | [c884bad7f40bee56](https://tlsfingerprint.io/id/c884bad7f40bee56) |
|
||||
| Firefox 63 | very low | no | MaxRecordSize | [6bfedc5d5c740d58](https://tlsfingerprint.io/id/6bfedc5d5c740d58) |
|
||||
| iOS 11.1 | low** | no | None | [71a81bafd58e1301](https://tlsfingerprint.io/id/71a81bafd58e1301) |
|
||||
|
||||
\* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild,
|
||||
*visibly breaking the connection*.
|
||||
\*\* No risk, if `utls.EnableWeakCiphers()` is called prior to using it.
|
||||
|
||||
#### Parrots FAQ
|
||||
> Does it really look like, say, Google Chrome with all the [GREASE](https://tools.ietf.org/html/draft-davidben-tls-grease-01) and stuff?
|
||||
|
||||
It LGTM, but please open up Wireshark and check. If you see something — [say something](issues).
|
||||
|
||||
> Aren't there side channels? Everybody knows that the ~~bird is a word~~[parrot is dead](https://people.cs.umass.edu/~amir/papers/parrot.pdf)
|
||||
|
||||
There sure are. If you found one that approaches practicality at line speed — [please tell us](issues).
|
||||
|
||||
#### Things to implement in Golang to make parrots better
|
||||
uTLS is fundamentially limited in parroting, because Golang's "crypto/tls" doesn't support many things. Would be nice to have:
|
||||
* ChannelID extension
|
||||
* In general, any modern crypto is likely to be useful going forward.
|
||||
### Custom Handshake
|
||||
It is possible to create custom handshake by
|
||||
1) Use `HelloCustom` as an argument for `UClient()` to get empty config
|
||||
2) Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
|
||||
3) Configure and add various [TLS Extensions](u_tls_extensions.go) to UConn.Extensions: they will be marshaled in order.
|
||||
4) Set Session and SessionCache, as needed.
|
||||
|
||||
If you need to manually control all the bytes on the wire(certainly not recommended!),
|
||||
you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself.
|
||||
In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup
|
||||
and not confuse "crypto/tls", which will be processing response from server.
|
||||
## Fake Session Tickets
|
||||
Fake session tickets is a very nifty trick that allows power users to hide parts of handshake, which may have some very fingerprintable features of handshake, and saves 1 RTT.
|
||||
Currently, there is a simple function to set session ticket to any desired state:
|
||||
|
||||
```Golang
|
||||
// If you want you session tickets to be reused - use same cache on following connections
|
||||
func (uconn *UConn) SetSessionState(session *ClientSessionState)
|
||||
```
|
||||
|
||||
Note that session tickets (fake ones or otherwise) are not reused.
|
||||
To reuse tickets, create a shared cache and set it on current and further configs:
|
||||
|
||||
```Golang
|
||||
// If you want you session tickets to be reused - use same cache on following connections
|
||||
func (uconn *UConn) SetSessionCache(cache ClientSessionCache)
|
||||
```
|
||||
|
||||
# Client Hello IDs
|
||||
See full list of `clientHelloID` values [here](https://godoc.org/github.com/refraction-networking/utls#ClientHelloID).
|
||||
There are different behaviors you can get, depending on your `clientHelloID`:
|
||||
|
||||
1. ```utls.HelloRandomized``` adds/reorders extensions, ciphersuites, etc. randomly.
|
||||
`HelloRandomized` adds ALPN in 50% of cases, you may want to use `HelloRandomizedALPN` or
|
||||
`HelloRandomizedNoALPN` to choose specific behavior explicitly, as ALPN might affect application layer.
|
||||
2. ```utls.HelloGolang```
|
||||
HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL
|
||||
overwrite your changes to Hello(Config, Session are fine).
|
||||
You might want to call BuildHandshakeState() before applying any changes.
|
||||
UConn.Extensions will be completely ignored.
|
||||
3. ```utls.HelloCustom```
|
||||
will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
|
||||
4. The rest will will parrot given browser. Such parrots include, for example:
|
||||
* `utls.HelloChrome_Auto`- parrots recommended(usually latest) Google Chrome version
|
||||
* `utls.HelloChrome_58` - parrots Google Chrome 58
|
||||
* `utls.HelloFirefox_Auto` - parrots recommended(usually latest) Firefox version
|
||||
* `utls.HelloFirefox_55` - parrots Firefox 55
|
||||
|
||||
# Usage
|
||||
## Examples
|
||||
Find basic examples [here](examples/examples.go).
|
||||
Here's a more [advanced example](https://github.com/sergeyfrolov/gotapdance/blob//9a777f35a04b0c4c5dacd30bca0e9224eb737b5e/tapdance/conn_raw.go#L275-L292) showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.
|
||||
### Migrating from "crypto/tls"
|
||||
Here's how default "crypto/tls" is typically used:
|
||||
```Golang
|
||||
dialConn, err := net.Dial("tcp", "172.217.11.46:443")
|
||||
if err != nil {
|
||||
fmt.Printf("net.Dial() failed: %+v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
config := tls.Config{ServerName: "www.google.com"}
|
||||
tlsConn := tls.Client(dialConn, &config)
|
||||
n, err = tlsConn.Write("Hello, World!")
|
||||
//...
|
||||
```
|
||||
To start using using uTLS:
|
||||
1. Import this library (e.g. `import tls "github.com/refraction-networking/utls"`)
|
||||
2. Pick the [Client Hello ID](#client-hello-ids)
|
||||
3. Simply substitute `tlsConn := tls.Client(dialConn, &config)`
|
||||
with `tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)`
|
||||
|
||||
### Customizing handshake
|
||||
Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:
|
||||
```Golang
|
||||
cRandom := []byte{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}
|
||||
tlsConn.SetClientRandom(cRandom)
|
||||
masterSecret := make([]byte, 48)
|
||||
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security
|
||||
|
||||
// Create a session ticket that wasn't actually issued by the server.
|
||||
sessionState := utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
|
||||
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
masterSecret,
|
||||
nil, nil)
|
||||
tlsConn.SetSessionState(sessionState)
|
||||
```
|
||||
|
||||
For other customizations there are following functions
|
||||
```
|
||||
// you can use this to build the state manually and change it
|
||||
// for example use Randomized ClientHello, and add more extensions
|
||||
func (uconn *UConn) BuildHandshakeState() error
|
||||
```
|
||||
```
|
||||
// Then apply the changes and marshal final bytes, which will be sent
|
||||
func (uconn *UConn) MarshalClientHello() error
|
||||
```
|
||||
|
||||
## Contributors' guide
|
||||
Please refer to [this document](./CONTRIBUTORS_GUIDE.md) if you're interested in internals
|
||||
|
||||
## Credits
|
||||
The initial development of uTLS was completed during an internship at [Google Jigsaw](https://jigsaw.google.com/). This is not an official Google product.
|
@ -1,85 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import "strconv"
|
||||
|
||||
type alert uint8
|
||||
|
||||
const (
|
||||
// alert level
|
||||
alertLevelWarning = 1
|
||||
alertLevelError = 2
|
||||
)
|
||||
|
||||
const (
|
||||
alertCloseNotify alert = 0
|
||||
alertUnexpectedMessage alert = 10
|
||||
alertBadRecordMAC alert = 20
|
||||
alertDecryptionFailed alert = 21
|
||||
alertRecordOverflow alert = 22
|
||||
alertDecompressionFailure alert = 30
|
||||
alertHandshakeFailure alert = 40
|
||||
alertBadCertificate alert = 42
|
||||
alertUnsupportedCertificate alert = 43
|
||||
alertCertificateRevoked alert = 44
|
||||
alertCertificateExpired alert = 45
|
||||
alertCertificateUnknown alert = 46
|
||||
alertIllegalParameter alert = 47
|
||||
alertUnknownCA alert = 48
|
||||
alertAccessDenied alert = 49
|
||||
alertDecodeError alert = 50
|
||||
alertDecryptError alert = 51
|
||||
alertProtocolVersion alert = 70
|
||||
alertInsufficientSecurity alert = 71
|
||||
alertInternalError alert = 80
|
||||
alertInappropriateFallback alert = 86
|
||||
alertUserCanceled alert = 90
|
||||
alertNoRenegotiation alert = 100
|
||||
alertMissingExtension alert = 109
|
||||
alertUnsupportedExtension alert = 110
|
||||
alertNoApplicationProtocol alert = 120
|
||||
)
|
||||
|
||||
var alertText = map[alert]string{
|
||||
alertCloseNotify: "close notify",
|
||||
alertUnexpectedMessage: "unexpected message",
|
||||
alertBadRecordMAC: "bad record MAC",
|
||||
alertDecryptionFailed: "decryption failed",
|
||||
alertRecordOverflow: "record overflow",
|
||||
alertDecompressionFailure: "decompression failure",
|
||||
alertHandshakeFailure: "handshake failure",
|
||||
alertBadCertificate: "bad certificate",
|
||||
alertUnsupportedCertificate: "unsupported certificate",
|
||||
alertCertificateRevoked: "revoked certificate",
|
||||
alertCertificateExpired: "expired certificate",
|
||||
alertCertificateUnknown: "unknown certificate",
|
||||
alertIllegalParameter: "illegal parameter",
|
||||
alertUnknownCA: "unknown certificate authority",
|
||||
alertAccessDenied: "access denied",
|
||||
alertDecodeError: "error decoding message",
|
||||
alertDecryptError: "error decrypting message",
|
||||
alertProtocolVersion: "protocol version not supported",
|
||||
alertInsufficientSecurity: "insufficient security level",
|
||||
alertInternalError: "internal error",
|
||||
alertInappropriateFallback: "inappropriate fallback",
|
||||
alertUserCanceled: "user canceled",
|
||||
alertNoRenegotiation: "no renegotiation",
|
||||
alertMissingExtension: "missing extension",
|
||||
alertUnsupportedExtension: "unsupported extension",
|
||||
alertNoApplicationProtocol: "no application protocol",
|
||||
}
|
||||
|
||||
func (e alert) String() string {
|
||||
s, ok := alertText[e]
|
||||
if ok {
|
||||
return "tls: " + s
|
||||
}
|
||||
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
|
||||
}
|
||||
|
||||
func (e alert) Error() string {
|
||||
return e.String()
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/asn1"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// pickSignatureAlgorithm selects a signature algorithm that is compatible with
|
||||
// the given public key and the list of algorithms from the peer and this side.
|
||||
// The lists of signature algorithms (peerSigAlgs and ourSigAlgs) are ignored
|
||||
// for tlsVersion < VersionTLS12.
|
||||
//
|
||||
// The returned SignatureScheme codepoint is only meaningful for TLS 1.2,
|
||||
// previous TLS versions have a fixed hash function.
|
||||
func pickSignatureAlgorithm(pubkey crypto.PublicKey, peerSigAlgs, ourSigAlgs []SignatureScheme, tlsVersion uint16) (sigAlg SignatureScheme, sigType uint8, hashFunc crypto.Hash, err error) {
|
||||
if tlsVersion < VersionTLS12 || len(peerSigAlgs) == 0 {
|
||||
// For TLS 1.1 and before, the signature algorithm could not be
|
||||
// negotiated and the hash is fixed based on the signature type. For TLS
|
||||
// 1.2, if the client didn't send signature_algorithms extension then we
|
||||
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
|
||||
switch pubkey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if tlsVersion < VersionTLS12 {
|
||||
return 0, signaturePKCS1v15, crypto.MD5SHA1, nil
|
||||
} else {
|
||||
return PKCS1WithSHA1, signaturePKCS1v15, crypto.SHA1, nil
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
return ECDSAWithSHA1, signatureECDSA, crypto.SHA1, nil
|
||||
default:
|
||||
return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey)
|
||||
}
|
||||
}
|
||||
for _, sigAlg := range peerSigAlgs {
|
||||
if !isSupportedSignatureAlgorithm(sigAlg, ourSigAlgs) {
|
||||
continue
|
||||
}
|
||||
hashAlg, err := hashFromSignatureScheme(sigAlg)
|
||||
if err != nil {
|
||||
panic("tls: supported signature algorithm has an unknown hash function")
|
||||
}
|
||||
sigType := signatureFromSignatureScheme(sigAlg)
|
||||
switch pubkey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
if sigType == signaturePKCS1v15 || sigType == signatureRSAPSS {
|
||||
return sigAlg, sigType, hashAlg, nil
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
if sigType == signatureECDSA {
|
||||
return sigAlg, sigType, hashAlg, nil
|
||||
}
|
||||
default:
|
||||
return 0, 0, 0, fmt.Errorf("tls: unsupported public key: %T", pubkey)
|
||||
}
|
||||
}
|
||||
return 0, 0, 0, errors.New("tls: peer doesn't support any common signature algorithms")
|
||||
}
|
||||
|
||||
// verifyHandshakeSignature verifies a signature against pre-hashed handshake
|
||||
// contents.
|
||||
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, digest, sig []byte) error {
|
||||
switch sigType {
|
||||
case signatureECDSA:
|
||||
pubKey, ok := pubkey.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return errors.New("tls: ECDSA signing requires a ECDSA public key")
|
||||
}
|
||||
ecdsaSig := new(ecdsaSignature)
|
||||
if _, err := asn1.Unmarshal(sig, ecdsaSig); err != nil {
|
||||
return err
|
||||
}
|
||||
if ecdsaSig.R.Sign() <= 0 || ecdsaSig.S.Sign() <= 0 {
|
||||
return errors.New("tls: ECDSA signature contained zero or negative values")
|
||||
}
|
||||
if !ecdsa.Verify(pubKey, digest, ecdsaSig.R, ecdsaSig.S) {
|
||||
return errors.New("tls: ECDSA verification failure")
|
||||
}
|
||||
case signaturePKCS1v15:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return errors.New("tls: RSA signing requires a RSA public key")
|
||||
}
|
||||
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, digest, sig); err != nil {
|
||||
return err
|
||||
}
|
||||
case signatureRSAPSS:
|
||||
pubKey, ok := pubkey.(*rsa.PublicKey)
|
||||
if !ok {
|
||||
return errors.New("tls: RSA signing requires a RSA public key")
|
||||
}
|
||||
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
|
||||
if err := rsa.VerifyPSS(pubKey, hashFunc, digest, sig, signOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.New("tls: unknown signature algorithm")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
|
||||
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
|
||||
)
|
||||
|
||||
var signaturePadding = []byte{
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
|
||||
}
|
||||
|
||||
// writeSignedMessage writes the content to be signed by certificate keys in TLS
|
||||
// 1.3 to sigHash. See RFC 8446, Section 4.4.3.
|
||||
func writeSignedMessage(sigHash io.Writer, context string, transcript hash.Hash) {
|
||||
sigHash.Write(signaturePadding)
|
||||
io.WriteString(sigHash, context)
|
||||
sigHash.Write(transcript.Sum(nil))
|
||||
}
|
||||
|
||||
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
|
||||
// for a given certificate, based on the public key.
|
||||
func signatureSchemesForCertificate(cert *Certificate) []SignatureScheme {
|
||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch priv := priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
switch priv.Curve {
|
||||
case elliptic.P256():
|
||||
return []SignatureScheme{ECDSAWithP256AndSHA256}
|
||||
case elliptic.P384():
|
||||
return []SignatureScheme{ECDSAWithP384AndSHA384}
|
||||
case elliptic.P521():
|
||||
return []SignatureScheme{ECDSAWithP521AndSHA512}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
case *rsa.PublicKey:
|
||||
// RSA keys with RSA-PSS OID are not supported by crypto/x509.
|
||||
return []SignatureScheme{
|
||||
PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512,
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
@ -1,472 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/hmac"
|
||||
"crypto/rc4"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// a keyAgreement implements the client and server side of a TLS key agreement
|
||||
// protocol by generating and processing key exchange messages.
|
||||
type keyAgreement interface {
|
||||
// On the server side, the first two methods are called in order.
|
||||
|
||||
// In the case that the key agreement protocol doesn't use a
|
||||
// ServerKeyExchange message, generateServerKeyExchange can return nil,
|
||||
// nil.
|
||||
generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
|
||||
processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
|
||||
|
||||
// On the client side, the next two methods are called in order.
|
||||
|
||||
// This method may not be called if the server doesn't send a
|
||||
// ServerKeyExchange message.
|
||||
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
|
||||
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
|
||||
}
|
||||
|
||||
const (
|
||||
// suiteECDH indicates that the cipher suite involves elliptic curve
|
||||
// Diffie-Hellman. This means that it should only be selected when the
|
||||
// client indicates that it supports ECC with a curve and point format
|
||||
// that we're happy with.
|
||||
suiteECDHE = 1 << iota
|
||||
// suiteECDSA indicates that the cipher suite involves an ECDSA
|
||||
// signature and therefore may only be selected when the server's
|
||||
// certificate is ECDSA. If this is not set then the cipher suite is
|
||||
// RSA based.
|
||||
suiteECDSA
|
||||
// suiteTLS12 indicates that the cipher suite should only be advertised
|
||||
// and accepted when using TLS 1.2.
|
||||
suiteTLS12
|
||||
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
|
||||
// handshake hash.
|
||||
suiteSHA384
|
||||
// suiteDefaultOff indicates that this cipher suite is not included by
|
||||
// default.
|
||||
suiteDefaultOff
|
||||
)
|
||||
|
||||
// A cipherSuite is a specific combination of key agreement, cipher and MAC function.
|
||||
type cipherSuite struct {
|
||||
id uint16
|
||||
// the lengths, in bytes, of the key material needed for each component.
|
||||
keyLen int
|
||||
macLen int
|
||||
ivLen int
|
||||
ka func(version uint16) keyAgreement
|
||||
// flags is a bitmask of the suite* values, above.
|
||||
flags int
|
||||
cipher func(key, iv []byte, isRead bool) interface{}
|
||||
mac func(version uint16, macKey []byte) macFunction
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
}
|
||||
|
||||
var cipherSuites = []*cipherSuite{
|
||||
// Ciphersuite order is chosen so that ECDHE comes before plain RSA and
|
||||
// AEADs are the top preference.
|
||||
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECDSA, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
|
||||
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
|
||||
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
|
||||
|
||||
// RC4-based cipher suites are disabled by default.
|
||||
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, suiteDefaultOff, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE | suiteDefaultOff, cipherRC4, macSHA1, nil},
|
||||
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteDefaultOff, cipherRC4, macSHA1, nil},
|
||||
}
|
||||
|
||||
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
|
||||
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
|
||||
type cipherSuiteTLS13 struct {
|
||||
id uint16
|
||||
keyLen int
|
||||
aead func(key, fixedNonce []byte) aead
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
var cipherSuitesTLS13 = []*cipherSuiteTLS13{
|
||||
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
|
||||
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
|
||||
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
|
||||
}
|
||||
|
||||
func cipherRC4(key, iv []byte, isRead bool) interface{} {
|
||||
cipher, _ := rc4.NewCipher(key)
|
||||
return cipher
|
||||
}
|
||||
|
||||
func cipher3DES(key, iv []byte, isRead bool) interface{} {
|
||||
block, _ := des.NewTripleDESCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
func cipherAES(key, iv []byte, isRead bool) interface{} {
|
||||
block, _ := aes.NewCipher(key)
|
||||
if isRead {
|
||||
return cipher.NewCBCDecrypter(block, iv)
|
||||
}
|
||||
return cipher.NewCBCEncrypter(block, iv)
|
||||
}
|
||||
|
||||
// macSHA1 returns a macFunction for the given protocol version.
|
||||
func macSHA1(version uint16, key []byte) macFunction {
|
||||
if version == VersionSSL30 {
|
||||
mac := ssl30MAC{
|
||||
h: sha1.New(),
|
||||
key: make([]byte, len(key)),
|
||||
}
|
||||
copy(mac.key, key)
|
||||
return mac
|
||||
}
|
||||
return tls10MAC{h: hmac.New(newConstantTimeHash(sha1.New), key)}
|
||||
}
|
||||
|
||||
// macSHA256 returns a SHA-256 based MAC. These are only supported in TLS 1.2
|
||||
// so the given version is ignored.
|
||||
func macSHA256(version uint16, key []byte) macFunction {
|
||||
return tls10MAC{h: hmac.New(sha256.New, key)}
|
||||
}
|
||||
|
||||
type macFunction interface {
|
||||
// Size returns the length of the MAC.
|
||||
Size() int
|
||||
// MAC appends the MAC of (seq, header, data) to out. The extra data is fed
|
||||
// into the MAC after obtaining the result to normalize timing. The result
|
||||
// is only valid until the next invocation of MAC as the buffer is reused.
|
||||
MAC(seq, header, data, extra []byte) []byte
|
||||
}
|
||||
|
||||
type aead interface {
|
||||
cipher.AEAD
|
||||
|
||||
// explicitNonceLen returns the number of bytes of explicit nonce
|
||||
// included in each record. This is eight for older AEADs and
|
||||
// zero for modern ones.
|
||||
explicitNonceLen() int
|
||||
}
|
||||
|
||||
const (
|
||||
aeadNonceLength = 12
|
||||
noncePrefixLength = 4
|
||||
)
|
||||
|
||||
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
|
||||
// each call.
|
||||
type prefixNonceAEAD struct {
|
||||
// nonce contains the fixed part of the nonce in the first four bytes.
|
||||
nonce [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
|
||||
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
|
||||
|
||||
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
copy(f.nonce[4:], nonce)
|
||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// before each call.
|
||||
type xorNonceAEAD struct {
|
||||
nonceMask [aeadNonceLength]byte
|
||||
aead cipher.AEAD
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
|
||||
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
|
||||
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
|
||||
|
||||
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
|
||||
for i, b := range nonce {
|
||||
f.nonceMask[4+i] ^= b
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func aeadAESGCM(key, noncePrefix []byte) aead {
|
||||
if len(noncePrefix) != noncePrefixLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &prefixNonceAEAD{aead: aead}
|
||||
copy(ret.nonce[:], noncePrefix)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aes, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
|
||||
if len(nonceMask) != aeadNonceLength {
|
||||
panic("tls: internal error: wrong nonce length")
|
||||
}
|
||||
aead, err := chacha20poly1305.New(key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ret := &xorNonceAEAD{aead: aead}
|
||||
copy(ret.nonceMask[:], nonceMask)
|
||||
return ret
|
||||
}
|
||||
|
||||
// ssl30MAC implements the SSLv3 MAC function, as defined in
|
||||
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 5.2.3.1
|
||||
type ssl30MAC struct {
|
||||
h hash.Hash
|
||||
key []byte
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (s ssl30MAC) Size() int {
|
||||
return s.h.Size()
|
||||
}
|
||||
|
||||
var ssl30Pad1 = [48]byte{0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36}
|
||||
|
||||
var ssl30Pad2 = [48]byte{0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c}
|
||||
|
||||
// MAC does not offer constant timing guarantees for SSL v3.0, since it's deemed
|
||||
// useless considering the similar, protocol-level POODLE vulnerability.
|
||||
func (s ssl30MAC) MAC(seq, header, data, extra []byte) []byte {
|
||||
padLength := 48
|
||||
if s.h.Size() == 20 {
|
||||
padLength = 40
|
||||
}
|
||||
|
||||
s.h.Reset()
|
||||
s.h.Write(s.key)
|
||||
s.h.Write(ssl30Pad1[:padLength])
|
||||
s.h.Write(seq)
|
||||
s.h.Write(header[:1])
|
||||
s.h.Write(header[3:5])
|
||||
s.h.Write(data)
|
||||
s.buf = s.h.Sum(s.buf[:0])
|
||||
|
||||
s.h.Reset()
|
||||
s.h.Write(s.key)
|
||||
s.h.Write(ssl30Pad2[:padLength])
|
||||
s.h.Write(s.buf)
|
||||
return s.h.Sum(s.buf[:0])
|
||||
}
|
||||
|
||||
type constantTimeHash interface {
|
||||
hash.Hash
|
||||
ConstantTimeSum(b []byte) []byte
|
||||
}
|
||||
|
||||
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
|
||||
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
|
||||
type cthWrapper struct {
|
||||
h constantTimeHash
|
||||
}
|
||||
|
||||
func (c *cthWrapper) Size() int { return c.h.Size() }
|
||||
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
|
||||
func (c *cthWrapper) Reset() { c.h.Reset() }
|
||||
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
|
||||
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
|
||||
|
||||
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
|
||||
return func() hash.Hash {
|
||||
return &cthWrapper{h().(constantTimeHash)}
|
||||
}
|
||||
}
|
||||
|
||||
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
|
||||
type tls10MAC struct {
|
||||
h hash.Hash
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (s tls10MAC) Size() int {
|
||||
return s.h.Size()
|
||||
}
|
||||
|
||||
// MAC is guaranteed to take constant time, as long as
|
||||
// len(seq)+len(header)+len(data)+len(extra) is constant. extra is not fed into
|
||||
// the MAC, but is only provided to make the timing profile constant.
|
||||
func (s tls10MAC) MAC(seq, header, data, extra []byte) []byte {
|
||||
s.h.Reset()
|
||||
s.h.Write(seq)
|
||||
s.h.Write(header)
|
||||
s.h.Write(data)
|
||||
res := s.h.Sum(s.buf[:0])
|
||||
if extra != nil {
|
||||
s.h.Write(extra)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func rsaKA(version uint16) keyAgreement {
|
||||
return rsaKeyAgreement{}
|
||||
}
|
||||
|
||||
func ecdheECDSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: false,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func ecdheRSAKA(version uint16) keyAgreement {
|
||||
return &ecdheKeyAgreement{
|
||||
isRSA: true,
|
||||
version: version,
|
||||
}
|
||||
}
|
||||
|
||||
// mutualCipherSuite returns a cipherSuite given a list of supported
|
||||
// ciphersuites and the id requested by the peer.
|
||||
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteByID(id uint16) *cipherSuite {
|
||||
for _, cipherSuite := range utlsSupportedCipherSuites {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
|
||||
for _, id := range have {
|
||||
if id == want {
|
||||
return cipherSuiteTLS13ByID(id)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
|
||||
for _, cipherSuite := range cipherSuitesTLS13 {
|
||||
if cipherSuite.id == id {
|
||||
return cipherSuite
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// A list of cipher suite IDs that are, or have been, implemented by this
|
||||
// package.
|
||||
//
|
||||
// Taken from https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
|
||||
const (
|
||||
// TLS 1.0 - 1.2 cipher suites.
|
||||
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
|
||||
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
|
||||
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
|
||||
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca8
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 uint16 = 0xcca9
|
||||
|
||||
// TLS 1.3 cipher suites.
|
||||
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
|
||||
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
|
||||
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
|
||||
|
||||
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
|
||||
// that the client is doing version fallback. See RFC 7507.
|
||||
TLS_FALLBACK_SCSV uint16 = 0x5600
|
||||
)
|
1143
external/github.com/refraction-networking/utls/common.go
vendored
1143
external/github.com/refraction-networking/utls/common.go
vendored
File diff suppressed because it is too large
Load Diff
1427
external/github.com/refraction-networking/utls/conn.go
vendored
1427
external/github.com/refraction-networking/utls/conn.go
vendored
File diff suppressed because it is too large
Load Diff
@ -1,77 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package cpu implements processor feature detection
|
||||
// used by the Go standard library.
|
||||
package cpu
|
||||
|
||||
var X86 x86
|
||||
|
||||
// The booleans in x86 contain the correspondingly named cpuid feature bit.
|
||||
// HasAVX and HasAVX2 are only set if the OS does support XMM and YMM registers
|
||||
// in addition to the cpuid feature bit being set.
|
||||
// The struct is padded to avoid false sharing.
|
||||
type x86 struct {
|
||||
_ [CacheLineSize]byte
|
||||
HasAES bool
|
||||
HasADX bool
|
||||
HasAVX bool
|
||||
HasAVX2 bool
|
||||
HasBMI1 bool
|
||||
HasBMI2 bool
|
||||
HasERMS bool
|
||||
HasFMA bool
|
||||
HasOSXSAVE bool
|
||||
HasPCLMULQDQ bool
|
||||
HasPOPCNT bool
|
||||
HasSSE2 bool
|
||||
HasSSE3 bool
|
||||
HasSSSE3 bool
|
||||
HasSSE41 bool
|
||||
HasSSE42 bool
|
||||
_ [CacheLineSize]byte
|
||||
}
|
||||
|
||||
var PPC64 ppc64
|
||||
|
||||
// For ppc64x, it is safe to check only for ISA level starting on ISA v3.00,
|
||||
// since there are no optional categories. There are some exceptions that also
|
||||
// require kernel support to work (darn, scv), so there are capability bits for
|
||||
// those as well. The minimum processor requirement is POWER8 (ISA 2.07), so we
|
||||
// maintain some of the old capability checks for optional categories for
|
||||
// safety.
|
||||
// The struct is padded to avoid false sharing.
|
||||
type ppc64 struct {
|
||||
_ [CacheLineSize]byte
|
||||
HasVMX bool // Vector unit (Altivec)
|
||||
HasDFP bool // Decimal Floating Point unit
|
||||
HasVSX bool // Vector-scalar unit
|
||||
HasHTM bool // Hardware Transactional Memory
|
||||
HasISEL bool // Integer select
|
||||
HasVCRYPTO bool // Vector cryptography
|
||||
HasHTMNOSC bool // HTM: kernel-aborted transaction in syscalls
|
||||
HasDARN bool // Hardware random number generator (requires kernel enablement)
|
||||
HasSCV bool // Syscall vectored (requires kernel enablement)
|
||||
IsPOWER8 bool // ISA v2.07 (POWER8)
|
||||
IsPOWER9 bool // ISA v3.00 (POWER9)
|
||||
_ [CacheLineSize]byte
|
||||
}
|
||||
|
||||
var ARM64 arm64
|
||||
|
||||
// The booleans in arm64 contain the correspondingly named cpu feature bit.
|
||||
// The struct is padded to avoid false sharing.
|
||||
type arm64 struct {
|
||||
_ [CacheLineSize]byte
|
||||
HasFP bool
|
||||
HasASIMD bool
|
||||
HasEVTSTRM bool
|
||||
HasAES bool
|
||||
HasPMULL bool
|
||||
HasSHA1 bool
|
||||
HasSHA2 bool
|
||||
HasCRC32 bool
|
||||
HasATOMICS bool
|
||||
_ [CacheLineSize]byte
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 32
|
@ -1,45 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build arm64
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 64
|
||||
|
||||
// arm64 doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
|
||||
// These are linknamed in runtime/os_linux_arm64.go and are initialized by
|
||||
// archauxv().
|
||||
var arm64_hwcap uint
|
||||
var arm64_hwcap2 uint
|
||||
|
||||
// HWCAP/HWCAP2 bits. These are exposed by Linux.
|
||||
const (
|
||||
_ARM64_FEATURE_HAS_FP = (1 << 0)
|
||||
_ARM64_FEATURE_HAS_ASIMD = (1 << 1)
|
||||
_ARM64_FEATURE_HAS_EVTSTRM = (1 << 2)
|
||||
_ARM64_FEATURE_HAS_AES = (1 << 3)
|
||||
_ARM64_FEATURE_HAS_PMULL = (1 << 4)
|
||||
_ARM64_FEATURE_HAS_SHA1 = (1 << 5)
|
||||
_ARM64_FEATURE_HAS_SHA2 = (1 << 6)
|
||||
_ARM64_FEATURE_HAS_CRC32 = (1 << 7)
|
||||
_ARM64_FEATURE_HAS_ATOMICS = (1 << 8)
|
||||
)
|
||||
|
||||
func init() {
|
||||
// HWCAP feature bits
|
||||
ARM64.HasFP = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_FP)
|
||||
ARM64.HasASIMD = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ASIMD)
|
||||
ARM64.HasEVTSTRM = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_EVTSTRM)
|
||||
ARM64.HasAES = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_AES)
|
||||
ARM64.HasPMULL = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_PMULL)
|
||||
ARM64.HasSHA1 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA1)
|
||||
ARM64.HasSHA2 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_SHA2)
|
||||
ARM64.HasCRC32 = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_CRC32)
|
||||
ARM64.HasATOMICS = isSet(arm64_hwcap, _ARM64_FEATURE_HAS_ATOMICS)
|
||||
}
|
||||
|
||||
func isSet(hwc uint, value uint) bool {
|
||||
return hwc&value != 0
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 32
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 32
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 32
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 32
|
@ -1,54 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ppc64 ppc64le
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 128
|
||||
|
||||
// ppc64x doesn't have a 'cpuid' equivalent, so we rely on HWCAP/HWCAP2.
|
||||
// These are linknamed in runtime/os_linux_ppc64x.go and are initialized by
|
||||
// archauxv().
|
||||
var ppc64x_hwcap uint
|
||||
var ppc64x_hwcap2 uint
|
||||
|
||||
// HWCAP/HWCAP2 bits. These are exposed by the kernel.
|
||||
const (
|
||||
// ISA Level
|
||||
_PPC_FEATURE2_ARCH_2_07 = 0x80000000
|
||||
_PPC_FEATURE2_ARCH_3_00 = 0x00800000
|
||||
|
||||
// CPU features
|
||||
_PPC_FEATURE_HAS_ALTIVEC = 0x10000000
|
||||
_PPC_FEATURE_HAS_DFP = 0x00000400
|
||||
_PPC_FEATURE_HAS_VSX = 0x00000080
|
||||
_PPC_FEATURE2_HAS_HTM = 0x40000000
|
||||
_PPC_FEATURE2_HAS_ISEL = 0x08000000
|
||||
_PPC_FEATURE2_HAS_VEC_CRYPTO = 0x02000000
|
||||
_PPC_FEATURE2_HTM_NOSC = 0x01000000
|
||||
_PPC_FEATURE2_DARN = 0x00200000
|
||||
_PPC_FEATURE2_SCV = 0x00100000
|
||||
)
|
||||
|
||||
func init() {
|
||||
// HWCAP feature bits
|
||||
PPC64.HasVMX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_ALTIVEC)
|
||||
PPC64.HasDFP = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_DFP)
|
||||
PPC64.HasVSX = isSet(ppc64x_hwcap, _PPC_FEATURE_HAS_VSX)
|
||||
|
||||
// HWCAP2 feature bits
|
||||
PPC64.IsPOWER8 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_2_07)
|
||||
PPC64.HasHTM = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_HTM)
|
||||
PPC64.HasISEL = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_ISEL)
|
||||
PPC64.HasVCRYPTO = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HAS_VEC_CRYPTO)
|
||||
PPC64.HasHTMNOSC = isSet(ppc64x_hwcap2, _PPC_FEATURE2_HTM_NOSC)
|
||||
PPC64.IsPOWER9 = isSet(ppc64x_hwcap2, _PPC_FEATURE2_ARCH_3_00)
|
||||
PPC64.HasDARN = isSet(ppc64x_hwcap2, _PPC_FEATURE2_DARN)
|
||||
PPC64.HasSCV = isSet(ppc64x_hwcap2, _PPC_FEATURE2_SCV)
|
||||
}
|
||||
|
||||
func isSet(hwc uint, value uint) bool {
|
||||
return hwc&value != 0
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 256
|
@ -1,61 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build 386 amd64 amd64p32
|
||||
|
||||
package cpu
|
||||
|
||||
const CacheLineSize = 64
|
||||
|
||||
// cpuid is implemented in cpu_x86.s.
|
||||
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||
|
||||
// xgetbv with ecx = 0 is implemented in cpu_x86.s.
|
||||
func xgetbv() (eax, edx uint32)
|
||||
|
||||
func init() {
|
||||
maxID, _, _, _ := cpuid(0, 0)
|
||||
|
||||
if maxID < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
_, _, ecx1, edx1 := cpuid(1, 0)
|
||||
X86.HasSSE2 = isSet(26, edx1)
|
||||
|
||||
X86.HasSSE3 = isSet(0, ecx1)
|
||||
X86.HasPCLMULQDQ = isSet(1, ecx1)
|
||||
X86.HasSSSE3 = isSet(9, ecx1)
|
||||
X86.HasFMA = isSet(12, ecx1)
|
||||
X86.HasSSE41 = isSet(19, ecx1)
|
||||
X86.HasSSE42 = isSet(20, ecx1)
|
||||
X86.HasPOPCNT = isSet(23, ecx1)
|
||||
X86.HasAES = isSet(25, ecx1)
|
||||
X86.HasOSXSAVE = isSet(27, ecx1)
|
||||
|
||||
osSupportsAVX := false
|
||||
// For XGETBV, OSXSAVE bit is required and sufficient.
|
||||
if X86.HasOSXSAVE {
|
||||
eax, _ := xgetbv()
|
||||
// Check if XMM and YMM registers have OS support.
|
||||
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
|
||||
}
|
||||
|
||||
X86.HasAVX = isSet(28, ecx1) && osSupportsAVX
|
||||
|
||||
if maxID < 7 {
|
||||
return
|
||||
}
|
||||
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
X86.HasBMI1 = isSet(3, ebx7)
|
||||
X86.HasAVX2 = isSet(5, ebx7) && osSupportsAVX
|
||||
X86.HasBMI2 = isSet(8, ebx7)
|
||||
X86.HasERMS = isSet(9, ebx7)
|
||||
X86.HasADX = isSet(19, ebx7)
|
||||
}
|
||||
|
||||
func isSet(bitpos uint, value uint32) bool {
|
||||
return value&(1<<bitpos) != 0
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build 386 amd64 amd64p32
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
||||
TEXT ·cpuid(SB), NOSPLIT, $0-24
|
||||
MOVL eaxArg+0(FP), AX
|
||||
MOVL ecxArg+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
||||
// func xgetbv() (eax, edx uint32)
|
||||
TEXT ·xgetbv(SB),NOSPLIT,$0-8
|
||||
#ifdef GOOS_nacl
|
||||
// nacl does not support XGETBV.
|
||||
MOVL $0, eax+0(FP)
|
||||
MOVL $0, edx+4(FP)
|
||||
#else
|
||||
MOVL $0, CX
|
||||
XGETBV
|
||||
MOVL AX, eax+0(FP)
|
||||
MOVL DX, edx+4(FP)
|
||||
#endif
|
||||
RET
|
@ -1,169 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// Generate a self-signed X.509 certificate for a TLS server. Outputs to
|
||||
// 'cert.pem' and 'key.pem' and will overwrite existing files.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
|
||||
validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
|
||||
validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
|
||||
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
|
||||
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
|
||||
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
|
||||
)
|
||||
|
||||
func publicKey(priv interface{}) interface{} {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func pemBlockForKey(priv interface{}) *pem.Block {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)}
|
||||
case *ecdsa.PrivateKey:
|
||||
b, err := x509.MarshalECPrivateKey(k)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if len(*host) == 0 {
|
||||
log.Fatalf("Missing required --host parameter")
|
||||
}
|
||||
|
||||
var priv interface{}
|
||||
var err error
|
||||
switch *ecdsaCurve {
|
||||
case "":
|
||||
priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
|
||||
case "P224":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
fmt.Fprintf(os.Stderr, "Unrecognized elliptic curve: %q", *ecdsaCurve)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("failed to generate private key: %s", err)
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(*validFrom) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to parse creation date: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(*validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to generate serial number: %s", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(*host, ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if *isCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %s", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to open cert.pem for writing: %s", err)
|
||||
}
|
||||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
log.Fatalf("failed to write data to cert.pem: %s", err)
|
||||
}
|
||||
if err := certOut.Close(); err != nil {
|
||||
log.Fatalf("error closing cert.pem: %s", err)
|
||||
}
|
||||
log.Print("wrote cert.pem\n")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Print("failed to open key.pem for writing:", err)
|
||||
return
|
||||
}
|
||||
if err := pem.Encode(keyOut, pemBlockForKey(priv)); err != nil {
|
||||
log.Fatalf("failed to write data to key.pem: %s", err)
|
||||
}
|
||||
if err := keyOut.Close(); err != nil {
|
||||
log.Fatalf("error closing key.pem: %s", err)
|
||||
}
|
||||
log.Print("wrote key.pem\n")
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,672 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
type clientHandshakeStateTLS13 struct {
|
||||
c *Conn
|
||||
serverHello *serverHelloMsg
|
||||
hello *clientHelloMsg
|
||||
ecdheParams ecdheParameters
|
||||
|
||||
session *ClientSessionState
|
||||
earlySecret []byte
|
||||
binderKey []byte
|
||||
|
||||
certReq *certificateRequestMsgTLS13
|
||||
usingPSK bool
|
||||
sentDummyCCS bool
|
||||
suite *cipherSuiteTLS13
|
||||
transcript hash.Hash
|
||||
masterSecret []byte
|
||||
trafficSecret []byte // client_application_traffic_secret_0
|
||||
}
|
||||
|
||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
|
||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
|
||||
// sections 4.1.2 and 4.1.3.
|
||||
if c.handshakes > 0 {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
|
||||
}
|
||||
|
||||
// Consistency check on the presence of a keyShare and its parameters.
|
||||
if hs.ecdheParams == nil || len(hs.hello.keyShares) < 1 { // [uTLS]
|
||||
// keyshares "< 1" instead of "!= 1", as uTLS may send multiple
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.transcript = hs.suite.hash.New()
|
||||
hs.transcript.Write(hs.hello.marshal())
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.processHelloRetryRequest(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hs.transcript.Write(hs.serverHello.marshal())
|
||||
|
||||
c.buffering = true
|
||||
if err := hs.processServerHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishHandshakeKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
|
||||
// HelloRetryRequest messages. It sets hs.suite.
|
||||
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.serverHello.supportedVersion == 0 {
|
||||
c.sendAlert(alertMissingExtension)
|
||||
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
|
||||
}
|
||||
|
||||
if hs.serverHello.supportedVersion != VersionTLS13 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
|
||||
}
|
||||
|
||||
if hs.serverHello.vers != VersionTLS12 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an incorrect legacy version")
|
||||
}
|
||||
|
||||
if hs.serverHello.nextProtoNeg ||
|
||||
len(hs.serverHello.nextProtos) != 0 ||
|
||||
hs.serverHello.ocspStapling ||
|
||||
hs.serverHello.ticketSupported ||
|
||||
hs.serverHello.secureRenegotiationSupported ||
|
||||
len(hs.serverHello.secureRenegotiation) != 0 ||
|
||||
len(hs.serverHello.alpnProtocol) != 0 ||
|
||||
len(hs.serverHello.scts) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
|
||||
}
|
||||
|
||||
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not echo the legacy session ID")
|
||||
}
|
||||
|
||||
if hs.serverHello.compressionMethod != compressionNone {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported compression format")
|
||||
}
|
||||
|
||||
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
|
||||
if hs.suite != nil && selectedSuite != hs.suite {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
|
||||
}
|
||||
if selectedSuite == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server chose an unconfigured cipher suite")
|
||||
}
|
||||
hs.suite = selectedSuite
|
||||
c.cipherSuite = hs.suite.id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
||||
func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
if hs.sentDummyCCS {
|
||||
return nil
|
||||
}
|
||||
hs.sentDummyCCS = true
|
||||
|
||||
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
return err
|
||||
}
|
||||
|
||||
// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
|
||||
// resends hs.hello, and reads the new ServerHello into hs.serverHello.
|
||||
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c := hs.c
|
||||
|
||||
// The first ClientHello gets double-hashed into the transcript upon a
|
||||
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
||||
chHash := hs.transcript.Sum(nil)
|
||||
hs.transcript.Reset()
|
||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
hs.transcript.Write(chHash)
|
||||
hs.transcript.Write(hs.serverHello.marshal())
|
||||
|
||||
if hs.serverHello.serverShare.group != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received malformed key_share extension")
|
||||
}
|
||||
|
||||
curveID := hs.serverHello.selectedGroup
|
||||
if curveID == 0 {
|
||||
c.sendAlert(alertMissingExtension)
|
||||
return errors.New("tls: received HelloRetryRequest without selected group")
|
||||
}
|
||||
curveOK := false
|
||||
for _, id := range hs.hello.supportedCurves {
|
||||
if id == curveID {
|
||||
curveOK = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !curveOK {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
if hs.ecdheParams.CurveID() == curveID {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
|
||||
}
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(c.config.rand(), curveID)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
hs.ecdheParams = params
|
||||
hs.hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
|
||||
hs.hello.cookie = hs.serverHello.cookie
|
||||
|
||||
hs.hello.raw = nil
|
||||
if len(hs.hello.pskIdentities) > 0 {
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash == hs.suite.hash {
|
||||
// Update binders and obfuscated_ticket_age.
|
||||
ticketAge := uint32(c.config.time().Sub(hs.session.receivedAt) / time.Millisecond)
|
||||
hs.hello.pskIdentities[0].obfuscatedTicketAge = ticketAge + hs.session.ageAdd
|
||||
|
||||
transcript := hs.suite.hash.New()
|
||||
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
transcript.Write(chHash)
|
||||
transcript.Write(hs.serverHello.marshal())
|
||||
transcript.Write(hs.hello.marshalWithoutBinders())
|
||||
pskBinders := [][]byte{hs.suite.finishedHash(hs.binderKey, transcript)}
|
||||
hs.hello.updateBinders(pskBinders)
|
||||
} else {
|
||||
// Server selected a cipher suite incompatible with the PSK.
|
||||
hs.hello.pskIdentities = nil
|
||||
hs.hello.pskBinders = nil
|
||||
}
|
||||
}
|
||||
|
||||
hs.transcript.Write(hs.hello.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverHello, ok := msg.(*serverHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(serverHello, msg)
|
||||
}
|
||||
hs.serverHello = serverHello
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
c := hs.c
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: server sent two HelloRetryRequest messages")
|
||||
}
|
||||
|
||||
if len(hs.serverHello.cookie) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a cookie in a normal ServerHello")
|
||||
}
|
||||
|
||||
if hs.serverHello.selectedGroup != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: malformed key_share extension")
|
||||
}
|
||||
|
||||
if hs.serverHello.serverShare.group == 0 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not send a key share")
|
||||
}
|
||||
if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
|
||||
if !hs.serverHello.selectedIdentityPresent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK")
|
||||
}
|
||||
|
||||
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash != hs.suite.hash {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
|
||||
}
|
||||
|
||||
hs.usingPSK = true
|
||||
c.didResume = true
|
||||
c.peerCertificates = hs.session.serverCertificates
|
||||
c.verifiedChains = hs.session.verifiedChains
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
c := hs.c
|
||||
|
||||
sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
|
||||
if sharedKey == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
|
||||
earlySecret := hs.earlySecret
|
||||
if !hs.usingPSK {
|
||||
earlySecret = hs.suite.extract(nil, nil)
|
||||
}
|
||||
handshakeSecret := hs.suite.extract(sharedKey,
|
||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
||||
|
||||
clientSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
clientHandshakeTrafficLabel, hs.transcript)
|
||||
c.out.setTrafficSecret(hs.suite, clientSecret)
|
||||
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
serverHandshakeTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
hs.masterSecret = hs.suite.extract(nil,
|
||||
hs.suite.deriveSecret(handshakeSecret, "derived", nil))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerParameters() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(encryptedExtensions, msg)
|
||||
}
|
||||
hs.transcript.Write(encryptedExtensions.marshal())
|
||||
|
||||
if len(encryptedExtensions.alpnProtocol) != 0 && len(hs.hello.alpnProtocols) == 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server advertised unrequested ALPN extension")
|
||||
}
|
||||
c.clientProtocol = encryptedExtensions.alpnProtocol
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
// Either a PSK or a certificate is always used, but not both.
|
||||
// See RFC 8446, Section 4.1.1.
|
||||
if hs.usingPSK {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
||||
if ok {
|
||||
hs.transcript.Write(certReq.marshal())
|
||||
|
||||
hs.certReq = certReq
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
if len(certMsg.certificate.Certificate) == 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received empty certificates message")
|
||||
}
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
|
||||
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
||||
c.ocspResponse = certMsg.certificate.OCSPStaple
|
||||
|
||||
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.4.3.
|
||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid certificate signature algorithm")
|
||||
}
|
||||
sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
if sigType == 0 || err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid certificate signature algorithm")
|
||||
}
|
||||
h := sigHash.New()
|
||||
writeSignedMessage(h, serverSignatureContext, hs.transcript)
|
||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
||||
sigHash, h.Sum(nil), certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid certificate signature")
|
||||
}
|
||||
|
||||
hs.transcript.Write(certVerify.marshal())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerFinished() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(finished, msg)
|
||||
}
|
||||
|
||||
expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
||||
if !hmac.Equal(expectedMAC, finished.verifyData) {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid server finished hash")
|
||||
}
|
||||
|
||||
hs.transcript.Write(finished.marshal())
|
||||
|
||||
// Derive secrets that take context through the server Finished.
|
||||
|
||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
clientApplicationTrafficLabel, hs.transcript)
|
||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
||||
serverApplicationTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.certReq == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cert, err := c.getClientCertificate(&CertificateRequestInfo{
|
||||
AcceptableCAs: hs.certReq.certificateAuthorities,
|
||||
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsgTLS13)
|
||||
|
||||
certMsg.certificate = *cert
|
||||
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
||||
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
||||
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the client is sending an empty certificate message, skip the CertificateVerify.
|
||||
if len(cert.Certificate) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
certVerifyMsg := new(certificateVerifyMsg)
|
||||
certVerifyMsg.hasSignatureAlgorithm = true
|
||||
|
||||
supportedAlgs := signatureSchemesForCertificate(cert)
|
||||
if supportedAlgs == nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported certificate key (%T)", cert.PrivateKey)
|
||||
}
|
||||
// Pick signature scheme in server preference order, as the client
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range hs.certReq.supportedSignatureAlgorithms {
|
||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
||||
certVerifyMsg.signatureAlgorithm = preferredAlg
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
sigType := signatureFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
||||
sigHash, err := hashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
||||
if sigType == 0 || err != nil {
|
||||
// getClientCertificate returned a certificate incompatible with the
|
||||
// CertificateRequestInfo supported signature algorithms.
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
h := sigHash.New()
|
||||
writeSignedMessage(h, clientSignatureContext, hs.transcript)
|
||||
|
||||
signOpts := crypto.SignerOpts(sigHash)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
||||
}
|
||||
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
||||
}
|
||||
certVerifyMsg.signature = sig
|
||||
|
||||
hs.transcript.Write(certVerifyMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
||||
c := hs.c
|
||||
|
||||
finished := &finishedMsg{
|
||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
||||
}
|
||||
|
||||
hs.transcript.Write(finished.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.out.setTrafficSecret(hs.suite, hs.trafficSecret)
|
||||
|
||||
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
|
||||
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
resumptionLabel, hs.transcript)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
|
||||
if !c.isClient {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: received new session ticket from a client")
|
||||
}
|
||||
|
||||
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.6.1.
|
||||
if msg.lifetime == 0 {
|
||||
return nil
|
||||
}
|
||||
lifetime := time.Duration(msg.lifetime) * time.Second
|
||||
if lifetime > maxSessionTicketLifetime {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: received a session ticket with invalid lifetime")
|
||||
}
|
||||
|
||||
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
||||
if cipherSuite == nil || c.resumptionSecret == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
// Save the resumption_master_secret and nonce instead of deriving the PSK
|
||||
// to do the least amount of work on NewSessionTicket messages before we
|
||||
// know if the ticket will be used. Forward secrecy of resumed connections
|
||||
// is guaranteed by the requirement for pskModeDHE.
|
||||
session := &ClientSessionState{
|
||||
sessionTicket: msg.label,
|
||||
vers: c.vers,
|
||||
cipherSuite: c.cipherSuite,
|
||||
masterSecret: c.resumptionSecret,
|
||||
serverCertificates: c.peerCertificates,
|
||||
verifiedChains: c.verifiedChains,
|
||||
receivedAt: c.config.time(),
|
||||
nonce: msg.nonce,
|
||||
useBy: c.config.time().Add(lifetime),
|
||||
ageAdd: msg.ageAdd,
|
||||
}
|
||||
|
||||
cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config)
|
||||
c.config.ClientSessionCache.Put(cacheKey, session)
|
||||
|
||||
return nil
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,821 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// serverHandshakeState contains details of a server handshake in progress.
|
||||
// It's discarded once the handshake has completed.
|
||||
type serverHandshakeState struct {
|
||||
c *Conn
|
||||
clientHello *clientHelloMsg
|
||||
hello *serverHelloMsg
|
||||
suite *cipherSuite
|
||||
ellipticOk bool
|
||||
ecdsaOk bool
|
||||
rsaDecryptOk bool
|
||||
rsaSignOk bool
|
||||
sessionState *sessionState
|
||||
finishedHash finishedHash
|
||||
masterSecret []byte
|
||||
cert *Certificate
|
||||
}
|
||||
|
||||
// serverHandshake performs a TLS handshake as a server.
|
||||
func (c *Conn) serverHandshake() error {
|
||||
// If this is the first server handshake, we generate a random key to
|
||||
// encrypt the tickets with.
|
||||
c.config.serverInitOnce.Do(func() { c.config.serverInit(nil) })
|
||||
|
||||
clientHello, err := c.readClientHello()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.vers == VersionTLS13 {
|
||||
hs := serverHandshakeStateTLS13{
|
||||
c: c,
|
||||
clientHello: clientHello,
|
||||
}
|
||||
return hs.handshake()
|
||||
}
|
||||
|
||||
hs := serverHandshakeState{
|
||||
c: c,
|
||||
clientHello: clientHello,
|
||||
}
|
||||
return hs.handshake()
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
if err := hs.processClientHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For an overview of TLS handshaking, see RFC 5246, Section 7.3.
|
||||
c.buffering = true
|
||||
if hs.checkForResumption() {
|
||||
// The client has included a session ticket and so we do an abbreviated handshake.
|
||||
if err := hs.doResumeHandshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
// ticketSupported is set in a resumption handshake if the
|
||||
// ticket from the client was encrypted with an old session
|
||||
// ticket key and thus a refreshed ticket should be sent.
|
||||
if hs.hello.ticketSupported {
|
||||
if err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := hs.sendFinished(c.serverFinished[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientFinishedIsFirst = false
|
||||
if err := hs.readFinished(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
c.didResume = true
|
||||
} else {
|
||||
// The client didn't include a session ticket, or it wasn't
|
||||
// valid so we do a full handshake.
|
||||
if err := hs.pickCipherSuite(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.doFullHandshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readFinished(c.clientFinished[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientFinishedIsFirst = true
|
||||
c.buffering = true
|
||||
if err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendFinished(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readClientHello reads a ClientHello message and selects the protocol version.
|
||||
func (c *Conn) readClientHello() (*clientHelloMsg, error) {
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHello, ok := msg.(*clientHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return nil, unexpectedMessageError(clientHello, msg)
|
||||
}
|
||||
|
||||
if c.config.GetConfigForClient != nil {
|
||||
chi := clientHelloInfo(c, clientHello)
|
||||
if newConfig, err := c.config.GetConfigForClient(chi); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, err
|
||||
} else if newConfig != nil {
|
||||
newConfig.serverInitOnce.Do(func() { newConfig.serverInit(c.config) })
|
||||
c.config = newConfig
|
||||
}
|
||||
}
|
||||
|
||||
clientVersions := clientHello.supportedVersions
|
||||
if len(clientHello.supportedVersions) == 0 {
|
||||
clientVersions = supportedVersionsFromMax(clientHello.vers)
|
||||
}
|
||||
c.vers, ok = c.config.mutualVersion(false, clientVersions)
|
||||
if !ok {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
||||
}
|
||||
c.haveVers = true
|
||||
c.in.version = c.vers
|
||||
c.out.version = c.vers
|
||||
|
||||
return clientHello, nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) processClientHello() error {
|
||||
c := hs.c
|
||||
|
||||
hs.hello = new(serverHelloMsg)
|
||||
hs.hello.vers = c.vers
|
||||
|
||||
supportedCurve := false
|
||||
preferredCurves := c.config.curvePreferences()
|
||||
Curves:
|
||||
for _, curve := range hs.clientHello.supportedCurves {
|
||||
for _, supported := range preferredCurves {
|
||||
if supported == curve {
|
||||
supportedCurve = true
|
||||
break Curves
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
supportedPointFormat := false
|
||||
for _, pointFormat := range hs.clientHello.supportedPoints {
|
||||
if pointFormat == pointFormatUncompressed {
|
||||
supportedPointFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
hs.ellipticOk = supportedCurve && supportedPointFormat
|
||||
|
||||
foundCompression := false
|
||||
// We only support null compression, so check that the client offered it.
|
||||
for _, compression := range hs.clientHello.compressionMethods {
|
||||
if compression == compressionNone {
|
||||
foundCompression = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundCompression {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: client does not support uncompressed connections")
|
||||
}
|
||||
|
||||
hs.hello.random = make([]byte, 32)
|
||||
serverRandom := hs.hello.random
|
||||
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
|
||||
maxVers := c.config.maxSupportedVersion(false)
|
||||
if maxVers >= VersionTLS12 && c.vers < maxVers {
|
||||
if c.vers == VersionTLS12 {
|
||||
copy(serverRandom[24:], downgradeCanaryTLS12)
|
||||
} else {
|
||||
copy(serverRandom[24:], downgradeCanaryTLS11)
|
||||
}
|
||||
serverRandom = serverRandom[:24]
|
||||
}
|
||||
_, err := io.ReadFull(c.config.rand(), serverRandom)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(hs.clientHello.secureRenegotiation) != 0 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
||||
}
|
||||
|
||||
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
|
||||
hs.hello.compressionMethod = compressionNone
|
||||
if len(hs.clientHello.serverName) > 0 {
|
||||
c.serverName = hs.clientHello.serverName
|
||||
}
|
||||
|
||||
if len(hs.clientHello.alpnProtocols) > 0 {
|
||||
if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
|
||||
hs.hello.alpnProtocol = selectedProto
|
||||
c.clientProtocol = selectedProto
|
||||
}
|
||||
} else {
|
||||
// Although sending an empty NPN extension is reasonable, Firefox has
|
||||
// had a bug around this. Best to send nothing at all if
|
||||
// c.config.NextProtos is empty. See
|
||||
// https://golang.org/issue/5445.
|
||||
if hs.clientHello.nextProtoNeg && len(c.config.NextProtos) > 0 {
|
||||
hs.hello.nextProtoNeg = true
|
||||
hs.hello.nextProtos = c.config.NextProtos
|
||||
}
|
||||
}
|
||||
|
||||
hs.cert, err = c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
if hs.clientHello.scts {
|
||||
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
||||
}
|
||||
|
||||
if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
|
||||
switch priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
hs.ecdsaOk = true
|
||||
case *rsa.PublicKey:
|
||||
hs.rsaSignOk = true
|
||||
default:
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
|
||||
}
|
||||
}
|
||||
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
|
||||
switch priv.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
hs.rsaDecryptOk = true
|
||||
default:
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) pickCipherSuite() error {
|
||||
c := hs.c
|
||||
|
||||
var preferenceList, supportedList []uint16
|
||||
if c.config.PreferServerCipherSuites {
|
||||
preferenceList = c.config.cipherSuites()
|
||||
supportedList = hs.clientHello.cipherSuites
|
||||
} else {
|
||||
preferenceList = hs.clientHello.cipherSuites
|
||||
supportedList = c.config.cipherSuites()
|
||||
}
|
||||
|
||||
for _, id := range preferenceList {
|
||||
if hs.setCipherSuite(id, supportedList, c.vers) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hs.suite == nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: no cipher suite supported by both client and server")
|
||||
}
|
||||
|
||||
for _, id := range hs.clientHello.cipherSuites {
|
||||
if id == TLS_FALLBACK_SCSV {
|
||||
// The client is doing a fallback connection. See RFC 7507.
|
||||
if hs.clientHello.vers < c.config.maxSupportedVersion(false) {
|
||||
c.sendAlert(alertInappropriateFallback)
|
||||
return errors.New("tls: client using inappropriate protocol fallback")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkForResumption reports whether we should perform resumption on this connection.
|
||||
func (hs *serverHandshakeState) checkForResumption() bool {
|
||||
c := hs.c
|
||||
|
||||
if c.config.SessionTicketsDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
plaintext, usedOldKey := c.decryptTicket(hs.clientHello.sessionTicket)
|
||||
if plaintext == nil {
|
||||
return false
|
||||
}
|
||||
hs.sessionState = &sessionState{usedOldKey: usedOldKey}
|
||||
ok := hs.sessionState.unmarshal(plaintext)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// Never resume a session for a different TLS version.
|
||||
if c.vers != hs.sessionState.vers {
|
||||
return false
|
||||
}
|
||||
|
||||
cipherSuiteOk := false
|
||||
// Check that the client is still offering the ciphersuite in the session.
|
||||
for _, id := range hs.clientHello.cipherSuites {
|
||||
if id == hs.sessionState.cipherSuite {
|
||||
cipherSuiteOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !cipherSuiteOk {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check that we also support the ciphersuite from the session.
|
||||
if !hs.setCipherSuite(hs.sessionState.cipherSuite, c.config.cipherSuites(), hs.sessionState.vers) {
|
||||
return false
|
||||
}
|
||||
|
||||
sessionHasClientCerts := len(hs.sessionState.certificates) != 0
|
||||
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
||||
if needClientCerts && !sessionHasClientCerts {
|
||||
return false
|
||||
}
|
||||
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) doResumeHandshake() error {
|
||||
c := hs.c
|
||||
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
// We echo the client's session ID in the ServerHello to let it know
|
||||
// that we're doing a resumption.
|
||||
hs.hello.sessionId = hs.clientHello.sessionId
|
||||
hs.hello.ticketSupported = hs.sessionState.usedOldKey
|
||||
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
hs.finishedHash.Write(hs.clientHello.marshal())
|
||||
hs.finishedHash.Write(hs.hello.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.processCertsFromClient(Certificate{
|
||||
Certificate: hs.sessionState.certificates,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.masterSecret = hs.sessionState.masterSecret
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) doFullHandshake() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
|
||||
hs.hello.ocspStapling = true
|
||||
}
|
||||
|
||||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
|
||||
hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
|
||||
if c.config.ClientAuth == NoClientCert {
|
||||
// No need to keep a full record of the handshake if client
|
||||
// certificates won't be used.
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
}
|
||||
hs.finishedHash.Write(hs.clientHello.marshal())
|
||||
hs.finishedHash.Write(hs.hello.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsg)
|
||||
certMsg.certificates = hs.cert.Certificate
|
||||
hs.finishedHash.Write(certMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hs.hello.ocspStapling {
|
||||
certStatus := new(certificateStatusMsg)
|
||||
certStatus.response = hs.cert.OCSPStaple
|
||||
hs.finishedHash.Write(certStatus.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
keyAgreement := hs.suite.ka(c.vers)
|
||||
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
|
||||
if err != nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
if skx != nil {
|
||||
hs.finishedHash.Write(skx.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, skx.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.ClientAuth >= RequestClientCert {
|
||||
// Request a client certificate
|
||||
certReq := new(certificateRequestMsg)
|
||||
certReq.certificateTypes = []byte{
|
||||
byte(certTypeRSASign),
|
||||
byte(certTypeECDSASign),
|
||||
}
|
||||
if c.vers >= VersionTLS12 {
|
||||
certReq.hasSignatureAlgorithm = true
|
||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
|
||||
}
|
||||
|
||||
// An empty list of certificateAuthorities signals to
|
||||
// the client that it may send any certificate in response
|
||||
// to our request. When we know the CAs we trust, then
|
||||
// we can send them down, so that the client can choose
|
||||
// an appropriate certificate to give to us.
|
||||
if c.config.ClientCAs != nil {
|
||||
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
||||
}
|
||||
hs.finishedHash.Write(certReq.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
helloDone := new(serverHelloDoneMsg)
|
||||
hs.finishedHash.Write(helloDone.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, helloDone.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pub crypto.PublicKey // public key for client auth, if any
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we requested a client certificate, then the client must send a
|
||||
// certificate message, even if it's empty.
|
||||
if c.config.ClientAuth >= RequestClientCert {
|
||||
certMsg, ok := msg.(*certificateMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
hs.finishedHash.Write(certMsg.marshal())
|
||||
|
||||
if err := c.processCertsFromClient(Certificate{
|
||||
Certificate: certMsg.certificates,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(certMsg.certificates) != 0 {
|
||||
pub = c.peerCertificates[0].PublicKey
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Get client key exchange
|
||||
ckx, ok := msg.(*clientKeyExchangeMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(ckx, msg)
|
||||
}
|
||||
hs.finishedHash.Write(ckx.marshal())
|
||||
|
||||
preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
|
||||
if err != nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
|
||||
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
// If we received a client cert in response to our certificate request message,
|
||||
// the client will send us a certificateVerifyMsg immediately after the
|
||||
// clientKeyExchangeMsg. This message is a digest of all preceding
|
||||
// handshake-layer messages that is signed using the private key corresponding
|
||||
// to the client's certificate. This allows us to verify that the client is in
|
||||
// possession of the private key of the certificate.
|
||||
if len(c.peerCertificates) > 0 {
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
// Determine the signature type.
|
||||
_, sigType, hashFunc, err := pickSignatureAlgorithm(pub, []SignatureScheme{certVerify.signatureAlgorithm}, supportedSignatureAlgorithms, c.vers)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return err
|
||||
}
|
||||
|
||||
var digest []byte
|
||||
if digest, err = hs.finishedHash.hashForClientCertificate(sigType, hashFunc, hs.masterSecret); err == nil {
|
||||
err = verifyHandshakeSignature(sigType, pub, hashFunc, digest, certVerify.signature)
|
||||
}
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: could not validate signature of connection nonces: " + err.Error())
|
||||
}
|
||||
|
||||
hs.finishedHash.Write(certVerify.marshal())
|
||||
}
|
||||
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) establishKeys() error {
|
||||
c := hs.c
|
||||
|
||||
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
||||
keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
||||
|
||||
var clientCipher, serverCipher interface{}
|
||||
var clientHash, serverHash macFunction
|
||||
|
||||
if hs.suite.aead == nil {
|
||||
clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
|
||||
clientHash = hs.suite.mac(c.vers, clientMAC)
|
||||
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
|
||||
serverHash = hs.suite.mac(c.vers, serverMAC)
|
||||
} else {
|
||||
clientCipher = hs.suite.aead(clientKey, clientIV)
|
||||
serverCipher = hs.suite.aead(serverKey, serverIV)
|
||||
}
|
||||
|
||||
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
||||
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) readFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
if err := c.readChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hs.hello.nextProtoNeg {
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nextProto, ok := msg.(*nextProtoMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(nextProto, msg)
|
||||
}
|
||||
hs.finishedHash.Write(nextProto.marshal())
|
||||
c.clientProtocol = nextProto.proto
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientFinished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(clientFinished, msg)
|
||||
}
|
||||
|
||||
verify := hs.finishedHash.clientSum(hs.masterSecret)
|
||||
if len(verify) != len(clientFinished.verifyData) ||
|
||||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: client's Finished message is incorrect")
|
||||
}
|
||||
|
||||
hs.finishedHash.Write(clientFinished.marshal())
|
||||
copy(out, verify)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) sendSessionTicket() error {
|
||||
if !hs.hello.ticketSupported {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := hs.c
|
||||
m := new(newSessionTicketMsg)
|
||||
|
||||
var certsFromClient [][]byte
|
||||
for _, cert := range c.peerCertificates {
|
||||
certsFromClient = append(certsFromClient, cert.Raw)
|
||||
}
|
||||
state := sessionState{
|
||||
vers: c.vers,
|
||||
cipherSuite: hs.suite.id,
|
||||
masterSecret: hs.masterSecret,
|
||||
certificates: certsFromClient,
|
||||
}
|
||||
var err error
|
||||
m.ticket, err = c.encryptTicket(state.marshal())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.finishedHash.Write(m.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) sendFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
if _, err := c.writeRecord(recordTypeChangeCipherSpec, []byte{1}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished := new(finishedMsg)
|
||||
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
|
||||
hs.finishedHash.Write(finished.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.cipherSuite = hs.suite.id
|
||||
copy(out, finished.verifyData)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processCertsFromClient takes a chain of client certificates either from a
|
||||
// Certificates message or from a sessionState and verifies them. It returns
|
||||
// the public key of the leaf certificate.
|
||||
func (c *Conn) processCertsFromClient(certificate Certificate) error {
|
||||
certificates := certificate.Certificate
|
||||
certs := make([]*x509.Certificate, len(certificates))
|
||||
var err error
|
||||
for i, asn1Data := range certificates {
|
||||
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: failed to parse client certificate: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: client didn't provide a certificate")
|
||||
}
|
||||
|
||||
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: c.config.ClientCAs,
|
||||
CurrentTime: c.config.time(),
|
||||
Intermediates: x509.NewCertPool(),
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
chains, err := certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: failed to verify client's certificate: " + err.Error())
|
||||
}
|
||||
|
||||
c.verifiedChains = chains
|
||||
}
|
||||
|
||||
if c.config.VerifyPeerCertificate != nil {
|
||||
if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(certs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch certs[0].PublicKey.(type) {
|
||||
case *ecdsa.PublicKey, *rsa.PublicKey:
|
||||
default:
|
||||
c.sendAlert(alertUnsupportedCertificate)
|
||||
return fmt.Errorf("tls: client's certificate contains an unsupported public key of type %T", certs[0].PublicKey)
|
||||
}
|
||||
|
||||
c.peerCertificates = certs
|
||||
c.ocspResponse = certificate.OCSPStaple
|
||||
c.scts = certificate.SignedCertificateTimestamps
|
||||
return nil
|
||||
}
|
||||
|
||||
// setCipherSuite sets a cipherSuite with the given id as the serverHandshakeState
|
||||
// suite if that cipher suite is acceptable to use.
|
||||
// It returns a bool indicating if the suite was set.
|
||||
func (hs *serverHandshakeState) setCipherSuite(id uint16, supportedCipherSuites []uint16, version uint16) bool {
|
||||
for _, supported := range supportedCipherSuites {
|
||||
if id == supported {
|
||||
candidate := cipherSuiteByID(id)
|
||||
if candidate == nil {
|
||||
continue
|
||||
}
|
||||
// Don't select a ciphersuite which we can't
|
||||
// support for this client.
|
||||
if candidate.flags&suiteECDHE != 0 {
|
||||
if !hs.ellipticOk {
|
||||
continue
|
||||
}
|
||||
if candidate.flags&suiteECDSA != 0 {
|
||||
if !hs.ecdsaOk {
|
||||
continue
|
||||
}
|
||||
} else if !hs.rsaSignOk {
|
||||
continue
|
||||
}
|
||||
} else if !hs.rsaDecryptOk {
|
||||
continue
|
||||
}
|
||||
if version < VersionTLS12 && candidate.flags&suiteTLS12 != 0 {
|
||||
continue
|
||||
}
|
||||
hs.suite = candidate
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func clientHelloInfo(c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
|
||||
supportedVersions := clientHello.supportedVersions
|
||||
if len(clientHello.supportedVersions) == 0 {
|
||||
supportedVersions = supportedVersionsFromMax(clientHello.vers)
|
||||
}
|
||||
|
||||
return &ClientHelloInfo{
|
||||
CipherSuites: clientHello.cipherSuites,
|
||||
ServerName: clientHello.serverName,
|
||||
SupportedCurves: clientHello.supportedCurves,
|
||||
SupportedPoints: clientHello.supportedPoints,
|
||||
SignatureSchemes: clientHello.supportedSignatureAlgorithms,
|
||||
SupportedProtos: clientHello.alpnProtocols,
|
||||
SupportedVersions: supportedVersions,
|
||||
Conn: c.conn,
|
||||
}
|
||||
}
|
@ -1,856 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// maxClientPSKIdentities is the number of client PSK identities the server will
|
||||
// attempt to validate. It will ignore the rest not to let cheap ClientHello
|
||||
// messages cause too much work in session ticket decryption attempts.
|
||||
const maxClientPSKIdentities = 5
|
||||
|
||||
type serverHandshakeStateTLS13 struct {
|
||||
c *Conn
|
||||
clientHello *clientHelloMsg
|
||||
hello *serverHelloMsg
|
||||
sentDummyCCS bool
|
||||
usingPSK bool
|
||||
suite *cipherSuiteTLS13
|
||||
cert *Certificate
|
||||
sigAlg SignatureScheme
|
||||
earlySecret []byte
|
||||
sharedKey []byte
|
||||
handshakeSecret []byte
|
||||
masterSecret []byte
|
||||
trafficSecret []byte // client_application_traffic_secret_0
|
||||
transcript hash.Hash
|
||||
clientFinished []byte
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
// For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2.
|
||||
if err := hs.processClientHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.checkForResumption(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.pickCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.buffering = true
|
||||
if err := hs.sendServerParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendServerCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendServerFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Note that at this point we could start sending application data without
|
||||
// waiting for the client's second flight, but the application might not
|
||||
// expect the lack of replay protection of the ClientHello parameters.
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readClientCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readClientFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) processClientHello() error {
|
||||
c := hs.c
|
||||
|
||||
hs.hello = new(serverHelloMsg)
|
||||
|
||||
// TLS 1.3 froze the ServerHello.legacy_version field, and uses
|
||||
// supported_versions instead. See RFC 8446, sections 4.1.3 and 4.2.1.
|
||||
hs.hello.vers = VersionTLS12
|
||||
hs.hello.supportedVersion = c.vers
|
||||
|
||||
if len(hs.clientHello.supportedVersions) == 0 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: client used the legacy version field to negotiate TLS 1.3")
|
||||
}
|
||||
|
||||
// Abort if the client is doing a fallback and landing lower than what we
|
||||
// support. See RFC 7507, which however does not specify the interaction
|
||||
// with supported_versions. The only difference is that with
|
||||
// supported_versions a client has a chance to attempt a [TLS 1.2, TLS 1.4]
|
||||
// handshake in case TLS 1.3 is broken but 1.2 is not. Alas, in that case,
|
||||
// it will have to drop the TLS_FALLBACK_SCSV protection if it falls back to
|
||||
// TLS 1.2, because a TLS 1.3 server would abort here. The situation before
|
||||
// supported_versions was not better because there was just no way to do a
|
||||
// TLS 1.4 handshake without risking the server selecting TLS 1.3.
|
||||
for _, id := range hs.clientHello.cipherSuites {
|
||||
if id == TLS_FALLBACK_SCSV {
|
||||
// Use c.vers instead of max(supported_versions) because an attacker
|
||||
// could defeat this by adding an arbitrary high version otherwise.
|
||||
if c.vers < c.config.maxSupportedVersion(false) {
|
||||
c.sendAlert(alertInappropriateFallback)
|
||||
return errors.New("tls: client using inappropriate protocol fallback")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(hs.clientHello.compressionMethods) != 1 ||
|
||||
hs.clientHello.compressionMethods[0] != compressionNone {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: TLS 1.3 client supports illegal compression methods")
|
||||
}
|
||||
|
||||
hs.hello.random = make([]byte, 32)
|
||||
if _, err := io.ReadFull(c.config.rand(), hs.hello.random); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(hs.clientHello.secureRenegotiation) != 0 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
||||
}
|
||||
|
||||
if hs.clientHello.earlyData {
|
||||
// See RFC 8446, Section 4.2.10 for the complicated behavior required
|
||||
// here. The scenario is that a different server at our address offered
|
||||
// to accept early data in the past, which we can't handle. For now, all
|
||||
// 0-RTT enabled session tickets need to expire before a Go server can
|
||||
// replace a server or join a pool. That's the same requirement that
|
||||
// applies to mixing or replacing with any TLS 1.2 server.
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: client sent unexpected early data")
|
||||
}
|
||||
|
||||
hs.hello.sessionId = hs.clientHello.sessionId
|
||||
hs.hello.compressionMethod = compressionNone
|
||||
|
||||
var preferenceList, supportedList []uint16
|
||||
if c.config.PreferServerCipherSuites {
|
||||
preferenceList = defaultCipherSuitesTLS13()
|
||||
supportedList = hs.clientHello.cipherSuites
|
||||
} else {
|
||||
preferenceList = hs.clientHello.cipherSuites
|
||||
supportedList = defaultCipherSuitesTLS13()
|
||||
}
|
||||
for _, suiteID := range preferenceList {
|
||||
hs.suite = mutualCipherSuiteTLS13(supportedList, suiteID)
|
||||
if hs.suite != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if hs.suite == nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: no cipher suite supported by both client and server")
|
||||
}
|
||||
c.cipherSuite = hs.suite.id
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
hs.transcript = hs.suite.hash.New()
|
||||
|
||||
// Pick the ECDHE group in server preference order, but give priority to
|
||||
// groups with a key share, to avoid a HelloRetryRequest round-trip.
|
||||
var selectedGroup CurveID
|
||||
var clientKeyShare *keyShare
|
||||
GroupSelection:
|
||||
for _, preferredGroup := range c.config.curvePreferences() {
|
||||
for _, ks := range hs.clientHello.keyShares {
|
||||
if ks.group == preferredGroup {
|
||||
selectedGroup = ks.group
|
||||
clientKeyShare = &ks
|
||||
break GroupSelection
|
||||
}
|
||||
}
|
||||
if selectedGroup != 0 {
|
||||
continue
|
||||
}
|
||||
for _, group := range hs.clientHello.supportedCurves {
|
||||
if group == preferredGroup {
|
||||
selectedGroup = group
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if selectedGroup == 0 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: no ECDHE curve supported by both client and server")
|
||||
}
|
||||
if clientKeyShare == nil {
|
||||
if err := hs.doHelloRetryRequest(selectedGroup); err != nil {
|
||||
return err
|
||||
}
|
||||
clientKeyShare = &hs.clientHello.keyShares[0]
|
||||
}
|
||||
|
||||
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
hs.hello.serverShare = keyShare{group: selectedGroup, data: params.PublicKey()}
|
||||
hs.sharedKey = params.SharedKey(clientKeyShare.data)
|
||||
if hs.sharedKey == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid client key share")
|
||||
}
|
||||
|
||||
c.serverName = hs.clientHello.serverName
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) checkForResumption() error {
|
||||
c := hs.c
|
||||
|
||||
if c.config.SessionTicketsDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
modeOK := false
|
||||
for _, mode := range hs.clientHello.pskModes {
|
||||
if mode == pskModeDHE {
|
||||
modeOK = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !modeOK {
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(hs.clientHello.pskIdentities) != len(hs.clientHello.pskBinders) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid or missing PSK binders")
|
||||
}
|
||||
if len(hs.clientHello.pskIdentities) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, identity := range hs.clientHello.pskIdentities {
|
||||
if i >= maxClientPSKIdentities {
|
||||
break
|
||||
}
|
||||
|
||||
plaintext, _ := c.decryptTicket(identity.label)
|
||||
if plaintext == nil {
|
||||
continue
|
||||
}
|
||||
sessionState := new(sessionStateTLS13)
|
||||
if ok := sessionState.unmarshal(plaintext); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
||||
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
||||
continue
|
||||
}
|
||||
|
||||
// We don't check the obfuscated ticket age because it's affected by
|
||||
// clock skew and it's only a freshness signal useful for shrinking the
|
||||
// window for replay attacks, which don't affect us as we don't do 0-RTT.
|
||||
|
||||
pskSuite := cipherSuiteTLS13ByID(sessionState.cipherSuite)
|
||||
if pskSuite == nil || pskSuite.hash != hs.suite.hash {
|
||||
continue
|
||||
}
|
||||
|
||||
// PSK connections don't re-establish client certificates, but carry
|
||||
// them over in the session ticket. Ensure the presence of client certs
|
||||
// in the ticket is consistent with the configured requirements.
|
||||
sessionHasClientCerts := len(sessionState.certificate.Certificate) != 0
|
||||
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
||||
if needClientCerts && !sessionHasClientCerts {
|
||||
continue
|
||||
}
|
||||
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
||||
continue
|
||||
}
|
||||
|
||||
psk := hs.suite.expandLabel(sessionState.resumptionSecret, "resumption",
|
||||
nil, hs.suite.hash.Size())
|
||||
hs.earlySecret = hs.suite.extract(psk, nil)
|
||||
binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil)
|
||||
// Clone the transcript in case a HelloRetryRequest was recorded.
|
||||
transcript := cloneHash(hs.transcript, hs.suite.hash)
|
||||
if transcript == nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: internal error: failed to clone hash")
|
||||
}
|
||||
transcript.Write(hs.clientHello.marshalWithoutBinders())
|
||||
pskBinder := hs.suite.finishedHash(binderKey, transcript)
|
||||
if !hmac.Equal(hs.clientHello.pskBinders[i], pskBinder) {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid PSK binder")
|
||||
}
|
||||
|
||||
if err := c.processCertsFromClient(sessionState.certificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.hello.selectedIdentityPresent = true
|
||||
hs.hello.selectedIdentity = uint16(i)
|
||||
hs.usingPSK = true
|
||||
c.didResume = true
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cloneHash uses the encoding.BinaryMarshaler and encoding.BinaryUnmarshaler
|
||||
// interfaces implemented by standard library hashes to clone the state of in
|
||||
// to a new instance of h. It returns nil if the operation fails.
|
||||
func cloneHash(in hash.Hash, h crypto.Hash) hash.Hash {
|
||||
// Recreate the interface to avoid importing encoding.
|
||||
type binaryMarshaler interface {
|
||||
MarshalBinary() (data []byte, err error)
|
||||
UnmarshalBinary(data []byte) error
|
||||
}
|
||||
marshaler, ok := in.(binaryMarshaler)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
state, err := marshaler.MarshalBinary()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
out := h.New()
|
||||
unmarshaler, ok := out.(binaryMarshaler)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if err := unmarshaler.UnmarshalBinary(state); err != nil {
|
||||
return nil
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) pickCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
// Only one of PSK and certificates are used at a time.
|
||||
if hs.usingPSK {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This implements a very simplistic certificate selection strategy for now:
|
||||
// getCertificate delegates to the application Config.GetCertificate, or
|
||||
// selects based on the server_name only. If the selected certificate's
|
||||
// public key does not match the client signature_algorithms, the handshake
|
||||
// is aborted. No attention is given to signature_algorithms_cert, and it is
|
||||
// not passed to the application Config.GetCertificate. This will need to
|
||||
// improve according to RFC 8446, sections 4.4.2.2 and 4.2.3.
|
||||
certificate, err := c.config.getCertificate(clientHelloInfo(c, hs.clientHello))
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
supportedAlgs := signatureSchemesForCertificate(certificate)
|
||||
if supportedAlgs == nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported certificate key (%T)", certificate.PrivateKey)
|
||||
}
|
||||
// Pick signature scheme in client preference order, as the server
|
||||
// preference order is not configurable.
|
||||
for _, preferredAlg := range hs.clientHello.supportedSignatureAlgorithms {
|
||||
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
|
||||
hs.sigAlg = preferredAlg
|
||||
break
|
||||
}
|
||||
}
|
||||
if hs.sigAlg == 0 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: client doesn't support selected certificate")
|
||||
}
|
||||
hs.cert = certificate
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
||||
func (hs *serverHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
if hs.sentDummyCCS {
|
||||
return nil
|
||||
}
|
||||
hs.sentDummyCCS = true
|
||||
|
||||
_, err := hs.c.writeRecord(recordTypeChangeCipherSpec, []byte{1})
|
||||
return err
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) error {
|
||||
c := hs.c
|
||||
|
||||
// The first ClientHello gets double-hashed into the transcript upon a
|
||||
// HelloRetryRequest. See RFC 8446, Section 4.4.1.
|
||||
hs.transcript.Write(hs.clientHello.marshal())
|
||||
chHash := hs.transcript.Sum(nil)
|
||||
hs.transcript.Reset()
|
||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
hs.transcript.Write(chHash)
|
||||
|
||||
helloRetryRequest := &serverHelloMsg{
|
||||
vers: hs.hello.vers,
|
||||
random: helloRetryRequestRandom,
|
||||
sessionId: hs.hello.sessionId,
|
||||
cipherSuite: hs.hello.cipherSuite,
|
||||
compressionMethod: hs.hello.compressionMethod,
|
||||
supportedVersion: hs.hello.supportedVersion,
|
||||
selectedGroup: selectedGroup,
|
||||
}
|
||||
|
||||
hs.transcript.Write(helloRetryRequest.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, helloRetryRequest.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
clientHello, ok := msg.(*clientHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(clientHello, msg)
|
||||
}
|
||||
|
||||
if len(clientHello.keyShares) != 1 || clientHello.keyShares[0].group != selectedGroup {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: client sent invalid key share in second ClientHello")
|
||||
}
|
||||
|
||||
if clientHello.earlyData {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: client indicated early data in second ClientHello")
|
||||
}
|
||||
|
||||
if illegalClientHelloChange(clientHello, hs.clientHello) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: client illegally modified second ClientHello")
|
||||
}
|
||||
|
||||
hs.clientHello = clientHello
|
||||
return nil
|
||||
}
|
||||
|
||||
// illegalClientHelloChange returns whether the two ClientHello messages are
|
||||
// different, with the exception of the changes allowed before and after a
|
||||
// HelloRetryRequest. See RFC 8446, Section 4.1.2.
|
||||
func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool {
|
||||
if len(ch.supportedVersions) != len(ch1.supportedVersions) ||
|
||||
len(ch.cipherSuites) != len(ch1.cipherSuites) ||
|
||||
len(ch.supportedCurves) != len(ch1.supportedCurves) ||
|
||||
len(ch.supportedSignatureAlgorithms) != len(ch1.supportedSignatureAlgorithms) ||
|
||||
len(ch.supportedSignatureAlgorithmsCert) != len(ch1.supportedSignatureAlgorithmsCert) ||
|
||||
len(ch.alpnProtocols) != len(ch1.alpnProtocols) {
|
||||
return true
|
||||
}
|
||||
for i := range ch.supportedVersions {
|
||||
if ch.supportedVersions[i] != ch1.supportedVersions[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := range ch.cipherSuites {
|
||||
if ch.cipherSuites[i] != ch1.cipherSuites[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := range ch.supportedCurves {
|
||||
if ch.supportedCurves[i] != ch1.supportedCurves[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := range ch.supportedSignatureAlgorithms {
|
||||
if ch.supportedSignatureAlgorithms[i] != ch1.supportedSignatureAlgorithms[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := range ch.supportedSignatureAlgorithmsCert {
|
||||
if ch.supportedSignatureAlgorithmsCert[i] != ch1.supportedSignatureAlgorithmsCert[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for i := range ch.alpnProtocols {
|
||||
if ch.alpnProtocols[i] != ch1.alpnProtocols[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return ch.vers != ch1.vers ||
|
||||
!bytes.Equal(ch.random, ch1.random) ||
|
||||
!bytes.Equal(ch.sessionId, ch1.sessionId) ||
|
||||
!bytes.Equal(ch.compressionMethods, ch1.compressionMethods) ||
|
||||
ch.nextProtoNeg != ch1.nextProtoNeg ||
|
||||
ch.serverName != ch1.serverName ||
|
||||
ch.ocspStapling != ch1.ocspStapling ||
|
||||
!bytes.Equal(ch.supportedPoints, ch1.supportedPoints) ||
|
||||
ch.ticketSupported != ch1.ticketSupported ||
|
||||
!bytes.Equal(ch.sessionTicket, ch1.sessionTicket) ||
|
||||
ch.secureRenegotiationSupported != ch1.secureRenegotiationSupported ||
|
||||
!bytes.Equal(ch.secureRenegotiation, ch1.secureRenegotiation) ||
|
||||
ch.scts != ch1.scts ||
|
||||
!bytes.Equal(ch.cookie, ch1.cookie) ||
|
||||
!bytes.Equal(ch.pskModes, ch1.pskModes)
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) sendServerParameters() error {
|
||||
c := hs.c
|
||||
|
||||
hs.transcript.Write(hs.clientHello.marshal())
|
||||
hs.transcript.Write(hs.hello.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hs.hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
earlySecret := hs.earlySecret
|
||||
if earlySecret == nil {
|
||||
earlySecret = hs.suite.extract(nil, nil)
|
||||
}
|
||||
hs.handshakeSecret = hs.suite.extract(hs.sharedKey,
|
||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
||||
|
||||
clientSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
||||
clientHandshakeTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, clientSecret)
|
||||
serverSecret := hs.suite.deriveSecret(hs.handshakeSecret,
|
||||
serverHandshakeTrafficLabel, hs.transcript)
|
||||
c.out.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.clientHello.random, clientSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.clientHello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedExtensions := new(encryptedExtensionsMsg)
|
||||
|
||||
if len(hs.clientHello.alpnProtocols) > 0 {
|
||||
if selectedProto, fallback := mutualProtocol(hs.clientHello.alpnProtocols, c.config.NextProtos); !fallback {
|
||||
encryptedExtensions.alpnProtocol = selectedProto
|
||||
c.clientProtocol = selectedProto
|
||||
}
|
||||
}
|
||||
|
||||
hs.transcript.Write(encryptedExtensions.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, encryptedExtensions.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) requestClientCert() bool {
|
||||
return hs.c.config.ClientAuth >= RequestClientCert && !hs.usingPSK
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) sendServerCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
// Only one of PSK and certificates are used at a time.
|
||||
if hs.usingPSK {
|
||||
return nil
|
||||
}
|
||||
|
||||
if hs.requestClientCert() {
|
||||
// Request a client certificate
|
||||
certReq := new(certificateRequestMsgTLS13)
|
||||
certReq.ocspStapling = true
|
||||
certReq.scts = true
|
||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms
|
||||
if c.config.ClientCAs != nil {
|
||||
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
||||
}
|
||||
|
||||
hs.transcript.Write(certReq.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certReq.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsgTLS13)
|
||||
|
||||
certMsg.certificate = *hs.cert
|
||||
certMsg.scts = hs.clientHello.scts && len(hs.cert.SignedCertificateTimestamps) > 0
|
||||
certMsg.ocspStapling = hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0
|
||||
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certVerifyMsg := new(certificateVerifyMsg)
|
||||
certVerifyMsg.hasSignatureAlgorithm = true
|
||||
certVerifyMsg.signatureAlgorithm = hs.sigAlg
|
||||
|
||||
sigType := signatureFromSignatureScheme(hs.sigAlg)
|
||||
sigHash, err := hashFromSignatureScheme(hs.sigAlg)
|
||||
if sigType == 0 || err != nil {
|
||||
// getCertificate returned a certificate incompatible with the
|
||||
// ClientHello supported signature algorithms.
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
h := sigHash.New()
|
||||
writeSignedMessage(h, serverSignatureContext, hs.transcript)
|
||||
|
||||
signOpts := crypto.SignerOpts(sigHash)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
||||
}
|
||||
sig, err := hs.cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), h.Sum(nil), signOpts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
||||
}
|
||||
certVerifyMsg.signature = sig
|
||||
|
||||
hs.transcript.Write(certVerifyMsg.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, certVerifyMsg.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) sendServerFinished() error {
|
||||
c := hs.c
|
||||
|
||||
finished := &finishedMsg{
|
||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
||||
}
|
||||
|
||||
hs.transcript.Write(finished.marshal())
|
||||
if _, err := c.writeRecord(recordTypeHandshake, finished.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Derive secrets that take context through the server Finished.
|
||||
|
||||
hs.masterSecret = hs.suite.extract(nil,
|
||||
hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil))
|
||||
|
||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
clientApplicationTrafficLabel, hs.transcript)
|
||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
||||
serverApplicationTrafficLabel, hs.transcript)
|
||||
c.out.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err := c.config.writeKeyLog(keyLogLabelClientTraffic, hs.clientHello.random, hs.trafficSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.clientHello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
||||
|
||||
// If we did not request client certificates, at this point we can
|
||||
// precompute the client finished and roll the transcript forward to send
|
||||
// session tickets in our first flight.
|
||||
if !hs.requestClientCert() {
|
||||
if err := hs.sendSessionTickets(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) shouldSendSessionTickets() bool {
|
||||
if hs.c.config.SessionTicketsDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
// Don't send tickets the client wouldn't use. See RFC 8446, Section 4.2.9.
|
||||
for _, pskMode := range hs.clientHello.pskModes {
|
||||
if pskMode == pskModeDHE {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) sendSessionTickets() error {
|
||||
c := hs.c
|
||||
|
||||
hs.clientFinished = hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
||||
finishedMsg := &finishedMsg{
|
||||
verifyData: hs.clientFinished,
|
||||
}
|
||||
hs.transcript.Write(finishedMsg.marshal())
|
||||
|
||||
if !hs.shouldSendSessionTickets() {
|
||||
return nil
|
||||
}
|
||||
|
||||
resumptionSecret := hs.suite.deriveSecret(hs.masterSecret,
|
||||
resumptionLabel, hs.transcript)
|
||||
|
||||
m := new(newSessionTicketMsgTLS13)
|
||||
|
||||
var certsFromClient [][]byte
|
||||
for _, cert := range c.peerCertificates {
|
||||
certsFromClient = append(certsFromClient, cert.Raw)
|
||||
}
|
||||
state := sessionStateTLS13{
|
||||
cipherSuite: hs.suite.id,
|
||||
createdAt: uint64(c.config.time().Unix()),
|
||||
resumptionSecret: resumptionSecret,
|
||||
certificate: Certificate{
|
||||
Certificate: certsFromClient,
|
||||
OCSPStaple: c.ocspResponse,
|
||||
SignedCertificateTimestamps: c.scts,
|
||||
},
|
||||
}
|
||||
var err error
|
||||
m.label, err = c.encryptTicket(state.marshal())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.lifetime = uint32(maxSessionTicketLifetime / time.Second)
|
||||
|
||||
if _, err := c.writeRecord(recordTypeHandshake, m.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) readClientCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
if !hs.requestClientCert() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If we requested a client certificate, then the client must send a
|
||||
// certificate message. If it's empty, no CertificateVerify is sent.
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
hs.transcript.Write(certMsg.marshal())
|
||||
|
||||
if err := c.processCertsFromClient(certMsg.certificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(certMsg.certificate.Certificate) != 0 {
|
||||
msg, err = c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.4.3.
|
||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid certificate signature algorithm")
|
||||
}
|
||||
sigType := signatureFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
sigHash, err := hashFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
if sigType == 0 || err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid certificate signature algorithm")
|
||||
}
|
||||
h := sigHash.New()
|
||||
writeSignedMessage(h, clientSignatureContext, hs.transcript)
|
||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
||||
sigHash, h.Sum(nil), certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid certificate signature")
|
||||
}
|
||||
|
||||
hs.transcript.Write(certVerify.marshal())
|
||||
}
|
||||
|
||||
// If we waited until the client certificates to send session tickets, we
|
||||
// are ready to do it now.
|
||||
if err := hs.sendSessionTickets(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeStateTLS13) readClientFinished() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(finished, msg)
|
||||
}
|
||||
|
||||
if !hmac.Equal(hs.clientFinished, finished.verifyData) {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid client finished hash")
|
||||
}
|
||||
|
||||
c.in.setTrafficSecret(hs.suite, hs.trafficSecret)
|
||||
|
||||
return nil
|
||||
}
|
@ -1,313 +0,0 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/md5"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
|
||||
var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
|
||||
|
||||
// rsaKeyAgreement implements the standard TLS key agreement where the client
|
||||
// encrypts the pre-master secret to the server's public key.
|
||||
type rsaKeyAgreement struct{}
|
||||
|
||||
func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
|
||||
if len(ckx.ciphertext) < 2 {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
|
||||
ciphertext := ckx.ciphertext
|
||||
if version != VersionSSL30 {
|
||||
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
|
||||
if ciphertextLen != len(ckx.ciphertext)-2 {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
ciphertext = ckx.ciphertext[2:]
|
||||
}
|
||||
priv, ok := cert.PrivateKey.(crypto.Decrypter)
|
||||
if !ok {
|
||||
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
|
||||
}
|
||||
// Perform constant time RSA PKCS#1 v1.5 decryption
|
||||
preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// We don't check the version number in the premaster secret. For one,
|
||||
// by checking it, we would leak information about the validity of the
|
||||
// encrypted pre-master secret. Secondly, it provides only a small
|
||||
// benefit against a downgrade attack and some implementations send the
|
||||
// wrong version anyway. See the discussion at the end of section
|
||||
// 7.4.7.1 of RFC 4346.
|
||||
return preMasterSecret, nil
|
||||
}
|
||||
|
||||
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
|
||||
return errors.New("tls: unexpected ServerKeyExchange")
|
||||
}
|
||||
|
||||
func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
|
||||
preMasterSecret := make([]byte, 48)
|
||||
preMasterSecret[0] = byte(clientHello.vers >> 8)
|
||||
preMasterSecret[1] = byte(clientHello.vers)
|
||||
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
encrypted, err := rsa.EncryptPKCS1v15(config.rand(), cert.PublicKey.(*rsa.PublicKey), preMasterSecret)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
ckx := new(clientKeyExchangeMsg)
|
||||
ckx.ciphertext = make([]byte, len(encrypted)+2)
|
||||
ckx.ciphertext[0] = byte(len(encrypted) >> 8)
|
||||
ckx.ciphertext[1] = byte(len(encrypted))
|
||||
copy(ckx.ciphertext[2:], encrypted)
|
||||
return preMasterSecret, ckx, nil
|
||||
}
|
||||
|
||||
// sha1Hash calculates a SHA1 hash over the given byte slices.
|
||||
func sha1Hash(slices [][]byte) []byte {
|
||||
hsha1 := sha1.New()
|
||||
for _, slice := range slices {
|
||||
hsha1.Write(slice)
|
||||
}
|
||||
return hsha1.Sum(nil)
|
||||
}
|
||||
|
||||
// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
|
||||
// concatenation of an MD5 and SHA1 hash.
|
||||
func md5SHA1Hash(slices [][]byte) []byte {
|
||||
md5sha1 := make([]byte, md5.Size+sha1.Size)
|
||||
hmd5 := md5.New()
|
||||
for _, slice := range slices {
|
||||
hmd5.Write(slice)
|
||||
}
|
||||
copy(md5sha1, hmd5.Sum(nil))
|
||||
copy(md5sha1[md5.Size:], sha1Hash(slices))
|
||||
return md5sha1
|
||||
}
|
||||
|
||||
// hashForServerKeyExchange hashes the given slices and returns their digest
|
||||
// using the given hash function (for >= TLS 1.2) or using a default based on
|
||||
// the sigType (for earlier TLS versions).
|
||||
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) ([]byte, error) {
|
||||
if version >= VersionTLS12 {
|
||||
h := hashFunc.New()
|
||||
for _, slice := range slices {
|
||||
h.Write(slice)
|
||||
}
|
||||
digest := h.Sum(nil)
|
||||
return digest, nil
|
||||
}
|
||||
if sigType == signatureECDSA {
|
||||
return sha1Hash(slices), nil
|
||||
}
|
||||
return md5SHA1Hash(slices), nil
|
||||
}
|
||||
|
||||
// ecdheKeyAgreement implements a TLS key agreement where the server
|
||||
// generates an ephemeral EC public/private key pair and signs it. The
|
||||
// pre-master secret is then calculated using ECDH. The signature may
|
||||
// either be ECDSA or RSA.
|
||||
type ecdheKeyAgreement struct {
|
||||
version uint16
|
||||
isRSA bool
|
||||
params ecdheParameters
|
||||
|
||||
// ckx and preMasterSecret are generated in processServerKeyExchange
|
||||
// and returned in generateClientKeyExchange.
|
||||
ckx *clientKeyExchangeMsg
|
||||
preMasterSecret []byte
|
||||
}
|
||||
|
||||
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
|
||||
preferredCurves := config.curvePreferences()
|
||||
|
||||
var curveID CurveID
|
||||
NextCandidate:
|
||||
for _, candidate := range preferredCurves {
|
||||
for _, c := range clientHello.supportedCurves {
|
||||
if candidate == c {
|
||||
curveID = c
|
||||
break NextCandidate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if curveID == 0 {
|
||||
return nil, errors.New("tls: no supported elliptic curves offered")
|
||||
}
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
|
||||
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ka.params = params
|
||||
|
||||
// See RFC 4492, Section 5.4.
|
||||
ecdhePublic := params.PublicKey()
|
||||
serverECDHParams := make([]byte, 1+2+1+len(ecdhePublic))
|
||||
serverECDHParams[0] = 3 // named curve
|
||||
serverECDHParams[1] = byte(curveID >> 8)
|
||||
serverECDHParams[2] = byte(curveID)
|
||||
serverECDHParams[3] = byte(len(ecdhePublic))
|
||||
copy(serverECDHParams[4:], ecdhePublic)
|
||||
|
||||
priv, ok := cert.PrivateKey.(crypto.Signer)
|
||||
if !ok {
|
||||
return nil, errors.New("tls: certificate private key does not implement crypto.Signer")
|
||||
}
|
||||
|
||||
signatureAlgorithm, sigType, hashFunc, err := pickSignatureAlgorithm(priv.Public(), clientHello.supportedSignatureAlgorithms, supportedSignatureAlgorithms, ka.version)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
|
||||
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
|
||||
}
|
||||
|
||||
digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, hello.random, serverECDHParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signOpts := crypto.SignerOpts(hashFunc)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: hashFunc}
|
||||
}
|
||||
sig, err := priv.Sign(config.rand(), digest, signOpts)
|
||||
if err != nil {
|
||||
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
|
||||
}
|
||||
|
||||
skx := new(serverKeyExchangeMsg)
|
||||
sigAndHashLen := 0
|
||||
if ka.version >= VersionTLS12 {
|
||||
sigAndHashLen = 2
|
||||
}
|
||||
skx.key = make([]byte, len(serverECDHParams)+sigAndHashLen+2+len(sig))
|
||||
copy(skx.key, serverECDHParams)
|
||||
k := skx.key[len(serverECDHParams):]
|
||||
if ka.version >= VersionTLS12 {
|
||||
k[0] = byte(signatureAlgorithm >> 8)
|
||||
k[1] = byte(signatureAlgorithm)
|
||||
k = k[2:]
|
||||
}
|
||||
k[0] = byte(len(sig) >> 8)
|
||||
k[1] = byte(len(sig))
|
||||
copy(k[2:], sig)
|
||||
|
||||
return skx, nil
|
||||
}
|
||||
|
||||
func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
|
||||
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
|
||||
preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
|
||||
if preMasterSecret == nil {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
|
||||
return preMasterSecret, nil
|
||||
}
|
||||
|
||||
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
|
||||
if len(skx.key) < 4 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
if skx.key[0] != 3 { // named curve
|
||||
return errors.New("tls: server selected unsupported curve")
|
||||
}
|
||||
curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
|
||||
|
||||
publicLen := int(skx.key[3])
|
||||
if publicLen+4 > len(skx.key) {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
serverECDHParams := skx.key[:4+publicLen]
|
||||
publicKey := serverECDHParams[4:]
|
||||
|
||||
sig := skx.key[4+publicLen:]
|
||||
if len(sig) < 2 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
return errors.New("tls: server selected unsupported curve")
|
||||
}
|
||||
|
||||
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ka.params = params
|
||||
|
||||
ka.preMasterSecret = params.SharedKey(publicKey)
|
||||
if ka.preMasterSecret == nil {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
|
||||
ourPublicKey := params.PublicKey()
|
||||
ka.ckx = new(clientKeyExchangeMsg)
|
||||
ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
|
||||
ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
|
||||
copy(ka.ckx.ciphertext[1:], ourPublicKey)
|
||||
|
||||
var signatureAlgorithm SignatureScheme
|
||||
if ka.version >= VersionTLS12 {
|
||||
// handle SignatureAndHashAlgorithm
|
||||
signatureAlgorithm = SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
|
||||
sig = sig[2:]
|
||||
if len(sig) < 2 {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
}
|
||||
_, sigType, hashFunc, err := pickSignatureAlgorithm(cert.PublicKey, []SignatureScheme{signatureAlgorithm}, clientHello.supportedSignatureAlgorithms, ka.version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
|
||||
sigLen := int(sig[0])<<8 | int(sig[1])
|
||||
if sigLen+2 != len(sig) {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
sig = sig[2:]
|
||||
|
||||
digest, err := hashForServerKeyExchange(sigType, hashFunc, ka.version, clientHello.random, serverHello.random, serverECDHParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return verifyHandshakeSignature(sigType, cert.PublicKey, hashFunc, digest, sig)
|
||||
}
|
||||
|
||||
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
|
||||
if ka.ckx == nil {
|
||||
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
|
||||
}
|
||||
|
||||
return ka.preMasterSecret, ka.ckx, nil
|
||||
}
|
@ -1,200 +0,0 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// This file contains the functions necessary to compute the TLS 1.3 key
|
||||
// schedule. See RFC 8446, Section 7.
|
||||
|
||||
const (
|
||||
resumptionBinderLabel = "res binder"
|
||||
clientHandshakeTrafficLabel = "c hs traffic"
|
||||
serverHandshakeTrafficLabel = "s hs traffic"
|
||||
clientApplicationTrafficLabel = "c ap traffic"
|
||||
serverApplicationTrafficLabel = "s ap traffic"
|
||||
exporterLabel = "exp master"
|
||||
resumptionLabel = "res master"
|
||||
trafficUpdateLabel = "traffic upd"
|
||||
)
|
||||
|
||||
// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
|
||||
func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
|
||||
var hkdfLabel cryptobyte.Builder
|
||||
hkdfLabel.AddUint16(uint16(length))
|
||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes([]byte("tls13 "))
|
||||
b.AddBytes([]byte(label))
|
||||
})
|
||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(context)
|
||||
})
|
||||
out := make([]byte, length)
|
||||
n, err := hkdf.Expand(c.hash.New, secret, hkdfLabel.BytesOrPanic()).Read(out)
|
||||
if err != nil || n != length {
|
||||
panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
|
||||
func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
|
||||
if transcript == nil {
|
||||
transcript = c.hash.New()
|
||||
}
|
||||
return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
|
||||
}
|
||||
|
||||
// extract implements HKDF-Extract with the cipher suite hash.
|
||||
func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
|
||||
if newSecret == nil {
|
||||
newSecret = make([]byte, c.hash.Size())
|
||||
}
|
||||
return hkdf.Extract(c.hash.New, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// nextTrafficSecret generates the next traffic secret, given the current one,
|
||||
// according to RFC 8446, Section 7.2.
|
||||
func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
|
||||
return c.expandLabel(trafficSecret, trafficUpdateLabel, nil, c.hash.Size())
|
||||
}
|
||||
|
||||
// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
|
||||
func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
|
||||
key = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
|
||||
iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength)
|
||||
return
|
||||
}
|
||||
|
||||
// finishedHash generates the Finished verify_data or PskBinderEntry according
|
||||
// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
|
||||
// selection.
|
||||
func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
|
||||
finishedKey := c.expandLabel(baseKey, "finished", nil, c.hash.Size())
|
||||
verifyData := hmac.New(c.hash.New, finishedKey)
|
||||
verifyData.Write(transcript.Sum(nil))
|
||||
return verifyData.Sum(nil)
|
||||
}
|
||||
|
||||
// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
|
||||
// RFC 8446, Section 7.5.
|
||||
func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
|
||||
expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
|
||||
return func(label string, context []byte, length int) ([]byte, error) {
|
||||
secret := c.deriveSecret(expMasterSecret, label, nil)
|
||||
h := c.hash.New()
|
||||
h.Write(context)
|
||||
return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
|
||||
}
|
||||
}
|
||||
|
||||
// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
|
||||
// according to RFC 8446, Section 4.2.8.2.
|
||||
type ecdheParameters interface {
|
||||
CurveID() CurveID
|
||||
PublicKey() []byte
|
||||
SharedKey(peerPublicKey []byte) []byte
|
||||
}
|
||||
|
||||
func generateECDHEParameters(rand io.Reader, curveID CurveID) (ecdheParameters, error) {
|
||||
if curveID == X25519 {
|
||||
p := &x25519Parameters{}
|
||||
if _, err := io.ReadFull(rand, p.privateKey[:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
curve25519.ScalarBaseMult(&p.publicKey, &p.privateKey)
|
||||
return p, nil
|
||||
}
|
||||
|
||||
curve, ok := curveForCurveID(curveID)
|
||||
if !ok {
|
||||
return nil, errors.New("tls: internal error: unsupported curve")
|
||||
}
|
||||
|
||||
p := &nistParameters{curveID: curveID}
|
||||
var err error
|
||||
p.privateKey, p.x, p.y, err = elliptic.GenerateKey(curve, rand)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
|
||||
switch id {
|
||||
case CurveP256:
|
||||
return elliptic.P256(), true
|
||||
case CurveP384:
|
||||
return elliptic.P384(), true
|
||||
case CurveP521:
|
||||
return elliptic.P521(), true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
type nistParameters struct {
|
||||
privateKey []byte
|
||||
x, y *big.Int // public key
|
||||
curveID CurveID
|
||||
}
|
||||
|
||||
func (p *nistParameters) CurveID() CurveID {
|
||||
return p.curveID
|
||||
}
|
||||
|
||||
func (p *nistParameters) PublicKey() []byte {
|
||||
curve, _ := curveForCurveID(p.curveID)
|
||||
return elliptic.Marshal(curve, p.x, p.y)
|
||||
}
|
||||
|
||||
func (p *nistParameters) SharedKey(peerPublicKey []byte) []byte {
|
||||
curve, _ := curveForCurveID(p.curveID)
|
||||
// Unmarshal also checks whether the given point is on the curve.
|
||||
x, y := elliptic.Unmarshal(curve, peerPublicKey)
|
||||
if x == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
xShared, _ := curve.ScalarMult(x, y, p.privateKey)
|
||||
sharedKey := make([]byte, (curve.Params().BitSize+7)>>3)
|
||||
xBytes := xShared.Bytes()
|
||||
copy(sharedKey[len(sharedKey)-len(xBytes):], xBytes)
|
||||
|
||||
return sharedKey
|
||||
}
|
||||
|
||||
type x25519Parameters struct {
|
||||
privateKey [32]byte
|
||||
publicKey [32]byte
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) CurveID() CurveID {
|
||||
return X25519
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) PublicKey() []byte {
|
||||
return p.publicKey[:]
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
|
||||
if len(peerPublicKey) != 32 {
|
||||
return nil
|
||||
}
|
||||
var theirPublicKey, sharedKey [32]byte
|
||||
copy(theirPublicKey[:], peerPublicKey)
|
||||
curve25519.ScalarMult(&sharedKey, &p.privateKey, &theirPublicKey)
|
||||
return sharedKey[:]
|
||||
}
|
@ -1,385 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Split a premaster secret in two as specified in RFC 4346, Section 5.
|
||||
func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
|
||||
s1 = secret[0 : (len(secret)+1)/2]
|
||||
s2 = secret[len(secret)/2:]
|
||||
return
|
||||
}
|
||||
|
||||
// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
|
||||
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
|
||||
h := hmac.New(hash, secret)
|
||||
h.Write(seed)
|
||||
a := h.Sum(nil)
|
||||
|
||||
j := 0
|
||||
for j < len(result) {
|
||||
h.Reset()
|
||||
h.Write(a)
|
||||
h.Write(seed)
|
||||
b := h.Sum(nil)
|
||||
copy(result[j:], b)
|
||||
j += len(b)
|
||||
|
||||
h.Reset()
|
||||
h.Write(a)
|
||||
a = h.Sum(nil)
|
||||
}
|
||||
}
|
||||
|
||||
// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
|
||||
func prf10(result, secret, label, seed []byte) {
|
||||
hashSHA1 := sha1.New
|
||||
hashMD5 := md5.New
|
||||
|
||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
||||
copy(labelAndSeed, label)
|
||||
copy(labelAndSeed[len(label):], seed)
|
||||
|
||||
s1, s2 := splitPreMasterSecret(secret)
|
||||
pHash(result, s1, labelAndSeed, hashMD5)
|
||||
result2 := make([]byte, len(result))
|
||||
pHash(result2, s2, labelAndSeed, hashSHA1)
|
||||
|
||||
for i, b := range result2 {
|
||||
result[i] ^= b
|
||||
}
|
||||
}
|
||||
|
||||
// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
|
||||
func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
|
||||
return func(result, secret, label, seed []byte) {
|
||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
||||
copy(labelAndSeed, label)
|
||||
copy(labelAndSeed[len(label):], seed)
|
||||
|
||||
pHash(result, secret, labelAndSeed, hashFunc)
|
||||
}
|
||||
}
|
||||
|
||||
// prf30 implements the SSL 3.0 pseudo-random function, as defined in
|
||||
// www.mozilla.org/projects/security/pki/nss/ssl/draft302.txt section 6.
|
||||
func prf30(result, secret, label, seed []byte) {
|
||||
hashSHA1 := sha1.New()
|
||||
hashMD5 := md5.New()
|
||||
|
||||
done := 0
|
||||
i := 0
|
||||
// RFC 5246 section 6.3 says that the largest PRF output needed is 128
|
||||
// bytes. Since no more ciphersuites will be added to SSLv3, this will
|
||||
// remain true. Each iteration gives us 16 bytes so 10 iterations will
|
||||
// be sufficient.
|
||||
var b [11]byte
|
||||
for done < len(result) {
|
||||
for j := 0; j <= i; j++ {
|
||||
b[j] = 'A' + byte(i)
|
||||
}
|
||||
|
||||
hashSHA1.Reset()
|
||||
hashSHA1.Write(b[:i+1])
|
||||
hashSHA1.Write(secret)
|
||||
hashSHA1.Write(seed)
|
||||
digest := hashSHA1.Sum(nil)
|
||||
|
||||
hashMD5.Reset()
|
||||
hashMD5.Write(secret)
|
||||
hashMD5.Write(digest)
|
||||
|
||||
done += copy(result[done:], hashMD5.Sum(nil))
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
|
||||
finishedVerifyLength = 12 // Length of verify_data in a Finished message.
|
||||
)
|
||||
|
||||
var masterSecretLabel = []byte("master secret")
|
||||
var keyExpansionLabel = []byte("key expansion")
|
||||
var clientFinishedLabel = []byte("client finished")
|
||||
var serverFinishedLabel = []byte("server finished")
|
||||
|
||||
func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) {
|
||||
switch version {
|
||||
case VersionSSL30:
|
||||
return prf30, crypto.Hash(0)
|
||||
case VersionTLS10, VersionTLS11:
|
||||
return prf10, crypto.Hash(0)
|
||||
case VersionTLS12:
|
||||
if suite.flags&suiteSHA384 != 0 {
|
||||
return prf12(sha512.New384), crypto.SHA384
|
||||
}
|
||||
return prf12(sha256.New), crypto.SHA256
|
||||
default:
|
||||
panic("unknown version")
|
||||
}
|
||||
}
|
||||
|
||||
func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) {
|
||||
prf, _ := prfAndHashForVersion(version, suite)
|
||||
return prf
|
||||
}
|
||||
|
||||
// masterFromPreMasterSecret generates the master secret from the pre-master
|
||||
// secret. See RFC 5246, Section 8.1.
|
||||
func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
|
||||
seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
|
||||
seed = append(seed, clientRandom...)
|
||||
seed = append(seed, serverRandom...)
|
||||
|
||||
masterSecret := make([]byte, masterSecretLength)
|
||||
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
|
||||
return masterSecret
|
||||
}
|
||||
|
||||
// keysFromMasterSecret generates the connection keys from the master
|
||||
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
|
||||
// RFC 2246, Section 6.3.
|
||||
func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
|
||||
seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
|
||||
seed = append(seed, serverRandom...)
|
||||
seed = append(seed, clientRandom...)
|
||||
|
||||
n := 2*macLen + 2*keyLen + 2*ivLen
|
||||
keyMaterial := make([]byte, n)
|
||||
prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
|
||||
clientMAC = keyMaterial[:macLen]
|
||||
keyMaterial = keyMaterial[macLen:]
|
||||
serverMAC = keyMaterial[:macLen]
|
||||
keyMaterial = keyMaterial[macLen:]
|
||||
clientKey = keyMaterial[:keyLen]
|
||||
keyMaterial = keyMaterial[keyLen:]
|
||||
serverKey = keyMaterial[:keyLen]
|
||||
keyMaterial = keyMaterial[keyLen:]
|
||||
clientIV = keyMaterial[:ivLen]
|
||||
keyMaterial = keyMaterial[ivLen:]
|
||||
serverIV = keyMaterial[:ivLen]
|
||||
return
|
||||
}
|
||||
|
||||
// hashFromSignatureScheme returns the corresponding crypto.Hash for a given
|
||||
// hash from a TLS SignatureScheme.
|
||||
func hashFromSignatureScheme(signatureAlgorithm SignatureScheme) (crypto.Hash, error) {
|
||||
switch signatureAlgorithm {
|
||||
case PKCS1WithSHA1, ECDSAWithSHA1:
|
||||
return crypto.SHA1, nil
|
||||
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
|
||||
return crypto.SHA256, nil
|
||||
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
|
||||
return crypto.SHA384, nil
|
||||
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
|
||||
return crypto.SHA512, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("tls: unsupported signature algorithm: %#04x", signatureAlgorithm)
|
||||
}
|
||||
}
|
||||
|
||||
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
|
||||
var buffer []byte
|
||||
if version == VersionSSL30 || version >= VersionTLS12 {
|
||||
buffer = []byte{}
|
||||
}
|
||||
|
||||
prf, hash := prfAndHashForVersion(version, cipherSuite)
|
||||
if hash != 0 {
|
||||
return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
|
||||
}
|
||||
|
||||
return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
|
||||
}
|
||||
|
||||
// A finishedHash calculates the hash of a set of handshake messages suitable
|
||||
// for including in a Finished message.
|
||||
type finishedHash struct {
|
||||
client hash.Hash
|
||||
server hash.Hash
|
||||
|
||||
// Prior to TLS 1.2, an additional MD5 hash is required.
|
||||
clientMD5 hash.Hash
|
||||
serverMD5 hash.Hash
|
||||
|
||||
// In TLS 1.2, a full buffer is sadly required.
|
||||
buffer []byte
|
||||
|
||||
version uint16
|
||||
prf func(result, secret, label, seed []byte)
|
||||
}
|
||||
|
||||
func (h *finishedHash) Write(msg []byte) (n int, err error) {
|
||||
h.client.Write(msg)
|
||||
h.server.Write(msg)
|
||||
|
||||
if h.version < VersionTLS12 {
|
||||
h.clientMD5.Write(msg)
|
||||
h.serverMD5.Write(msg)
|
||||
}
|
||||
|
||||
if h.buffer != nil {
|
||||
h.buffer = append(h.buffer, msg...)
|
||||
}
|
||||
|
||||
return len(msg), nil
|
||||
}
|
||||
|
||||
func (h finishedHash) Sum() []byte {
|
||||
if h.version >= VersionTLS12 {
|
||||
return h.client.Sum(nil)
|
||||
}
|
||||
|
||||
out := make([]byte, 0, md5.Size+sha1.Size)
|
||||
out = h.clientMD5.Sum(out)
|
||||
return h.client.Sum(out)
|
||||
}
|
||||
|
||||
// finishedSum30 calculates the contents of the verify_data member of a SSLv3
|
||||
// Finished message given the MD5 and SHA1 hashes of a set of handshake
|
||||
// messages.
|
||||
func finishedSum30(md5, sha1 hash.Hash, masterSecret []byte, magic []byte) []byte {
|
||||
md5.Write(magic)
|
||||
md5.Write(masterSecret)
|
||||
md5.Write(ssl30Pad1[:])
|
||||
md5Digest := md5.Sum(nil)
|
||||
|
||||
md5.Reset()
|
||||
md5.Write(masterSecret)
|
||||
md5.Write(ssl30Pad2[:])
|
||||
md5.Write(md5Digest)
|
||||
md5Digest = md5.Sum(nil)
|
||||
|
||||
sha1.Write(magic)
|
||||
sha1.Write(masterSecret)
|
||||
sha1.Write(ssl30Pad1[:40])
|
||||
sha1Digest := sha1.Sum(nil)
|
||||
|
||||
sha1.Reset()
|
||||
sha1.Write(masterSecret)
|
||||
sha1.Write(ssl30Pad2[:40])
|
||||
sha1.Write(sha1Digest)
|
||||
sha1Digest = sha1.Sum(nil)
|
||||
|
||||
ret := make([]byte, len(md5Digest)+len(sha1Digest))
|
||||
copy(ret, md5Digest)
|
||||
copy(ret[len(md5Digest):], sha1Digest)
|
||||
return ret
|
||||
}
|
||||
|
||||
var ssl3ClientFinishedMagic = [4]byte{0x43, 0x4c, 0x4e, 0x54}
|
||||
var ssl3ServerFinishedMagic = [4]byte{0x53, 0x52, 0x56, 0x52}
|
||||
|
||||
// clientSum returns the contents of the verify_data member of a client's
|
||||
// Finished message.
|
||||
func (h finishedHash) clientSum(masterSecret []byte) []byte {
|
||||
if h.version == VersionSSL30 {
|
||||
return finishedSum30(h.clientMD5, h.client, masterSecret, ssl3ClientFinishedMagic[:])
|
||||
}
|
||||
|
||||
out := make([]byte, finishedVerifyLength)
|
||||
h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
|
||||
return out
|
||||
}
|
||||
|
||||
// serverSum returns the contents of the verify_data member of a server's
|
||||
// Finished message.
|
||||
func (h finishedHash) serverSum(masterSecret []byte) []byte {
|
||||
if h.version == VersionSSL30 {
|
||||
return finishedSum30(h.serverMD5, h.server, masterSecret, ssl3ServerFinishedMagic[:])
|
||||
}
|
||||
|
||||
out := make([]byte, finishedVerifyLength)
|
||||
h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
|
||||
return out
|
||||
}
|
||||
|
||||
// hashForClientCertificate returns a digest over the handshake messages so far,
|
||||
// suitable for signing by a TLS client certificate.
|
||||
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) ([]byte, error) {
|
||||
if (h.version == VersionSSL30 || h.version >= VersionTLS12) && h.buffer == nil {
|
||||
panic("a handshake hash for a client-certificate was requested after discarding the handshake buffer")
|
||||
}
|
||||
|
||||
if h.version == VersionSSL30 {
|
||||
if sigType != signaturePKCS1v15 {
|
||||
return nil, errors.New("tls: unsupported signature type for client certificate")
|
||||
}
|
||||
|
||||
md5Hash := md5.New()
|
||||
md5Hash.Write(h.buffer)
|
||||
sha1Hash := sha1.New()
|
||||
sha1Hash.Write(h.buffer)
|
||||
return finishedSum30(md5Hash, sha1Hash, masterSecret, nil), nil
|
||||
}
|
||||
if h.version >= VersionTLS12 {
|
||||
hash := hashAlg.New()
|
||||
hash.Write(h.buffer)
|
||||
return hash.Sum(nil), nil
|
||||
}
|
||||
|
||||
if sigType == signatureECDSA {
|
||||
return h.server.Sum(nil), nil
|
||||
}
|
||||
|
||||
return h.Sum(), nil
|
||||
}
|
||||
|
||||
// discardHandshakeBuffer is called when there is no more need to
|
||||
// buffer the entirety of the handshake messages.
|
||||
func (h *finishedHash) discardHandshakeBuffer() {
|
||||
h.buffer = nil
|
||||
}
|
||||
|
||||
// noExportedKeyingMaterial is used as a value of
|
||||
// ConnectionState.ekm when renegotation is enabled and thus
|
||||
// we wish to fail all key-material export requests.
|
||||
func noExportedKeyingMaterial(label string, context []byte, length int) ([]byte, error) {
|
||||
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
|
||||
}
|
||||
|
||||
// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
|
||||
func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
|
||||
return func(label string, context []byte, length int) ([]byte, error) {
|
||||
switch label {
|
||||
case "client finished", "server finished", "master secret", "key expansion":
|
||||
// These values are reserved and may not be used.
|
||||
return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
|
||||
}
|
||||
|
||||
seedLen := len(serverRandom) + len(clientRandom)
|
||||
if context != nil {
|
||||
seedLen += 2 + len(context)
|
||||
}
|
||||
seed := make([]byte, 0, seedLen)
|
||||
|
||||
seed = append(seed, clientRandom...)
|
||||
seed = append(seed, serverRandom...)
|
||||
|
||||
if context != nil {
|
||||
if len(context) >= 1<<16 {
|
||||
return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
|
||||
}
|
||||
seed = append(seed, byte(len(context)>>8), byte(len(context)))
|
||||
seed = append(seed, context...)
|
||||
}
|
||||
|
||||
keyMaterial := make([]byte, length)
|
||||
prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
|
||||
return keyMaterial, nil
|
||||
}
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"io"
|
||||
)
|
||||
|
||||
// sessionState contains the information that is serialized into a session
|
||||
// ticket in order to later resume a connection.
|
||||
type sessionState struct {
|
||||
vers uint16
|
||||
cipherSuite uint16
|
||||
masterSecret []byte
|
||||
certificates [][]byte
|
||||
// usedOldKey is true if the ticket from which this session came from
|
||||
// was encrypted with an older key and thus should be refreshed.
|
||||
usedOldKey bool
|
||||
}
|
||||
|
||||
func (s *sessionState) marshal() []byte {
|
||||
length := 2 + 2 + 2 + len(s.masterSecret) + 2
|
||||
for _, cert := range s.certificates {
|
||||
length += 4 + len(cert)
|
||||
}
|
||||
|
||||
ret := make([]byte, length)
|
||||
x := ret
|
||||
x[0] = byte(s.vers >> 8)
|
||||
x[1] = byte(s.vers)
|
||||
x[2] = byte(s.cipherSuite >> 8)
|
||||
x[3] = byte(s.cipherSuite)
|
||||
x[4] = byte(len(s.masterSecret) >> 8)
|
||||
x[5] = byte(len(s.masterSecret))
|
||||
x = x[6:]
|
||||
copy(x, s.masterSecret)
|
||||
x = x[len(s.masterSecret):]
|
||||
|
||||
x[0] = byte(len(s.certificates) >> 8)
|
||||
x[1] = byte(len(s.certificates))
|
||||
x = x[2:]
|
||||
|
||||
for _, cert := range s.certificates {
|
||||
x[0] = byte(len(cert) >> 24)
|
||||
x[1] = byte(len(cert) >> 16)
|
||||
x[2] = byte(len(cert) >> 8)
|
||||
x[3] = byte(len(cert))
|
||||
copy(x[4:], cert)
|
||||
x = x[4+len(cert):]
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *sessionState) unmarshal(data []byte) bool {
|
||||
if len(data) < 8 {
|
||||
return false
|
||||
}
|
||||
|
||||
s.vers = uint16(data[0])<<8 | uint16(data[1])
|
||||
s.cipherSuite = uint16(data[2])<<8 | uint16(data[3])
|
||||
masterSecretLen := int(data[4])<<8 | int(data[5])
|
||||
data = data[6:]
|
||||
if len(data) < masterSecretLen {
|
||||
return false
|
||||
}
|
||||
|
||||
s.masterSecret = data[:masterSecretLen]
|
||||
data = data[masterSecretLen:]
|
||||
|
||||
if len(data) < 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
numCerts := int(data[0])<<8 | int(data[1])
|
||||
data = data[2:]
|
||||
|
||||
s.certificates = make([][]byte, numCerts)
|
||||
for i := range s.certificates {
|
||||
if len(data) < 4 {
|
||||
return false
|
||||
}
|
||||
certLen := int(data[0])<<24 | int(data[1])<<16 | int(data[2])<<8 | int(data[3])
|
||||
data = data[4:]
|
||||
if certLen < 0 {
|
||||
return false
|
||||
}
|
||||
if len(data) < certLen {
|
||||
return false
|
||||
}
|
||||
s.certificates[i] = data[:certLen]
|
||||
data = data[certLen:]
|
||||
}
|
||||
|
||||
return len(data) == 0
|
||||
}
|
||||
|
||||
// sessionStateTLS13 is the content of a TLS 1.3 session ticket. Its first
|
||||
// version (revision = 0) doesn't carry any of the information needed for 0-RTT
|
||||
// validation and the nonce is always empty.
|
||||
type sessionStateTLS13 struct {
|
||||
// uint8 version = 0x0304;
|
||||
// uint8 revision = 0;
|
||||
cipherSuite uint16
|
||||
createdAt uint64
|
||||
resumptionSecret []byte // opaque resumption_master_secret<1..2^8-1>;
|
||||
certificate Certificate // CertificateEntry certificate_list<0..2^24-1>;
|
||||
}
|
||||
|
||||
func (m *sessionStateTLS13) marshal() []byte {
|
||||
var b cryptobyte.Builder
|
||||
b.AddUint16(VersionTLS13)
|
||||
b.AddUint8(0) // revision
|
||||
b.AddUint16(m.cipherSuite)
|
||||
addUint64(&b, m.createdAt)
|
||||
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(m.resumptionSecret)
|
||||
})
|
||||
marshalCertificate(&b, m.certificate)
|
||||
return b.BytesOrPanic()
|
||||
}
|
||||
|
||||
func (m *sessionStateTLS13) unmarshal(data []byte) bool {
|
||||
*m = sessionStateTLS13{}
|
||||
s := cryptobyte.String(data)
|
||||
var version uint16
|
||||
var revision uint8
|
||||
return s.ReadUint16(&version) &&
|
||||
version == VersionTLS13 &&
|
||||
s.ReadUint8(&revision) &&
|
||||
revision == 0 &&
|
||||
s.ReadUint16(&m.cipherSuite) &&
|
||||
readUint64(&s, &m.createdAt) &&
|
||||
readUint8LengthPrefixed(&s, &m.resumptionSecret) &&
|
||||
len(m.resumptionSecret) != 0 &&
|
||||
unmarshalCertificate(&s, &m.certificate) &&
|
||||
s.Empty()
|
||||
}
|
||||
|
||||
func (c *Conn) encryptTicket(state []byte) ([]byte, error) {
|
||||
encrypted := make([]byte, ticketKeyNameLen+aes.BlockSize+len(state)+sha256.Size)
|
||||
keyName := encrypted[:ticketKeyNameLen]
|
||||
iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
|
||||
macBytes := encrypted[len(encrypted)-sha256.Size:]
|
||||
|
||||
if _, err := io.ReadFull(c.config.rand(), iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
key := c.config.ticketKeys()[0]
|
||||
copy(keyName, key.keyName[:])
|
||||
block, err := aes.NewCipher(key.aesKey[:])
|
||||
if err != nil {
|
||||
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
|
||||
}
|
||||
cipher.NewCTR(block, iv).XORKeyStream(encrypted[ticketKeyNameLen+aes.BlockSize:], state)
|
||||
|
||||
mac := hmac.New(sha256.New, key.hmacKey[:])
|
||||
mac.Write(encrypted[:len(encrypted)-sha256.Size])
|
||||
mac.Sum(macBytes[:0])
|
||||
|
||||
return encrypted, nil
|
||||
}
|
||||
|
||||
func (c *Conn) decryptTicket(encrypted []byte) (plaintext []byte, usedOldKey bool) {
|
||||
if len(encrypted) < ticketKeyNameLen+aes.BlockSize+sha256.Size {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
keyName := encrypted[:ticketKeyNameLen]
|
||||
iv := encrypted[ticketKeyNameLen : ticketKeyNameLen+aes.BlockSize]
|
||||
macBytes := encrypted[len(encrypted)-sha256.Size:]
|
||||
ciphertext := encrypted[ticketKeyNameLen+aes.BlockSize : len(encrypted)-sha256.Size]
|
||||
|
||||
keys := c.config.ticketKeys()
|
||||
keyIndex := -1
|
||||
for i, candidateKey := range keys {
|
||||
if bytes.Equal(keyName, candidateKey.keyName[:]) {
|
||||
keyIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if keyIndex == -1 {
|
||||
return nil, false
|
||||
}
|
||||
key := &keys[keyIndex]
|
||||
|
||||
mac := hmac.New(sha256.New, key.hmacKey[:])
|
||||
mac.Write(encrypted[:len(encrypted)-sha256.Size])
|
||||
expected := mac.Sum(nil)
|
||||
|
||||
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key.aesKey[:])
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
plaintext = make([]byte, len(ciphertext))
|
||||
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
|
||||
|
||||
return plaintext, keyIndex > 0
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tls partially implements TLS 1.2, as specified in RFC 5246,
|
||||
// and TLS 1.3, as specified in RFC 8446.
|
||||
package tls
|
||||
|
||||
// BUG(agl): The crypto/tls package only implements some countermeasures
|
||||
// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
|
||||
// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
|
||||
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Server returns a new TLS server side connection
|
||||
// using conn as the underlying transport.
|
||||
// The configuration config must be non-nil and must include
|
||||
// at least one certificate or else set GetCertificate.
|
||||
func Server(conn net.Conn, config *Config) *Conn {
|
||||
return &Conn{conn: conn, config: config}
|
||||
}
|
||||
|
||||
// Client returns a new TLS client side connection
|
||||
// using conn as the underlying transport.
|
||||
// The config cannot be nil: users must set either ServerName or
|
||||
// InsecureSkipVerify in the config.
|
||||
func Client(conn net.Conn, config *Config) *Conn {
|
||||
return &Conn{conn: conn, config: config, isClient: true}
|
||||
}
|
||||
|
||||
// A listener implements a network listener (net.Listener) for TLS connections.
|
||||
type listener struct {
|
||||
net.Listener
|
||||
config *Config
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next incoming TLS connection.
|
||||
// The returned connection is of type *Conn.
|
||||
func (l *listener) Accept() (net.Conn, error) {
|
||||
c, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Server(c, l.config), nil
|
||||
}
|
||||
|
||||
// NewListener creates a Listener which accepts connections from an inner
|
||||
// Listener and wraps each connection with Server.
|
||||
// The configuration config must be non-nil and must include
|
||||
// at least one certificate or else set GetCertificate.
|
||||
func NewListener(inner net.Listener, config *Config) net.Listener {
|
||||
l := new(listener)
|
||||
l.Listener = inner
|
||||
l.config = config
|
||||
return l
|
||||
}
|
||||
|
||||
// Listen creates a TLS listener accepting connections on the
|
||||
// given network address using net.Listen.
|
||||
// The configuration config must be non-nil and must include
|
||||
// at least one certificate or else set GetCertificate.
|
||||
func Listen(network, laddr string, config *Config) (net.Listener, error) {
|
||||
if config == nil || (len(config.Certificates) == 0 && config.GetCertificate == nil) {
|
||||
return nil, errors.New("tls: neither Certificates nor GetCertificate set in Config")
|
||||
}
|
||||
l, err := net.Listen(network, laddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewListener(l, config), nil
|
||||
}
|
||||
|
||||
type timeoutError struct{}
|
||||
|
||||
func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
|
||||
func (timeoutError) Timeout() bool { return true }
|
||||
func (timeoutError) Temporary() bool { return true }
|
||||
|
||||
// DialWithDialer connects to the given network address using dialer.Dial and
|
||||
// then initiates a TLS handshake, returning the resulting TLS connection. Any
|
||||
// timeout or deadline given in the dialer apply to connection and TLS
|
||||
// handshake as a whole.
|
||||
//
|
||||
// DialWithDialer interprets a nil configuration as equivalent to the zero
|
||||
// configuration; see the documentation of Config for the defaults.
|
||||
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
|
||||
// We want the Timeout and Deadline values from dialer to cover the
|
||||
// whole process: TCP connection and TLS handshake. This means that we
|
||||
// also need to start our own timers now.
|
||||
timeout := dialer.Timeout
|
||||
|
||||
if !dialer.Deadline.IsZero() {
|
||||
deadlineTimeout := time.Until(dialer.Deadline)
|
||||
if timeout == 0 || deadlineTimeout < timeout {
|
||||
timeout = deadlineTimeout
|
||||
}
|
||||
}
|
||||
|
||||
var errChannel chan error
|
||||
|
||||
if timeout != 0 {
|
||||
errChannel = make(chan error, 2)
|
||||
time.AfterFunc(timeout, func() {
|
||||
errChannel <- timeoutError{}
|
||||
})
|
||||
}
|
||||
|
||||
rawConn, err := dialer.Dial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
colonPos := strings.LastIndex(addr, ":")
|
||||
if colonPos == -1 {
|
||||
colonPos = len(addr)
|
||||
}
|
||||
hostname := addr[:colonPos]
|
||||
|
||||
if config == nil {
|
||||
config = defaultConfig()
|
||||
}
|
||||
// If no ServerName is set, infer the ServerName
|
||||
// from the hostname we're connecting to.
|
||||
if config.ServerName == "" {
|
||||
// Make a copy to avoid polluting argument or default.
|
||||
c := config.Clone()
|
||||
c.ServerName = hostname
|
||||
config = c
|
||||
}
|
||||
|
||||
conn := Client(rawConn, config)
|
||||
|
||||
if timeout == 0 {
|
||||
err = conn.Handshake()
|
||||
} else {
|
||||
go func() {
|
||||
errChannel <- conn.Handshake()
|
||||
}()
|
||||
|
||||
err = <-errChannel
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
rawConn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Dial connects to the given network address using net.Dial
|
||||
// and then initiates a TLS handshake, returning the resulting
|
||||
// TLS connection.
|
||||
// Dial interprets a nil configuration as equivalent to
|
||||
// the zero configuration; see the documentation of Config
|
||||
// for the defaults.
|
||||
func Dial(network, addr string, config *Config) (*Conn, error) {
|
||||
return DialWithDialer(new(net.Dialer), network, addr, config)
|
||||
}
|
||||
|
||||
// LoadX509KeyPair reads and parses a public/private key pair from a pair
|
||||
// of files. The files must contain PEM encoded data. The certificate file
|
||||
// may contain intermediate certificates following the leaf certificate to
|
||||
// form a certificate chain. On successful return, Certificate.Leaf will
|
||||
// be nil because the parsed form of the certificate is not retained.
|
||||
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
|
||||
certPEMBlock, err := ioutil.ReadFile(certFile)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
keyPEMBlock, err := ioutil.ReadFile(keyFile)
|
||||
if err != nil {
|
||||
return Certificate{}, err
|
||||
}
|
||||
return X509KeyPair(certPEMBlock, keyPEMBlock)
|
||||
}
|
||||
|
||||
// X509KeyPair parses a public/private key pair from a pair of
|
||||
// PEM encoded data. On successful return, Certificate.Leaf will be nil because
|
||||
// the parsed form of the certificate is not retained.
|
||||
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
|
||||
fail := func(err error) (Certificate, error) { return Certificate{}, err }
|
||||
|
||||
var cert Certificate
|
||||
var skippedBlockTypes []string
|
||||
for {
|
||||
var certDERBlock *pem.Block
|
||||
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
|
||||
if certDERBlock == nil {
|
||||
break
|
||||
}
|
||||
if certDERBlock.Type == "CERTIFICATE" {
|
||||
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
|
||||
} else {
|
||||
skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cert.Certificate) == 0 {
|
||||
if len(skippedBlockTypes) == 0 {
|
||||
return fail(errors.New("tls: failed to find any PEM data in certificate input"))
|
||||
}
|
||||
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
|
||||
return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
|
||||
}
|
||||
return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
|
||||
}
|
||||
|
||||
skippedBlockTypes = skippedBlockTypes[:0]
|
||||
var keyDERBlock *pem.Block
|
||||
for {
|
||||
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
|
||||
if keyDERBlock == nil {
|
||||
if len(skippedBlockTypes) == 0 {
|
||||
return fail(errors.New("tls: failed to find any PEM data in key input"))
|
||||
}
|
||||
if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
|
||||
return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
|
||||
}
|
||||
return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
|
||||
}
|
||||
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
|
||||
break
|
||||
}
|
||||
skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
|
||||
}
|
||||
|
||||
// We don't need to parse the public key for TLS, but we so do anyway
|
||||
// to check that it looks sane and matches the private key.
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
|
||||
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
|
||||
switch pub := x509Cert.PublicKey.(type) {
|
||||
case *rsa.PublicKey:
|
||||
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return fail(errors.New("tls: private key type does not match public key type"))
|
||||
}
|
||||
if pub.N.Cmp(priv.N) != 0 {
|
||||
return fail(errors.New("tls: private key does not match public key"))
|
||||
}
|
||||
case *ecdsa.PublicKey:
|
||||
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
|
||||
if !ok {
|
||||
return fail(errors.New("tls: private key type does not match public key type"))
|
||||
}
|
||||
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
|
||||
return fail(errors.New("tls: private key does not match public key"))
|
||||
}
|
||||
default:
|
||||
return fail(errors.New("tls: unknown public key algorithm"))
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
|
||||
// PKCS#1 private keys by default, while OpenSSL 1.0.0 generates PKCS#8 keys.
|
||||
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
|
||||
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
|
||||
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PrivateKey, *ecdsa.PrivateKey:
|
||||
return key, nil
|
||||
default:
|
||||
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
|
||||
}
|
||||
}
|
||||
if key, err := x509.ParseECPrivateKey(der); err == nil {
|
||||
return key, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("tls: failed to parse private key")
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Naming convention:
|
||||
// Unsupported things are prefixed with "Fake"
|
||||
// Things, supported by utls, but not crypto/tls' are prefixed with "utls"
|
||||
// Supported things, that have changed their ID are prefixed with "Old"
|
||||
// Supported but disabled things are prefixed with "Disabled". We will _enable_ them.
|
||||
const (
|
||||
utlsExtensionPadding uint16 = 21
|
||||
utlsExtensionExtendedMasterSecret uint16 = 23 // https://tools.ietf.org/html/rfc7627
|
||||
|
||||
// extensions with 'fake' prefix break connection, if server echoes them back
|
||||
fakeExtensionChannelID uint16 = 30032 // not IANA assigned
|
||||
|
||||
fakeCertCompressionAlgs uint16 = 0x001b
|
||||
fakeRecordSizeLimit uint16 = 0x001c
|
||||
)
|
||||
|
||||
const (
|
||||
OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc13)
|
||||
OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc14)
|
||||
|
||||
DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = uint16(0xc024)
|
||||
DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = uint16(0xc028)
|
||||
DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256 = uint16(0x003d)
|
||||
|
||||
FAKE_OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc15) // we can try to craft these ciphersuites
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = uint16(0x009e) // from existing pieces, if needed
|
||||
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = uint16(0x0033)
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = uint16(0x0039)
|
||||
FAKE_TLS_RSA_WITH_RC4_128_MD5 = uint16(0x0004)
|
||||
FAKE_TLS_EMPTY_RENEGOTIATION_INFO_SCSV = uint16(0x00ff)
|
||||
)
|
||||
|
||||
// newest signatures
|
||||
var (
|
||||
FakePKCS1WithSHA224 SignatureScheme = 0x0301
|
||||
FakeECDSAWithSHA224 SignatureScheme = 0x0303
|
||||
|
||||
// fakeEd25519 = SignatureAndHash{0x08, 0x07}
|
||||
// fakeEd448 = SignatureAndHash{0x08, 0x08}
|
||||
)
|
||||
|
||||
// fake curves(groups)
|
||||
var (
|
||||
FakeFFDHE2048 = uint16(0x0100)
|
||||
FakeFFDHE3072 = uint16(0x0101)
|
||||
)
|
||||
|
||||
type ClientHelloID struct {
|
||||
Browser string
|
||||
Version uint16
|
||||
// TODO: consider adding OS?
|
||||
}
|
||||
|
||||
func (p *ClientHelloID) Str() string {
|
||||
return fmt.Sprintf("%s-%d", p.Browser, p.Version)
|
||||
}
|
||||
|
||||
const (
|
||||
helloGolang = "Golang"
|
||||
helloRandomized = "Randomized"
|
||||
helloCustom = "Custom"
|
||||
helloFirefox = "Firefox"
|
||||
helloChrome = "Chrome"
|
||||
helloIOS = "iOS"
|
||||
helloAndroid = "Android"
|
||||
)
|
||||
|
||||
const (
|
||||
helloAutoVers = iota
|
||||
helloRandomizedALPN
|
||||
helloRandomizedNoALPN
|
||||
)
|
||||
|
||||
type ClientHelloSpec struct {
|
||||
CipherSuites []uint16 // nil => default
|
||||
CompressionMethods []uint8 // nil => no compression
|
||||
Extensions []TLSExtension // nil => no extensions
|
||||
|
||||
TLSVersMin uint16 // [1.0-1.3]
|
||||
TLSVersMax uint16 // [1.2-1.3]
|
||||
|
||||
// GreaseStyle: currently only random
|
||||
// sessionID may or may not depend on ticket; nil => random
|
||||
GetSessionID func(ticket []byte) [32]byte
|
||||
|
||||
// TLSFingerprintLink string // ?? link to tlsfingerprint.io for informational purposes
|
||||
}
|
||||
|
||||
var (
|
||||
// HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL
|
||||
// overwrite your changes to Hello(Config, Session are fine).
|
||||
// You might want to call BuildHandshakeState() before applying any changes.
|
||||
// UConn.Extensions will be completely ignored.
|
||||
HelloGolang = ClientHelloID{helloGolang, helloAutoVers}
|
||||
|
||||
// HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with
|
||||
// TLSExtensions manually or use ApplyPreset function
|
||||
HelloCustom = ClientHelloID{helloCustom, helloAutoVers}
|
||||
|
||||
// HelloRandomized* randomly adds/reorders extensions, ciphersuites, etc.
|
||||
HelloRandomized = ClientHelloID{helloRandomized, helloAutoVers}
|
||||
HelloRandomizedALPN = ClientHelloID{helloRandomized, helloRandomizedALPN}
|
||||
HelloRandomizedNoALPN = ClientHelloID{helloRandomized, helloRandomizedNoALPN}
|
||||
|
||||
// The rest will will parrot given browser.
|
||||
HelloFirefox_Auto = HelloFirefox_63
|
||||
HelloFirefox_55 = ClientHelloID{helloFirefox, 55}
|
||||
HelloFirefox_56 = ClientHelloID{helloFirefox, 56}
|
||||
HelloFirefox_63 = ClientHelloID{helloFirefox, 63}
|
||||
|
||||
HelloChrome_Auto = HelloChrome_70
|
||||
HelloChrome_58 = ClientHelloID{helloChrome, 58}
|
||||
HelloChrome_62 = ClientHelloID{helloChrome, 62}
|
||||
HelloChrome_70 = ClientHelloID{helloChrome, 70}
|
||||
|
||||
HelloIOS_Auto = HelloIOS_11_1
|
||||
HelloIOS_11_1 = ClientHelloID{helloIOS, 111}
|
||||
)
|
||||
|
||||
// based on spec's GreaseStyle, GREASE_PLACEHOLDER may be replaced by another GREASE value
|
||||
// https://tools.ietf.org/html/draft-ietf-tls-grease-01
|
||||
const GREASE_PLACEHOLDER = 0x0a0a
|
||||
|
||||
// utlsMacSHA384 returns a SHA-384 based MAC. These are only supported in TLS 1.2
|
||||
// so the given version is ignored.
|
||||
func utlsMacSHA384(version uint16, key []byte) macFunction {
|
||||
return tls10MAC{h: hmac.New(sha512.New384, key)}
|
||||
}
|
||||
|
||||
var utlsSupportedCipherSuites []*cipherSuite
|
||||
|
||||
func init() {
|
||||
utlsSupportedCipherSuites = append(cipherSuites, []*cipherSuite{
|
||||
{OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheRSAKA,
|
||||
suiteECDHE | suiteTLS12 | suiteDefaultOff, nil, nil, aeadChaCha20Poly1305},
|
||||
{OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheECDSAKA,
|
||||
suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff, nil, nil, aeadChaCha20Poly1305},
|
||||
}...)
|
||||
}
|
||||
|
||||
// EnableWeakCiphers allows utls connections to continue in some cases, when weak cipher was chosen.
|
||||
// This provides better compatibility with servers on the web, but weakens security. Feel free
|
||||
// to use this option if you establish additional secure connection inside of utls connection.
|
||||
// This option does not change the shape of parrots (i.e. same ciphers will be offered either way).
|
||||
// Must be called before establishing any connections.
|
||||
func EnableWeakCiphers() {
|
||||
utlsSupportedCipherSuites = append(cipherSuites, []*cipherSuite{
|
||||
{DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256, 32, 32, 16, rsaKA,
|
||||
suiteTLS12 | suiteDefaultOff, cipherAES, macSHA256, nil},
|
||||
|
||||
{DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA,
|
||||
suiteECDHE | suiteECDSA | suiteTLS12 | suiteDefaultOff | suiteSHA384, cipherAES, utlsMacSHA384, nil},
|
||||
{DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheRSAKA,
|
||||
suiteECDHE | suiteTLS12 | suiteDefaultOff | suiteSHA384, cipherAES, utlsMacSHA384, nil},
|
||||
}...)
|
||||
}
|
@ -1,561 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/cipher"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type UConn struct {
|
||||
*Conn
|
||||
|
||||
Extensions []TLSExtension
|
||||
clientHelloID ClientHelloID
|
||||
|
||||
ClientHelloBuilt bool
|
||||
HandshakeState ClientHandshakeState
|
||||
|
||||
// sessionID may or may not depend on ticket; nil => random
|
||||
GetSessionID func(ticket []byte) [32]byte
|
||||
|
||||
greaseSeed [ssl_grease_last_index]uint16
|
||||
}
|
||||
|
||||
// UClient returns a new uTLS client, with behavior depending on clientHelloID.
|
||||
// Config CAN be nil, but make sure to eventually specify ServerName.
|
||||
func UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID) *UConn {
|
||||
if config == nil {
|
||||
config = &Config{}
|
||||
}
|
||||
tlsConn := Conn{conn: conn, config: config, isClient: true}
|
||||
handshakeState := ClientHandshakeState{C: &tlsConn, Hello: &ClientHelloMsg{}}
|
||||
uconn := UConn{Conn: &tlsConn, clientHelloID: clientHelloID, HandshakeState: handshakeState}
|
||||
return &uconn
|
||||
}
|
||||
|
||||
// BuildHandshakeState behavior varies based on ClientHelloID and
|
||||
// whether it was already called before.
|
||||
// If HelloGolang:
|
||||
// [only once] make default ClientHello and overwrite existing state
|
||||
// If any other mimicking ClientHelloID is used:
|
||||
// [only once] make ClientHello based on ID and overwrite existing state
|
||||
// [each call] apply uconn.Extensions config to internal crypto/tls structures
|
||||
// [each call] marshal ClientHello.
|
||||
//
|
||||
// BuildHandshakeState is automatically called before uTLS performs handshake,
|
||||
// amd should only be called explicitly to inspect/change fields of
|
||||
// default/mimicked ClientHello.
|
||||
func (uconn *UConn) BuildHandshakeState() error {
|
||||
if uconn.clientHelloID == HelloGolang {
|
||||
if uconn.ClientHelloBuilt {
|
||||
return nil
|
||||
}
|
||||
|
||||
// use default Golang ClientHello.
|
||||
hello, ecdheParams, err := uconn.makeClientHello()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uconn.HandshakeState.Hello = hello.getPublicPtr()
|
||||
uconn.HandshakeState.State13.EcdheParams = ecdheParams
|
||||
uconn.HandshakeState.C = uconn.Conn
|
||||
} else {
|
||||
if !uconn.ClientHelloBuilt {
|
||||
err := uconn.applyPresetByID(uconn.clientHelloID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err := uconn.ApplyConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = uconn.MarshalClientHello()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
uconn.ClientHelloBuilt = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetSessionState sets the session ticket, which may be preshared or fake.
|
||||
// If session is nil, the body of session ticket extension will be unset,
|
||||
// but the extension itself still MAY be present for mimicking purposes.
|
||||
// Session tickets to be reused - use same cache on following connections.
|
||||
func (uconn *UConn) SetSessionState(session *ClientSessionState) error {
|
||||
uconn.HandshakeState.Session = session
|
||||
var sessionTicket []uint8
|
||||
if session != nil {
|
||||
sessionTicket = session.sessionTicket
|
||||
}
|
||||
uconn.HandshakeState.Hello.TicketSupported = true
|
||||
uconn.HandshakeState.Hello.SessionTicket = sessionTicket
|
||||
|
||||
for _, ext := range uconn.Extensions {
|
||||
st, ok := ext.(*SessionTicketExtension)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
st.Session = session
|
||||
if session != nil {
|
||||
if len(session.SessionTicket()) > 0 {
|
||||
if uconn.GetSessionID != nil {
|
||||
sid := uconn.GetSessionID(session.SessionTicket())
|
||||
uconn.HandshakeState.Hello.SessionId = sid[:]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
var sessionID [32]byte
|
||||
_, err := io.ReadFull(uconn.config.rand(), uconn.HandshakeState.Hello.SessionId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uconn.HandshakeState.Hello.SessionId = sessionID[:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// If you want session tickets to be reused - use same cache on following connections
|
||||
func (uconn *UConn) SetSessionCache(cache ClientSessionCache) {
|
||||
uconn.config.ClientSessionCache = cache
|
||||
uconn.HandshakeState.Hello.TicketSupported = true
|
||||
}
|
||||
|
||||
// SetClientRandom sets client random explicitly.
|
||||
// BuildHandshakeFirst() must be called before SetClientRandom.
|
||||
// r must to be 32 bytes long.
|
||||
func (uconn *UConn) SetClientRandom(r []byte) error {
|
||||
if len(r) != 32 {
|
||||
return errors.New("Incorrect client random length! Expected: 32, got: " + strconv.Itoa(len(r)))
|
||||
} else {
|
||||
uconn.HandshakeState.Hello.Random = make([]byte, 32)
|
||||
copy(uconn.HandshakeState.Hello.Random, r)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (uconn *UConn) SetSNI(sni string) {
|
||||
hname := hostnameInSNI(sni)
|
||||
uconn.config.ServerName = hname
|
||||
for _, ext := range uconn.Extensions {
|
||||
sniExt, ok := ext.(*SNIExtension)
|
||||
if ok {
|
||||
sniExt.ServerName = hname
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handshake runs the client handshake using given clientHandshakeState
|
||||
// Requires hs.hello, and, optionally, hs.session to be set.
|
||||
func (c *UConn) Handshake() error {
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshakeComplete() {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.in.Lock()
|
||||
defer c.in.Unlock()
|
||||
|
||||
if c.isClient {
|
||||
// [uTLS section begins]
|
||||
err := c.BuildHandshakeState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// [uTLS section ends]
|
||||
|
||||
c.handshakeErr = c.clientHandshake()
|
||||
} else {
|
||||
c.handshakeErr = c.serverHandshake()
|
||||
}
|
||||
if c.handshakeErr == nil {
|
||||
c.handshakes++
|
||||
} else {
|
||||
// If an error occurred during the hadshake try to flush the
|
||||
// alert that might be left in the buffer.
|
||||
c.flush()
|
||||
}
|
||||
|
||||
if c.handshakeErr == nil && !c.handshakeComplete() {
|
||||
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
||||
}
|
||||
|
||||
return c.handshakeErr
|
||||
}
|
||||
|
||||
// Copy-pasted from tls.Conn in its entirety. But c.Handshake() is now utls' one, not tls.
|
||||
// Write writes data to the connection.
|
||||
func (c *UConn) Write(b []byte) (int, error) {
|
||||
// interlock with Close below
|
||||
for {
|
||||
x := atomic.LoadInt32(&c.activeCall)
|
||||
if x&1 != 0 {
|
||||
return 0, errClosed
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
|
||||
defer atomic.AddInt32(&c.activeCall, -2)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Handshake(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
c.out.Lock()
|
||||
defer c.out.Unlock()
|
||||
|
||||
if err := c.out.err; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !c.handshakeComplete() {
|
||||
return 0, alertInternalError
|
||||
}
|
||||
|
||||
if c.closeNotifySent {
|
||||
return 0, errShutdown
|
||||
}
|
||||
|
||||
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
|
||||
// attack when using block mode ciphers due to predictable IVs.
|
||||
// This can be prevented by splitting each Application Data
|
||||
// record into two records, effectively randomizing the IV.
|
||||
//
|
||||
// https://www.openssl.org/~bodo/tls-cbc.txt
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
|
||||
// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
|
||||
|
||||
var m int
|
||||
if len(b) > 1 && c.vers <= VersionTLS10 {
|
||||
if _, ok := c.out.cipher.(cipher.BlockMode); ok {
|
||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
|
||||
if err != nil {
|
||||
return n, c.out.setErrorLocked(err)
|
||||
}
|
||||
m, b = 1, b[1:]
|
||||
}
|
||||
}
|
||||
|
||||
n, err := c.writeRecordLocked(recordTypeApplicationData, b)
|
||||
return n + m, c.out.setErrorLocked(err)
|
||||
}
|
||||
|
||||
// clientHandshakeWithOneState checks that exactly one expected state is set (1.2 or 1.3)
|
||||
// and performs client TLS handshake with that state
|
||||
func (c *UConn) clientHandshake() (err error) {
|
||||
// [uTLS section begins]
|
||||
hello := c.HandshakeState.Hello.getPrivatePtr()
|
||||
defer func() { c.HandshakeState.Hello = hello.getPublicPtr() }()
|
||||
|
||||
sessionIsAlreadySet := c.HandshakeState.Session != nil
|
||||
|
||||
// after this point exactly 1 out of 2 HandshakeState pointers is non-nil,
|
||||
// useTLS13 variable tells which pointer
|
||||
// [uTLS section ends]
|
||||
|
||||
if c.config == nil {
|
||||
c.config = defaultConfig()
|
||||
}
|
||||
|
||||
// This may be a renegotiation handshake, in which case some fields
|
||||
// need to be reset.
|
||||
c.didResume = false
|
||||
|
||||
// [uTLS section begins]
|
||||
// don't make new ClientHello, use hs.hello
|
||||
// preserve the checks from beginning and end of makeClientHello()
|
||||
if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify {
|
||||
return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
||||
}
|
||||
|
||||
nextProtosLength := 0
|
||||
for _, proto := range c.config.NextProtos {
|
||||
if l := len(proto); l == 0 || l > 255 {
|
||||
return errors.New("tls: invalid NextProtos value")
|
||||
} else {
|
||||
nextProtosLength += 1 + l
|
||||
}
|
||||
}
|
||||
|
||||
if nextProtosLength > 0xffff {
|
||||
return errors.New("tls: NextProtos values too large")
|
||||
}
|
||||
|
||||
if c.handshakes > 0 {
|
||||
hello.secureRenegotiation = c.clientFinished[:]
|
||||
}
|
||||
// [uTLS section ends]
|
||||
|
||||
cacheKey, session, earlySecret, binderKey := c.loadSession(hello)
|
||||
if cacheKey != "" && session != nil {
|
||||
defer func() {
|
||||
// If we got a handshake failure when resuming a session, throw away
|
||||
// the session ticket. See RFC 5077, Section 3.2.
|
||||
//
|
||||
// RFC 8446 makes no mention of dropping tickets on failure, but it
|
||||
// does require servers to abort on invalid binders, so we need to
|
||||
// delete tickets to recover from a corrupted PSK.
|
||||
if err != nil {
|
||||
c.config.ClientSessionCache.Put(cacheKey, nil)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
if !sessionIsAlreadySet { // uTLS: do not overwrite already set session
|
||||
err = c.SetSessionState(session)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := c.writeRecord(recordTypeHandshake, hello.marshal()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverHello, ok := msg.(*serverHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(serverHello, msg)
|
||||
}
|
||||
|
||||
if err := c.pickTLSVersion(serverHello); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// uTLS: do not create new handshakeState, use existing one
|
||||
if c.vers == VersionTLS13 {
|
||||
hs13 := c.HandshakeState.toPrivate13()
|
||||
hs13.serverHello = serverHello
|
||||
hs13.hello = hello
|
||||
if !sessionIsAlreadySet {
|
||||
hs13.earlySecret = earlySecret
|
||||
hs13.binderKey = binderKey
|
||||
}
|
||||
// In TLS 1.3, session tickets are delivered after the handshake.
|
||||
err = hs13.handshake()
|
||||
c.HandshakeState = *hs13.toPublic13()
|
||||
return err
|
||||
}
|
||||
|
||||
hs12 := c.HandshakeState.toPrivate12()
|
||||
hs12.serverHello = serverHello
|
||||
hs12.hello = hello
|
||||
err = hs12.handshake()
|
||||
c.HandshakeState = *hs12.toPublic13()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we had a successful handshake and hs.session is different from
|
||||
// the one already cached - cache a new one.
|
||||
if cacheKey != "" && hs12.session != nil && session != hs12.session {
|
||||
c.config.ClientSessionCache.Put(cacheKey, hs12.session)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uconn *UConn) ApplyConfig() error {
|
||||
for _, ext := range uconn.Extensions {
|
||||
err := ext.writeToUConn(uconn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uconn *UConn) MarshalClientHello() error {
|
||||
hello := uconn.HandshakeState.Hello
|
||||
headerLength := 2 + 32 + 1 + len(hello.SessionId) +
|
||||
2 + len(hello.CipherSuites)*2 +
|
||||
1 + len(hello.CompressionMethods)
|
||||
|
||||
extensionsLen := 0
|
||||
var paddingExt *UtlsPaddingExtension
|
||||
for _, ext := range uconn.Extensions {
|
||||
if pe, ok := ext.(*UtlsPaddingExtension); !ok {
|
||||
// If not padding - just add length of extension to total length
|
||||
extensionsLen += ext.Len()
|
||||
} else {
|
||||
// If padding - process it later
|
||||
if paddingExt == nil {
|
||||
paddingExt = pe
|
||||
} else {
|
||||
return errors.New("Multiple padding extensions!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if paddingExt != nil {
|
||||
// determine padding extension presence and length
|
||||
paddingExt.Update(headerLength + 4 + extensionsLen + 2)
|
||||
extensionsLen += paddingExt.Len()
|
||||
}
|
||||
|
||||
helloLen := headerLength
|
||||
if len(uconn.Extensions) > 0 {
|
||||
helloLen += 2 + extensionsLen // 2 bytes for extensions' length
|
||||
}
|
||||
|
||||
helloBuffer := bytes.Buffer{}
|
||||
bufferedWriter := bufio.NewWriterSize(&helloBuffer, helloLen+4) // 1 byte for tls record type, 3 for length
|
||||
// We use buffered Writer to avoid checking write errors after every Write(): whenever first error happens
|
||||
// Write() will become noop, and error will be accessible via Flush(), which is called once in the end
|
||||
|
||||
binary.Write(bufferedWriter, binary.BigEndian, typeClientHello)
|
||||
helloLenBytes := []byte{byte(helloLen >> 16), byte(helloLen >> 8), byte(helloLen)} // poor man's uint24
|
||||
binary.Write(bufferedWriter, binary.BigEndian, helloLenBytes)
|
||||
binary.Write(bufferedWriter, binary.BigEndian, hello.Vers)
|
||||
|
||||
binary.Write(bufferedWriter, binary.BigEndian, hello.Random)
|
||||
|
||||
binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.SessionId)))
|
||||
binary.Write(bufferedWriter, binary.BigEndian, hello.SessionId)
|
||||
|
||||
binary.Write(bufferedWriter, binary.BigEndian, uint16(len(hello.CipherSuites)<<1))
|
||||
for _, suite := range hello.CipherSuites {
|
||||
binary.Write(bufferedWriter, binary.BigEndian, suite)
|
||||
}
|
||||
|
||||
binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.CompressionMethods)))
|
||||
binary.Write(bufferedWriter, binary.BigEndian, hello.CompressionMethods)
|
||||
|
||||
if len(uconn.Extensions) > 0 {
|
||||
binary.Write(bufferedWriter, binary.BigEndian, uint16(extensionsLen))
|
||||
for _, ext := range uconn.Extensions {
|
||||
bufferedWriter.ReadFrom(ext)
|
||||
}
|
||||
}
|
||||
|
||||
err := bufferedWriter.Flush()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if helloBuffer.Len() != 4+helloLen {
|
||||
return errors.New("utls: unexpected ClientHello length. Expected: " + strconv.Itoa(4+helloLen) +
|
||||
". Got: " + strconv.Itoa(helloBuffer.Len()))
|
||||
}
|
||||
|
||||
hello.Raw = helloBuffer.Bytes()
|
||||
return nil
|
||||
}
|
||||
|
||||
// get current state of cipher and encrypt zeros to get keystream
|
||||
func (uconn *UConn) GetOutKeystream(length int) ([]byte, error) {
|
||||
zeros := make([]byte, length)
|
||||
|
||||
if outCipher, ok := uconn.out.cipher.(cipher.AEAD); ok {
|
||||
// AEAD.Seal() does not mutate internal state, other ciphers might
|
||||
return outCipher.Seal(nil, uconn.out.seq[:], zeros, nil), nil
|
||||
}
|
||||
return nil, errors.New("Could not convert OutCipher to cipher.AEAD")
|
||||
}
|
||||
|
||||
// SetVersCreateState set min and max TLS version in all appropriate places.
|
||||
func (uconn *UConn) SetTLSVers(minTLSVers, maxTLSVers uint16) error {
|
||||
if minTLSVers < VersionTLS10 || minTLSVers > VersionTLS12 {
|
||||
return fmt.Errorf("uTLS does not support 0x%X as min version", minTLSVers)
|
||||
}
|
||||
|
||||
if maxTLSVers < VersionTLS10 || maxTLSVers > VersionTLS13 {
|
||||
return fmt.Errorf("uTLS does not support 0x%X as max version", maxTLSVers)
|
||||
}
|
||||
|
||||
uconn.HandshakeState.Hello.SupportedVersions = makeSupportedVersions(minTLSVers, maxTLSVers)
|
||||
uconn.config.MinVersion = minTLSVers
|
||||
uconn.config.MaxVersion = maxTLSVers
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uconn *UConn) SetUnderlyingConn(c net.Conn) {
|
||||
uconn.Conn.conn = c
|
||||
}
|
||||
|
||||
func (uconn *UConn) GetUnderlyingConn() net.Conn {
|
||||
return uconn.Conn.conn
|
||||
}
|
||||
|
||||
// MakeConnWithCompleteHandshake allows to forge both server and client side TLS connections.
|
||||
// Major Hack Alert.
|
||||
func MakeConnWithCompleteHandshake(tcpConn net.Conn, version uint16, cipherSuite uint16, masterSecret []byte, clientRandom []byte, serverRandom []byte, isClient bool) *Conn {
|
||||
tlsConn := &Conn{conn: tcpConn, config: &Config{}, isClient: isClient}
|
||||
cs := cipherSuiteByID(cipherSuite)
|
||||
|
||||
// This is mostly borrowed from establishKeys()
|
||||
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
|
||||
keysFromMasterSecret(version, cs, masterSecret, clientRandom, serverRandom,
|
||||
cs.macLen, cs.keyLen, cs.ivLen)
|
||||
|
||||
var clientCipher, serverCipher interface{}
|
||||
var clientHash, serverHash macFunction
|
||||
if cs.cipher != nil {
|
||||
clientCipher = cs.cipher(clientKey, clientIV, true /* for reading */)
|
||||
clientHash = cs.mac(version, clientMAC)
|
||||
serverCipher = cs.cipher(serverKey, serverIV, false /* not for reading */)
|
||||
serverHash = cs.mac(version, serverMAC)
|
||||
} else {
|
||||
clientCipher = cs.aead(clientKey, clientIV)
|
||||
serverCipher = cs.aead(serverKey, serverIV)
|
||||
}
|
||||
|
||||
if isClient {
|
||||
tlsConn.in.prepareCipherSpec(version, serverCipher, serverHash)
|
||||
tlsConn.out.prepareCipherSpec(version, clientCipher, clientHash)
|
||||
} else {
|
||||
tlsConn.in.prepareCipherSpec(version, clientCipher, clientHash)
|
||||
tlsConn.out.prepareCipherSpec(version, serverCipher, serverHash)
|
||||
}
|
||||
|
||||
// skip the handshake states
|
||||
tlsConn.handshakeStatus = 1
|
||||
tlsConn.cipherSuite = cipherSuite
|
||||
tlsConn.haveVers = true
|
||||
tlsConn.vers = version
|
||||
|
||||
// Update to the new cipher specs
|
||||
// and consume the finished messages
|
||||
tlsConn.in.changeCipherSpec()
|
||||
tlsConn.out.changeCipherSpec()
|
||||
|
||||
tlsConn.in.incSeq()
|
||||
tlsConn.out.incSeq()
|
||||
|
||||
return tlsConn
|
||||
}
|
||||
|
||||
func makeSupportedVersions(minVers, maxVers uint16) []uint16 {
|
||||
a := make([]uint16, maxVers-minVers+1)
|
||||
for i := range a {
|
||||
a[i] = maxVers - uint16(i)
|
||||
}
|
||||
return a
|
||||
}
|
@ -1,794 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) {
|
||||
switch id {
|
||||
case HelloChrome_58, HelloChrome_62:
|
||||
return ClientHelloSpec{
|
||||
TLSVersMax: VersionTLS12,
|
||||
TLSVersMin: VersionTLS10,
|
||||
CipherSuites: []uint16{
|
||||
GREASE_PLACEHOLDER,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{compressionNone},
|
||||
Extensions: []TLSExtension{
|
||||
&UtlsGREASEExtension{},
|
||||
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
|
||||
&SNIExtension{},
|
||||
&UtlsExtendedMasterSecretExtension{},
|
||||
&SessionTicketExtension{},
|
||||
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
PSSWithSHA256,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PSSWithSHA384,
|
||||
PKCS1WithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA512,
|
||||
PKCS1WithSHA1},
|
||||
},
|
||||
&StatusRequestExtension{},
|
||||
&SCTExtension{},
|
||||
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
|
||||
&FakeChannelIDExtension{},
|
||||
&SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}},
|
||||
&SupportedCurvesExtension{[]CurveID{CurveID(GREASE_PLACEHOLDER),
|
||||
X25519, CurveP256, CurveP384}},
|
||||
&UtlsGREASEExtension{},
|
||||
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
|
||||
},
|
||||
GetSessionID: sha256.Sum256,
|
||||
}, nil
|
||||
case HelloChrome_70:
|
||||
return ClientHelloSpec{
|
||||
TLSVersMin: VersionTLS10,
|
||||
TLSVersMax: VersionTLS13,
|
||||
CipherSuites: []uint16{
|
||||
GREASE_PLACEHOLDER,
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{
|
||||
compressionNone,
|
||||
},
|
||||
Extensions: []TLSExtension{
|
||||
&UtlsGREASEExtension{},
|
||||
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
|
||||
&SNIExtension{},
|
||||
&UtlsExtendedMasterSecretExtension{},
|
||||
&SessionTicketExtension{},
|
||||
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
PSSWithSHA256,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PSSWithSHA384,
|
||||
PKCS1WithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA512,
|
||||
PKCS1WithSHA1,
|
||||
}},
|
||||
&StatusRequestExtension{},
|
||||
&SCTExtension{},
|
||||
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
|
||||
&FakeChannelIDExtension{},
|
||||
&SupportedPointsExtension{SupportedPoints: []byte{
|
||||
pointFormatUncompressed,
|
||||
}},
|
||||
&KeyShareExtension{[]KeyShare{
|
||||
{Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}},
|
||||
{Group: X25519},
|
||||
}},
|
||||
&PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}},
|
||||
&SupportedVersionsExtension{[]uint16{
|
||||
GREASE_PLACEHOLDER,
|
||||
VersionTLS13,
|
||||
VersionTLS12,
|
||||
VersionTLS11,
|
||||
VersionTLS10}},
|
||||
&SupportedCurvesExtension{[]CurveID{
|
||||
CurveID(GREASE_PLACEHOLDER),
|
||||
X25519,
|
||||
CurveP256,
|
||||
CurveP384,
|
||||
}},
|
||||
&GenericExtension{id: fakeCertCompressionAlgs, data: []byte{02, 00, 02}},
|
||||
&UtlsGREASEExtension{},
|
||||
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
|
||||
},
|
||||
}, nil
|
||||
case HelloFirefox_55, HelloFirefox_56:
|
||||
return ClientHelloSpec{
|
||||
TLSVersMax: VersionTLS12,
|
||||
TLSVersMin: VersionTLS10,
|
||||
CipherSuites: []uint16{
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{compressionNone},
|
||||
Extensions: []TLSExtension{
|
||||
&SNIExtension{},
|
||||
&UtlsExtendedMasterSecretExtension{},
|
||||
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
|
||||
&SupportedCurvesExtension{[]CurveID{X25519, CurveP256, CurveP384, CurveP521}},
|
||||
&SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}},
|
||||
&SessionTicketExtension{},
|
||||
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
|
||||
&StatusRequestExtension{},
|
||||
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA256,
|
||||
PKCS1WithSHA384,
|
||||
PKCS1WithSHA512,
|
||||
ECDSAWithSHA1,
|
||||
PKCS1WithSHA1},
|
||||
},
|
||||
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
|
||||
},
|
||||
GetSessionID: nil,
|
||||
}, nil
|
||||
case HelloFirefox_63:
|
||||
return ClientHelloSpec{
|
||||
TLSVersMin: VersionTLS10,
|
||||
TLSVersMax: VersionTLS13,
|
||||
CipherSuites: []uint16{
|
||||
TLS_AES_128_GCM_SHA256,
|
||||
TLS_CHACHA20_POLY1305_SHA256,
|
||||
TLS_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{
|
||||
compressionNone,
|
||||
},
|
||||
Extensions: []TLSExtension{
|
||||
&SNIExtension{},
|
||||
&UtlsExtendedMasterSecretExtension{},
|
||||
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
|
||||
&SupportedCurvesExtension{[]CurveID{
|
||||
X25519,
|
||||
CurveP256,
|
||||
CurveP384,
|
||||
CurveP521,
|
||||
CurveID(FakeFFDHE2048),
|
||||
CurveID(FakeFFDHE3072),
|
||||
}},
|
||||
&SupportedPointsExtension{SupportedPoints: []byte{
|
||||
pointFormatUncompressed,
|
||||
}},
|
||||
&SessionTicketExtension{},
|
||||
&ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}},
|
||||
&StatusRequestExtension{},
|
||||
&KeyShareExtension{[]KeyShare{
|
||||
{Group: X25519},
|
||||
{Group: CurveP256},
|
||||
}},
|
||||
&SupportedVersionsExtension{[]uint16{
|
||||
VersionTLS13,
|
||||
VersionTLS12,
|
||||
VersionTLS11,
|
||||
VersionTLS10}},
|
||||
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
ECDSAWithP521AndSHA512,
|
||||
PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA256,
|
||||
PKCS1WithSHA384,
|
||||
PKCS1WithSHA512,
|
||||
ECDSAWithSHA1,
|
||||
PKCS1WithSHA1,
|
||||
}},
|
||||
&PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}},
|
||||
&GenericExtension{id: fakeRecordSizeLimit, data: []byte{0x40, 0x01}},
|
||||
&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle},
|
||||
}}, nil
|
||||
case HelloIOS_11_1:
|
||||
return ClientHelloSpec{
|
||||
TLSVersMax: VersionTLS12,
|
||||
TLSVersMin: VersionTLS10,
|
||||
CipherSuites: []uint16{
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
||||
DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
||||
DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
||||
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
|
||||
TLS_RSA_WITH_AES_256_GCM_SHA384,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA256,
|
||||
TLS_RSA_WITH_AES_256_CBC_SHA,
|
||||
TLS_RSA_WITH_AES_128_CBC_SHA,
|
||||
},
|
||||
CompressionMethods: []byte{
|
||||
compressionNone,
|
||||
},
|
||||
Extensions: []TLSExtension{
|
||||
&RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient},
|
||||
&SNIExtension{},
|
||||
&UtlsExtendedMasterSecretExtension{},
|
||||
&SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
PSSWithSHA256,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PSSWithSHA384,
|
||||
PKCS1WithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA512,
|
||||
PKCS1WithSHA1,
|
||||
}},
|
||||
&StatusRequestExtension{},
|
||||
&NPNExtension{},
|
||||
&SCTExtension{},
|
||||
&ALPNExtension{AlpnProtocols: []string{"h2", "h2-16", "h2-15", "h2-14", "spdy/3.1", "spdy/3", "http/1.1"}},
|
||||
&SupportedPointsExtension{SupportedPoints: []byte{
|
||||
pointFormatUncompressed,
|
||||
}},
|
||||
&SupportedCurvesExtension{Curves: []CurveID{
|
||||
X25519,
|
||||
CurveP256,
|
||||
CurveP384,
|
||||
CurveP521,
|
||||
}},
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return ClientHelloSpec{}, errors.New("ClientHello ID " + id.Str() + " is unknown")
|
||||
}
|
||||
}
|
||||
|
||||
func (uconn *UConn) applyPresetByID(id ClientHelloID) (err error) {
|
||||
var spec ClientHelloSpec
|
||||
// choose/generate the spec
|
||||
switch id {
|
||||
case HelloRandomized:
|
||||
if tossBiasedCoin(0.5) {
|
||||
return uconn.applyPresetByID(HelloRandomizedALPN)
|
||||
} else {
|
||||
return uconn.applyPresetByID(HelloRandomizedNoALPN)
|
||||
}
|
||||
case HelloRandomizedALPN:
|
||||
spec, err = uconn.generateRandomizedSpec(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case HelloRandomizedNoALPN:
|
||||
spec, err = uconn.generateRandomizedSpec(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case HelloCustom:
|
||||
return nil
|
||||
|
||||
default:
|
||||
spec, err = utlsIdToSpec(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
uconn.clientHelloID = id
|
||||
return uconn.ApplyPreset(&spec)
|
||||
}
|
||||
|
||||
// ApplyPreset should only be used in conjunction with HelloCustom to apply custom specs.
|
||||
// Fields of TLSExtensions that are slices/pointers are shared across different connections with
|
||||
// same ClientHelloSpec. It is advised to use different specs and avoid any shared state.
|
||||
func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error {
|
||||
var err error
|
||||
err = uconn.SetTLSVers(p.TLSVersMin, p.TLSVersMax)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
privateHello, ecdheParams, err := uconn.makeClientHello()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
uconn.HandshakeState.Hello = privateHello.getPublicPtr()
|
||||
uconn.HandshakeState.State13.EcdheParams = ecdheParams
|
||||
hello := uconn.HandshakeState.Hello
|
||||
session := uconn.HandshakeState.Session
|
||||
|
||||
switch len(hello.Random) {
|
||||
case 0:
|
||||
hello.Random = make([]byte, 32)
|
||||
_, err := io.ReadFull(uconn.config.rand(), hello.Random)
|
||||
if err != nil {
|
||||
return errors.New("tls: short read from Rand: " + err.Error())
|
||||
}
|
||||
case 32:
|
||||
// carry on
|
||||
default:
|
||||
return errors.New("ClientHello expected length: 32 bytes. Got: " +
|
||||
strconv.Itoa(len(hello.Random)) + " bytes")
|
||||
}
|
||||
if len(hello.CipherSuites) == 0 {
|
||||
hello.CipherSuites = defaultCipherSuites()
|
||||
}
|
||||
if len(hello.CompressionMethods) == 0 {
|
||||
hello.CompressionMethods = []uint8{compressionNone}
|
||||
}
|
||||
|
||||
// Currently, GREASE is assumed to come from BoringSSL
|
||||
grease_bytes := make([]byte, 2*ssl_grease_last_index)
|
||||
grease_extensions_seen := 0
|
||||
_, err = io.ReadFull(uconn.config.rand(), grease_bytes)
|
||||
if err != nil {
|
||||
return errors.New("tls: short read from Rand: " + err.Error())
|
||||
}
|
||||
for i := range uconn.greaseSeed {
|
||||
uconn.greaseSeed[i] = binary.LittleEndian.Uint16(grease_bytes[2*i : 2*i+2])
|
||||
}
|
||||
if uconn.greaseSeed[ssl_grease_extension1] == uconn.greaseSeed[ssl_grease_extension2] {
|
||||
uconn.greaseSeed[ssl_grease_extension2] ^= 0x1010
|
||||
}
|
||||
|
||||
hello.CipherSuites = make([]uint16, len(p.CipherSuites))
|
||||
copy(hello.CipherSuites, p.CipherSuites)
|
||||
for i := range hello.CipherSuites {
|
||||
if hello.CipherSuites[i] == GREASE_PLACEHOLDER {
|
||||
hello.CipherSuites[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_cipher)
|
||||
}
|
||||
}
|
||||
uconn.GetSessionID = p.GetSessionID
|
||||
uconn.Extensions = make([]TLSExtension, len(p.Extensions))
|
||||
copy(uconn.Extensions, p.Extensions)
|
||||
|
||||
// reGrease, and point things to each other
|
||||
for _, e := range uconn.Extensions {
|
||||
switch ext := e.(type) {
|
||||
case *SNIExtension:
|
||||
if ext.ServerName == "" {
|
||||
ext.ServerName = uconn.config.ServerName
|
||||
}
|
||||
case *UtlsGREASEExtension:
|
||||
switch grease_extensions_seen {
|
||||
case 0:
|
||||
ext.Value = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_extension1)
|
||||
case 1:
|
||||
ext.Value = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_extension2)
|
||||
ext.Body = []byte{0}
|
||||
default:
|
||||
return errors.New("at most 2 grease extensions are supported")
|
||||
}
|
||||
grease_extensions_seen += 1
|
||||
case *SessionTicketExtension:
|
||||
err := uconn.SetSessionState(session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case *SupportedCurvesExtension:
|
||||
for i := range ext.Curves {
|
||||
if ext.Curves[i] == GREASE_PLACEHOLDER {
|
||||
ext.Curves[i] = CurveID(GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_group))
|
||||
}
|
||||
}
|
||||
case *KeyShareExtension:
|
||||
preferredCurveIsSet := false
|
||||
for i := range ext.KeyShares {
|
||||
curveID := ext.KeyShares[i].Group
|
||||
if curveID == GREASE_PLACEHOLDER {
|
||||
ext.KeyShares[i].Group = CurveID(GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_group))
|
||||
continue
|
||||
}
|
||||
if len(ext.KeyShares[i].Data) > 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
ecdheParams, err := generateECDHEParameters(uconn.config.rand(), curveID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unsupported Curve in KeyShareExtension: %v."+
|
||||
"To mimic it, fill the Data(key) field manually.", curveID)
|
||||
}
|
||||
ext.KeyShares[i].Data = ecdheParams.PublicKey()
|
||||
if !preferredCurveIsSet {
|
||||
// only do this once for the first non-grease curve
|
||||
uconn.HandshakeState.State13.EcdheParams = ecdheParams
|
||||
preferredCurveIsSet = true
|
||||
}
|
||||
}
|
||||
case *SupportedVersionsExtension:
|
||||
for i := range ext.Versions {
|
||||
if ext.Versions[i] == GREASE_PLACEHOLDER {
|
||||
ext.Versions[i] = GetBoringGREASEValue(uconn.greaseSeed, ssl_grease_version)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (uconn *UConn) generateRandomizedSpec(WithALPN bool) (ClientHelloSpec, error) {
|
||||
p := ClientHelloSpec{}
|
||||
|
||||
p.CipherSuites = make([]uint16, len(defaultCipherSuites()))
|
||||
copy(p.CipherSuites, defaultCipherSuites())
|
||||
shuffledSuites, err := shuffledCiphers()
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
|
||||
if tossBiasedCoin(0.4) {
|
||||
p.TLSVersMin = VersionTLS10
|
||||
p.TLSVersMax = VersionTLS13
|
||||
tls13ciphers := defaultCipherSuitesTLS13()
|
||||
err = shuffleUInts16(tls13ciphers)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
// appending TLS 1.3 ciphers before TLS 1.2, since that's what popular implementations do
|
||||
shuffledSuites = append(tls13ciphers, shuffledSuites...)
|
||||
|
||||
// TLS 1.3 forbids RC4 in any configurations
|
||||
shuffledSuites = removeRC4Ciphers(shuffledSuites)
|
||||
} else {
|
||||
p.TLSVersMin = VersionTLS10
|
||||
p.TLSVersMax = VersionTLS12
|
||||
}
|
||||
|
||||
p.CipherSuites = removeRandomCiphers(shuffledSuites, 0.4)
|
||||
|
||||
sni := SNIExtension{uconn.config.ServerName}
|
||||
sessionTicket := SessionTicketExtension{Session: uconn.HandshakeState.Session}
|
||||
|
||||
sigAndHashAlgos := []SignatureScheme{
|
||||
ECDSAWithP256AndSHA256,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PKCS1WithSHA384,
|
||||
PKCS1WithSHA1,
|
||||
PKCS1WithSHA512,
|
||||
}
|
||||
|
||||
if tossBiasedCoin(0.63) {
|
||||
sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithSHA1)
|
||||
}
|
||||
if tossBiasedCoin(0.59) {
|
||||
sigAndHashAlgos = append(sigAndHashAlgos, ECDSAWithP521AndSHA512)
|
||||
}
|
||||
if tossBiasedCoin(0.51) || p.TLSVersMax == VersionTLS13 {
|
||||
// https://tools.ietf.org/html/rfc8446 says "...RSASSA-PSS (which is mandatory in TLS 1.3)..."
|
||||
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA256)
|
||||
if tossBiasedCoin(0.9) {
|
||||
// these usually go together
|
||||
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA384)
|
||||
sigAndHashAlgos = append(sigAndHashAlgos, PSSWithSHA512)
|
||||
}
|
||||
}
|
||||
|
||||
err = shuffleSignatures(sigAndHashAlgos)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
sigAndHash := SignatureAlgorithmsExtension{SupportedSignatureAlgorithms: sigAndHashAlgos}
|
||||
|
||||
status := StatusRequestExtension{}
|
||||
sct := SCTExtension{}
|
||||
ems := UtlsExtendedMasterSecretExtension{}
|
||||
points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}}
|
||||
|
||||
curveIDs := []CurveID{}
|
||||
if tossBiasedCoin(0.71) || p.TLSVersMax == VersionTLS13 {
|
||||
curveIDs = append(curveIDs, X25519)
|
||||
}
|
||||
curveIDs = append(curveIDs, CurveP256, CurveP384)
|
||||
if tossBiasedCoin(0.46) {
|
||||
curveIDs = append(curveIDs, CurveP521)
|
||||
}
|
||||
|
||||
curves := SupportedCurvesExtension{curveIDs}
|
||||
|
||||
padding := UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}
|
||||
reneg := RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient}
|
||||
|
||||
p.Extensions = []TLSExtension{
|
||||
&sni,
|
||||
&sessionTicket,
|
||||
&sigAndHash,
|
||||
&points,
|
||||
&curves,
|
||||
}
|
||||
|
||||
if WithALPN {
|
||||
if len(uconn.config.NextProtos) == 0 {
|
||||
// if user didn't specify alpn yet, choose something popular
|
||||
uconn.config.NextProtos = []string{"h2", "http/1.1"}
|
||||
}
|
||||
alpn := ALPNExtension{AlpnProtocols: uconn.config.NextProtos}
|
||||
p.Extensions = append(p.Extensions, &alpn)
|
||||
}
|
||||
|
||||
if tossBiasedCoin(0.62) || p.TLSVersMax == VersionTLS13 {
|
||||
// always include for TLS 1.3, since TLS 1.3 ClientHellos are often over 256 bytes
|
||||
// and that's when padding is required to work around buggy middleboxes
|
||||
p.Extensions = append(p.Extensions, &padding)
|
||||
}
|
||||
if tossBiasedCoin(0.74) {
|
||||
p.Extensions = append(p.Extensions, &status)
|
||||
}
|
||||
if tossBiasedCoin(0.46) {
|
||||
p.Extensions = append(p.Extensions, &sct)
|
||||
}
|
||||
if tossBiasedCoin(0.75) {
|
||||
p.Extensions = append(p.Extensions, &reneg)
|
||||
}
|
||||
if tossBiasedCoin(0.77) {
|
||||
p.Extensions = append(p.Extensions, &ems)
|
||||
}
|
||||
if p.TLSVersMax == VersionTLS13 {
|
||||
ks := KeyShareExtension{[]KeyShare{
|
||||
{Group: X25519}, // the key for the group will be generated later
|
||||
}}
|
||||
if tossBiasedCoin(0.25) {
|
||||
// do not ADD second keyShare because crypto/tls does not support multiple ecdheParams
|
||||
// TODO: add it back when they implement multiple keyShares, or implement it oursevles
|
||||
// ks.KeyShares = append(ks.KeyShares, KeyShare{Group: CurveP256})
|
||||
ks.KeyShares[0].Group = CurveP256
|
||||
}
|
||||
pskExchangeModes := PSKKeyExchangeModesExtension{[]uint8{pskModeDHE}}
|
||||
supportedVersionsExt := SupportedVersionsExtension{
|
||||
Versions: makeSupportedVersions(p.TLSVersMin, p.TLSVersMax),
|
||||
}
|
||||
p.Extensions = append(p.Extensions, &ks, &pskExchangeModes, &supportedVersionsExt)
|
||||
}
|
||||
err = shuffleTLSExtensions(p.Extensions)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
err = uconn.SetTLSVers(p.TLSVersMin, p.TLSVersMax)
|
||||
if err != nil {
|
||||
return p, err
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func tossBiasedCoin(probability float32) bool {
|
||||
// probability is expected to be in [0,1]
|
||||
// this function never returns errors for ease of use
|
||||
const precision = 0xffff
|
||||
threshold := float32(precision) * probability
|
||||
value, err := getRandInt(precision)
|
||||
if err != nil {
|
||||
// I doubt that this code will ever actually be used, as other functions are expected to complain
|
||||
// about used source of entropy. Nonetheless, this is more than enough for given purpose
|
||||
return ((time.Now().Unix() & 1) == 0)
|
||||
}
|
||||
|
||||
if float32(value) <= threshold {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func removeRandomCiphers(s []uint16, maxRemovalProbability float32) []uint16 {
|
||||
// removes elements in place
|
||||
// probability to remove increases for further elements
|
||||
// never remove first cipher
|
||||
if len(s) <= 1 {
|
||||
return s
|
||||
}
|
||||
|
||||
// remove random elements
|
||||
floatLen := float32(len(s))
|
||||
sliceLen := len(s)
|
||||
for i := 1; i < sliceLen; i++ {
|
||||
if tossBiasedCoin(maxRemovalProbability * float32(i) / floatLen) {
|
||||
s = append(s[:i], s[i+1:]...)
|
||||
sliceLen--
|
||||
i--
|
||||
}
|
||||
}
|
||||
return s[:sliceLen]
|
||||
}
|
||||
|
||||
func removeRC4Ciphers(s []uint16) []uint16 {
|
||||
// removes elements in place
|
||||
sliceLen := len(s)
|
||||
for i := 0; i < sliceLen; i++ {
|
||||
cipher := s[i]
|
||||
if cipher == TLS_ECDHE_ECDSA_WITH_RC4_128_SHA ||
|
||||
cipher == TLS_ECDHE_RSA_WITH_RC4_128_SHA ||
|
||||
cipher == TLS_RSA_WITH_RC4_128_SHA {
|
||||
s = append(s[:i], s[i+1:]...)
|
||||
sliceLen--
|
||||
i--
|
||||
}
|
||||
}
|
||||
return s[:sliceLen]
|
||||
}
|
||||
|
||||
func getRandInt(max int) (int, error) {
|
||||
bigInt, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||
return int(bigInt.Int64()), err
|
||||
}
|
||||
|
||||
func getRandPerm(n int) ([]int, error) {
|
||||
permArray := make([]int, n)
|
||||
for i := 1; i < n; i++ {
|
||||
j, err := getRandInt(i + 1)
|
||||
if err != nil {
|
||||
return permArray, err
|
||||
}
|
||||
permArray[i] = permArray[j]
|
||||
permArray[j] = i
|
||||
}
|
||||
return permArray, nil
|
||||
}
|
||||
|
||||
func shuffledCiphers() ([]uint16, error) {
|
||||
ciphers := make(sortableCiphers, len(cipherSuites))
|
||||
perm, err := getRandPerm(len(cipherSuites))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, suite := range cipherSuites {
|
||||
ciphers[i] = sortableCipher{suite: suite.id,
|
||||
isObsolete: ((suite.flags & suiteTLS12) == 0),
|
||||
randomTag: perm[i]}
|
||||
}
|
||||
sort.Sort(ciphers)
|
||||
return ciphers.GetCiphers(), nil
|
||||
}
|
||||
|
||||
type sortableCipher struct {
|
||||
isObsolete bool
|
||||
randomTag int
|
||||
suite uint16
|
||||
}
|
||||
|
||||
type sortableCiphers []sortableCipher
|
||||
|
||||
func (ciphers sortableCiphers) Len() int {
|
||||
return len(ciphers)
|
||||
}
|
||||
|
||||
func (ciphers sortableCiphers) Less(i, j int) bool {
|
||||
if ciphers[i].isObsolete && !ciphers[j].isObsolete {
|
||||
return false
|
||||
}
|
||||
if ciphers[j].isObsolete && !ciphers[i].isObsolete {
|
||||
return true
|
||||
}
|
||||
return ciphers[i].randomTag < ciphers[j].randomTag
|
||||
}
|
||||
|
||||
func (ciphers sortableCiphers) Swap(i, j int) {
|
||||
ciphers[i], ciphers[j] = ciphers[j], ciphers[i]
|
||||
}
|
||||
|
||||
func (ciphers sortableCiphers) GetCiphers() []uint16 {
|
||||
cipherIDs := make([]uint16, len(ciphers))
|
||||
for i := range ciphers {
|
||||
cipherIDs[i] = ciphers[i].suite
|
||||
}
|
||||
return cipherIDs
|
||||
}
|
||||
|
||||
// so much for generics
|
||||
func shuffleTLSExtensions(s []TLSExtension) error {
|
||||
// shuffles array in place
|
||||
perm, err := getRandPerm(len(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range s {
|
||||
s[i], s[perm[i]] = s[perm[i]], s[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// so much for generics
|
||||
func shuffleSignatures(s []SignatureScheme) error {
|
||||
// shuffles array in place
|
||||
perm, err := getRandPerm(len(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range s {
|
||||
s[i], s[perm[i]] = s[perm[i]], s[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// so much for generics
|
||||
func shuffleUInts16(s []uint16) error {
|
||||
// shuffles array in place
|
||||
perm, err := getRandPerm(len(s))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for i := range s {
|
||||
s[i], s[perm[i]] = s[perm[i]], s[i]
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,604 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/x509"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// ClientHandshakeState includes both TLS 1.3-only and TLS 1.2-only states,
|
||||
// only one of them will be used, depending on negotiated version.
|
||||
//
|
||||
// ClientHandshakeState will be converted into and from either
|
||||
// - clientHandshakeState (TLS 1.2)
|
||||
// - clientHandshakeStateTLS13 (TLS 1.3)
|
||||
// uTLS will call .handshake() on one of these private internal states,
|
||||
// to perform TLS handshake using standard crypto/tls implementation.
|
||||
type ClientHandshakeState struct {
|
||||
C *Conn
|
||||
ServerHello *ServerHelloMsg
|
||||
Hello *ClientHelloMsg
|
||||
MasterSecret []byte
|
||||
Session *ClientSessionState
|
||||
|
||||
State12 TLS12OnlyState
|
||||
State13 TLS13OnlyState
|
||||
}
|
||||
|
||||
// TLS 1.3 only
|
||||
type TLS13OnlyState struct {
|
||||
Suite *CipherSuiteTLS13
|
||||
EcdheParams EcdheParameters
|
||||
EarlySecret []byte
|
||||
BinderKey []byte
|
||||
CertReq *CertificateRequestMsgTLS13
|
||||
UsingPSK bool
|
||||
SentDummyCCS bool
|
||||
Transcript hash.Hash
|
||||
TrafficSecret []byte // client_application_traffic_secret_0
|
||||
}
|
||||
|
||||
// TLS 1.2 and before only
|
||||
type TLS12OnlyState struct {
|
||||
FinishedHash FinishedHash
|
||||
Suite CipherSuite
|
||||
}
|
||||
|
||||
func (chs *ClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 {
|
||||
if chs == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &clientHandshakeStateTLS13{
|
||||
c: chs.C,
|
||||
serverHello: chs.ServerHello.getPrivatePtr(),
|
||||
hello: chs.Hello.getPrivatePtr(),
|
||||
ecdheParams: chs.State13.EcdheParams,
|
||||
|
||||
session: chs.Session,
|
||||
earlySecret: chs.State13.EarlySecret,
|
||||
binderKey: chs.State13.BinderKey,
|
||||
|
||||
certReq: chs.State13.CertReq.toPrivate(),
|
||||
usingPSK: chs.State13.UsingPSK,
|
||||
sentDummyCCS: chs.State13.SentDummyCCS,
|
||||
suite: chs.State13.Suite.toPrivate(),
|
||||
transcript: chs.State13.Transcript,
|
||||
masterSecret: chs.MasterSecret,
|
||||
trafficSecret: chs.State13.TrafficSecret,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (chs13 *clientHandshakeStateTLS13) toPublic13() *ClientHandshakeState {
|
||||
if chs13 == nil {
|
||||
return nil
|
||||
} else {
|
||||
tls13State := TLS13OnlyState{
|
||||
EcdheParams: chs13.ecdheParams,
|
||||
EarlySecret: chs13.earlySecret,
|
||||
BinderKey: chs13.binderKey,
|
||||
CertReq: chs13.certReq.toPublic(),
|
||||
UsingPSK: chs13.usingPSK,
|
||||
SentDummyCCS: chs13.sentDummyCCS,
|
||||
Suite: chs13.suite.toPublic(),
|
||||
TrafficSecret: chs13.trafficSecret,
|
||||
Transcript: chs13.transcript,
|
||||
}
|
||||
return &ClientHandshakeState{
|
||||
C: chs13.c,
|
||||
ServerHello: chs13.serverHello.getPublicPtr(),
|
||||
Hello: chs13.hello.getPublicPtr(),
|
||||
|
||||
Session: chs13.session,
|
||||
|
||||
MasterSecret: chs13.masterSecret,
|
||||
|
||||
State13: tls13State,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (chs *ClientHandshakeState) toPrivate12() *clientHandshakeState {
|
||||
if chs == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &clientHandshakeState{
|
||||
c: chs.C,
|
||||
serverHello: chs.ServerHello.getPrivatePtr(),
|
||||
hello: chs.Hello.getPrivatePtr(),
|
||||
suite: chs.State12.Suite.getPrivatePtr(),
|
||||
session: chs.Session,
|
||||
|
||||
masterSecret: chs.MasterSecret,
|
||||
|
||||
finishedHash: *chs.State12.FinishedHash.getPrivatePtr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (chs12 *clientHandshakeState) toPublic13() *ClientHandshakeState {
|
||||
if chs12 == nil {
|
||||
return nil
|
||||
} else {
|
||||
tls12State := TLS12OnlyState{
|
||||
Suite: *chs12.suite.getPublicPtr(),
|
||||
FinishedHash: *chs12.finishedHash.getPublicPtr(),
|
||||
}
|
||||
return &ClientHandshakeState{
|
||||
C: chs12.c,
|
||||
ServerHello: chs12.serverHello.getPublicPtr(),
|
||||
Hello: chs12.hello.getPublicPtr(),
|
||||
|
||||
Session: chs12.session,
|
||||
|
||||
MasterSecret: chs12.masterSecret,
|
||||
|
||||
State12: tls12State,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type EcdheParameters interface {
|
||||
ecdheParameters
|
||||
}
|
||||
|
||||
type CertificateRequestMsgTLS13 struct {
|
||||
Raw []byte
|
||||
OcspStapling bool
|
||||
Scts bool
|
||||
SupportedSignatureAlgorithms []SignatureScheme
|
||||
SupportedSignatureAlgorithmsCert []SignatureScheme
|
||||
CertificateAuthorities [][]byte
|
||||
}
|
||||
|
||||
func (crm *certificateRequestMsgTLS13) toPublic() *CertificateRequestMsgTLS13 {
|
||||
if crm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &CertificateRequestMsgTLS13{
|
||||
Raw: crm.raw,
|
||||
OcspStapling: crm.ocspStapling,
|
||||
Scts: crm.scts,
|
||||
SupportedSignatureAlgorithms: crm.supportedSignatureAlgorithms,
|
||||
SupportedSignatureAlgorithmsCert: crm.supportedSignatureAlgorithmsCert,
|
||||
CertificateAuthorities: crm.certificateAuthorities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (crm *CertificateRequestMsgTLS13) toPrivate() *certificateRequestMsgTLS13 {
|
||||
if crm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &certificateRequestMsgTLS13{
|
||||
raw: crm.Raw,
|
||||
ocspStapling: crm.OcspStapling,
|
||||
scts: crm.Scts,
|
||||
supportedSignatureAlgorithms: crm.SupportedSignatureAlgorithms,
|
||||
supportedSignatureAlgorithmsCert: crm.SupportedSignatureAlgorithmsCert,
|
||||
certificateAuthorities: crm.CertificateAuthorities,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CipherSuiteTLS13 struct {
|
||||
Id uint16
|
||||
KeyLen int
|
||||
Aead func(key, fixedNonce []byte) aead
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
func (c *cipherSuiteTLS13) toPublic() *CipherSuiteTLS13 {
|
||||
if c == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &CipherSuiteTLS13{
|
||||
Id: c.id,
|
||||
KeyLen: c.keyLen,
|
||||
Aead: c.aead,
|
||||
Hash: c.hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CipherSuiteTLS13) toPrivate() *cipherSuiteTLS13 {
|
||||
if c == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &cipherSuiteTLS13{
|
||||
id: c.Id,
|
||||
keyLen: c.KeyLen,
|
||||
aead: c.Aead,
|
||||
hash: c.Hash,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ServerHelloMsg struct {
|
||||
Raw []byte
|
||||
Vers uint16
|
||||
Random []byte
|
||||
SessionId []byte
|
||||
CipherSuite uint16
|
||||
CompressionMethod uint8
|
||||
NextProtoNeg bool
|
||||
NextProtos []string
|
||||
OcspStapling bool
|
||||
Scts [][]byte
|
||||
Ems bool
|
||||
TicketSupported bool
|
||||
SecureRenegotiation []byte
|
||||
SecureRenegotiationSupported bool
|
||||
AlpnProtocol string
|
||||
|
||||
// 1.3
|
||||
SupportedVersion uint16
|
||||
ServerShare keyShare
|
||||
SelectedIdentityPresent bool
|
||||
SelectedIdentity uint16
|
||||
Cookie []byte // HelloRetryRequest extension
|
||||
SelectedGroup CurveID // HelloRetryRequest extension
|
||||
|
||||
}
|
||||
|
||||
func (shm *ServerHelloMsg) getPrivatePtr() *serverHelloMsg {
|
||||
if shm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &serverHelloMsg{
|
||||
raw: shm.Raw,
|
||||
vers: shm.Vers,
|
||||
random: shm.Random,
|
||||
sessionId: shm.SessionId,
|
||||
cipherSuite: shm.CipherSuite,
|
||||
compressionMethod: shm.CompressionMethod,
|
||||
nextProtoNeg: shm.NextProtoNeg,
|
||||
nextProtos: shm.NextProtos,
|
||||
ocspStapling: shm.OcspStapling,
|
||||
scts: shm.Scts,
|
||||
ems: shm.Ems,
|
||||
ticketSupported: shm.TicketSupported,
|
||||
secureRenegotiation: shm.SecureRenegotiation,
|
||||
secureRenegotiationSupported: shm.SecureRenegotiationSupported,
|
||||
alpnProtocol: shm.AlpnProtocol,
|
||||
supportedVersion: shm.SupportedVersion,
|
||||
serverShare: shm.ServerShare,
|
||||
selectedIdentityPresent: shm.SelectedIdentityPresent,
|
||||
selectedIdentity: shm.SelectedIdentity,
|
||||
cookie: shm.Cookie,
|
||||
selectedGroup: shm.SelectedGroup,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (shm *serverHelloMsg) getPublicPtr() *ServerHelloMsg {
|
||||
if shm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &ServerHelloMsg{
|
||||
Raw: shm.raw,
|
||||
Vers: shm.vers,
|
||||
Random: shm.random,
|
||||
SessionId: shm.sessionId,
|
||||
CipherSuite: shm.cipherSuite,
|
||||
CompressionMethod: shm.compressionMethod,
|
||||
NextProtoNeg: shm.nextProtoNeg,
|
||||
NextProtos: shm.nextProtos,
|
||||
OcspStapling: shm.ocspStapling,
|
||||
Scts: shm.scts,
|
||||
Ems: shm.ems,
|
||||
TicketSupported: shm.ticketSupported,
|
||||
SecureRenegotiation: shm.secureRenegotiation,
|
||||
SecureRenegotiationSupported: shm.secureRenegotiationSupported,
|
||||
AlpnProtocol: shm.alpnProtocol,
|
||||
SupportedVersion: shm.supportedVersion,
|
||||
ServerShare: shm.serverShare,
|
||||
SelectedIdentityPresent: shm.selectedIdentityPresent,
|
||||
SelectedIdentity: shm.selectedIdentity,
|
||||
Cookie: shm.cookie,
|
||||
SelectedGroup: shm.selectedGroup,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ClientHelloMsg struct {
|
||||
Raw []byte
|
||||
Vers uint16
|
||||
Random []byte
|
||||
SessionId []byte
|
||||
CipherSuites []uint16
|
||||
CompressionMethods []uint8
|
||||
NextProtoNeg bool
|
||||
ServerName string
|
||||
OcspStapling bool
|
||||
Scts bool
|
||||
Ems bool // [UTLS] actually implemented due to its prevalence
|
||||
SupportedCurves []CurveID
|
||||
SupportedPoints []uint8
|
||||
TicketSupported bool
|
||||
SessionTicket []uint8
|
||||
SupportedSignatureAlgorithms []SignatureScheme
|
||||
SecureRenegotiation []byte
|
||||
SecureRenegotiationSupported bool
|
||||
AlpnProtocols []string
|
||||
|
||||
// 1.3
|
||||
SupportedSignatureAlgorithmsCert []SignatureScheme
|
||||
SupportedVersions []uint16
|
||||
Cookie []byte
|
||||
KeyShares []KeyShare
|
||||
EarlyData bool
|
||||
PskModes []uint8
|
||||
PskIdentities []pskIdentity
|
||||
PskBinders [][]byte
|
||||
}
|
||||
|
||||
func (chm *ClientHelloMsg) getPrivatePtr() *clientHelloMsg {
|
||||
if chm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &clientHelloMsg{
|
||||
raw: chm.Raw,
|
||||
vers: chm.Vers,
|
||||
random: chm.Random,
|
||||
sessionId: chm.SessionId,
|
||||
cipherSuites: chm.CipherSuites,
|
||||
compressionMethods: chm.CompressionMethods,
|
||||
nextProtoNeg: chm.NextProtoNeg,
|
||||
serverName: chm.ServerName,
|
||||
ocspStapling: chm.OcspStapling,
|
||||
scts: chm.Scts,
|
||||
ems: chm.Ems,
|
||||
supportedCurves: chm.SupportedCurves,
|
||||
supportedPoints: chm.SupportedPoints,
|
||||
ticketSupported: chm.TicketSupported,
|
||||
sessionTicket: chm.SessionTicket,
|
||||
supportedSignatureAlgorithms: chm.SupportedSignatureAlgorithms,
|
||||
secureRenegotiation: chm.SecureRenegotiation,
|
||||
secureRenegotiationSupported: chm.SecureRenegotiationSupported,
|
||||
alpnProtocols: chm.AlpnProtocols,
|
||||
|
||||
supportedSignatureAlgorithmsCert: chm.SupportedSignatureAlgorithmsCert,
|
||||
supportedVersions: chm.SupportedVersions,
|
||||
cookie: chm.Cookie,
|
||||
keyShares: KeyShares(chm.KeyShares).ToPrivate(),
|
||||
earlyData: chm.EarlyData,
|
||||
pskModes: chm.PskModes,
|
||||
pskIdentities: chm.PskIdentities,
|
||||
pskBinders: chm.PskBinders,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (chm *clientHelloMsg) getPublicPtr() *ClientHelloMsg {
|
||||
if chm == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &ClientHelloMsg{
|
||||
Raw: chm.raw,
|
||||
Vers: chm.vers,
|
||||
Random: chm.random,
|
||||
SessionId: chm.sessionId,
|
||||
CipherSuites: chm.cipherSuites,
|
||||
CompressionMethods: chm.compressionMethods,
|
||||
NextProtoNeg: chm.nextProtoNeg,
|
||||
ServerName: chm.serverName,
|
||||
OcspStapling: chm.ocspStapling,
|
||||
Scts: chm.scts,
|
||||
Ems: chm.ems,
|
||||
SupportedCurves: chm.supportedCurves,
|
||||
SupportedPoints: chm.supportedPoints,
|
||||
TicketSupported: chm.ticketSupported,
|
||||
SessionTicket: chm.sessionTicket,
|
||||
SupportedSignatureAlgorithms: chm.supportedSignatureAlgorithms,
|
||||
SecureRenegotiation: chm.secureRenegotiation,
|
||||
SecureRenegotiationSupported: chm.secureRenegotiationSupported,
|
||||
AlpnProtocols: chm.alpnProtocols,
|
||||
|
||||
SupportedSignatureAlgorithmsCert: chm.supportedSignatureAlgorithmsCert,
|
||||
SupportedVersions: chm.supportedVersions,
|
||||
Cookie: chm.cookie,
|
||||
KeyShares: keyShares(chm.keyShares).ToPublic(),
|
||||
EarlyData: chm.earlyData,
|
||||
PskModes: chm.pskModes,
|
||||
PskIdentities: chm.pskIdentities,
|
||||
PskBinders: chm.pskBinders,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A CipherSuite is a specific combination of key agreement, cipher and MAC
|
||||
// function. All cipher suites currently assume RSA key agreement.
|
||||
type CipherSuite struct {
|
||||
Id uint16
|
||||
// the lengths, in bytes, of the key material needed for each component.
|
||||
KeyLen int
|
||||
MacLen int
|
||||
IvLen int
|
||||
Ka func(version uint16) keyAgreement
|
||||
// flags is a bitmask of the suite* values, above.
|
||||
Flags int
|
||||
Cipher func(key, iv []byte, isRead bool) interface{}
|
||||
Mac func(version uint16, macKey []byte) macFunction
|
||||
Aead func(key, fixedNonce []byte) aead
|
||||
}
|
||||
|
||||
func (cs *CipherSuite) getPrivatePtr() *cipherSuite {
|
||||
if cs == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &cipherSuite{
|
||||
id: cs.Id,
|
||||
keyLen: cs.KeyLen,
|
||||
macLen: cs.MacLen,
|
||||
ivLen: cs.IvLen,
|
||||
ka: cs.Ka,
|
||||
flags: cs.Flags,
|
||||
cipher: cs.Cipher,
|
||||
mac: cs.Mac,
|
||||
aead: cs.Aead,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cs *cipherSuite) getPublicPtr() *CipherSuite {
|
||||
if cs == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &CipherSuite{
|
||||
Id: cs.id,
|
||||
KeyLen: cs.keyLen,
|
||||
MacLen: cs.macLen,
|
||||
IvLen: cs.ivLen,
|
||||
Ka: cs.ka,
|
||||
Flags: cs.flags,
|
||||
Cipher: cs.cipher,
|
||||
Mac: cs.mac,
|
||||
Aead: cs.aead,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A FinishedHash calculates the hash of a set of handshake messages suitable
|
||||
// for including in a Finished message.
|
||||
type FinishedHash struct {
|
||||
Client hash.Hash
|
||||
Server hash.Hash
|
||||
|
||||
// Prior to TLS 1.2, an additional MD5 hash is required.
|
||||
ClientMD5 hash.Hash
|
||||
ServerMD5 hash.Hash
|
||||
|
||||
// In TLS 1.2, a full buffer is sadly required.
|
||||
Buffer []byte
|
||||
|
||||
Version uint16
|
||||
Prf func(result, secret, label, seed []byte)
|
||||
}
|
||||
|
||||
func (fh *FinishedHash) getPrivatePtr() *finishedHash {
|
||||
if fh == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &finishedHash{
|
||||
client: fh.Client,
|
||||
server: fh.Server,
|
||||
clientMD5: fh.ClientMD5,
|
||||
serverMD5: fh.ServerMD5,
|
||||
buffer: fh.Buffer,
|
||||
version: fh.Version,
|
||||
prf: fh.Prf,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fh *finishedHash) getPublicPtr() *FinishedHash {
|
||||
if fh == nil {
|
||||
return nil
|
||||
} else {
|
||||
return &FinishedHash{
|
||||
Client: fh.client,
|
||||
Server: fh.server,
|
||||
ClientMD5: fh.clientMD5,
|
||||
ServerMD5: fh.serverMD5,
|
||||
Buffer: fh.buffer,
|
||||
Version: fh.version,
|
||||
Prf: fh.prf}
|
||||
}
|
||||
}
|
||||
|
||||
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
|
||||
type KeyShare struct {
|
||||
Group CurveID
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type KeyShares []KeyShare
|
||||
type keyShares []keyShare
|
||||
|
||||
func (kss keyShares) ToPublic() []KeyShare {
|
||||
var KSS []KeyShare
|
||||
for _, ks := range kss {
|
||||
KSS = append(KSS, KeyShare{Data: ks.data, Group: ks.group})
|
||||
}
|
||||
return KSS
|
||||
}
|
||||
func (KSS KeyShares) ToPrivate() []keyShare {
|
||||
var kss []keyShare
|
||||
for _, KS := range KSS {
|
||||
kss = append(kss, keyShare{data: KS.Data, group: KS.Group})
|
||||
}
|
||||
return kss
|
||||
}
|
||||
|
||||
// ClientSessionState is public, but all its fields are private. Let's add setters, getters and constructor
|
||||
|
||||
// ClientSessionState contains the state needed by clients to resume TLS sessions.
|
||||
func MakeClientSessionState(
|
||||
SessionTicket []uint8,
|
||||
Vers uint16,
|
||||
CipherSuite uint16,
|
||||
MasterSecret []byte,
|
||||
ServerCertificates []*x509.Certificate,
|
||||
VerifiedChains [][]*x509.Certificate) *ClientSessionState {
|
||||
css := ClientSessionState{sessionTicket: SessionTicket,
|
||||
vers: Vers,
|
||||
cipherSuite: CipherSuite,
|
||||
masterSecret: MasterSecret,
|
||||
serverCertificates: ServerCertificates,
|
||||
verifiedChains: VerifiedChains}
|
||||
return &css
|
||||
}
|
||||
|
||||
// Encrypted ticket used for session resumption with server
|
||||
func (css *ClientSessionState) SessionTicket() []uint8 {
|
||||
return css.sessionTicket
|
||||
}
|
||||
|
||||
// SSL/TLS version negotiated for the session
|
||||
func (css *ClientSessionState) Vers() uint16 {
|
||||
return css.vers
|
||||
}
|
||||
|
||||
// Ciphersuite negotiated for the session
|
||||
func (css *ClientSessionState) CipherSuite() uint16 {
|
||||
return css.cipherSuite
|
||||
}
|
||||
|
||||
// MasterSecret generated by client on a full handshake
|
||||
func (css *ClientSessionState) MasterSecret() []byte {
|
||||
return css.masterSecret
|
||||
}
|
||||
|
||||
// Certificate chain presented by the server
|
||||
func (css *ClientSessionState) ServerCertificates() []*x509.Certificate {
|
||||
return css.serverCertificates
|
||||
}
|
||||
|
||||
// Certificate chains we built for verification
|
||||
func (css *ClientSessionState) VerifiedChains() [][]*x509.Certificate {
|
||||
return css.verifiedChains
|
||||
}
|
||||
|
||||
func (css *ClientSessionState) SetSessionTicket(SessionTicket []uint8) {
|
||||
css.sessionTicket = SessionTicket
|
||||
}
|
||||
func (css *ClientSessionState) SetVers(Vers uint16) {
|
||||
css.vers = Vers
|
||||
}
|
||||
func (css *ClientSessionState) SetCipherSuite(CipherSuite uint16) {
|
||||
css.cipherSuite = CipherSuite
|
||||
}
|
||||
func (css *ClientSessionState) SetMasterSecret(MasterSecret []byte) {
|
||||
css.masterSecret = MasterSecret
|
||||
}
|
||||
func (css *ClientSessionState) SetServerCertificates(ServerCertificates []*x509.Certificate) {
|
||||
css.serverCertificates = ServerCertificates
|
||||
}
|
||||
func (css *ClientSessionState) SetVerifiedChains(VerifiedChains [][]*x509.Certificate) {
|
||||
css.verifiedChains = VerifiedChains
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Roller struct {
|
||||
HelloIDs []ClientHelloID
|
||||
HelloIDMu sync.Mutex
|
||||
WorkingHelloID *ClientHelloID
|
||||
TcpDialTimeout time.Duration
|
||||
TlsHandshakeTimeout time.Duration
|
||||
}
|
||||
|
||||
// NewRoller creates Roller object with default range of HelloIDs to cycle through until a
|
||||
// working/unblocked one is found.
|
||||
func NewRoller() (*Roller, error) {
|
||||
tcpDialTimeoutInc, err := getRandInt(14)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tcpDialTimeoutInc = 7 + tcpDialTimeoutInc
|
||||
|
||||
tlsHandshakeTimeoutInc, err := getRandInt(20)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsHandshakeTimeoutInc = 11 + tlsHandshakeTimeoutInc
|
||||
|
||||
return &Roller{
|
||||
HelloIDs: []ClientHelloID{
|
||||
HelloChrome_Auto,
|
||||
HelloFirefox_Auto,
|
||||
HelloIOS_Auto,
|
||||
HelloRandomized,
|
||||
},
|
||||
TcpDialTimeout: time.Second * time.Duration(tcpDialTimeoutInc),
|
||||
TlsHandshakeTimeout: time.Second * time.Duration(tlsHandshakeTimeoutInc),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Dial attempts to establish connection to given address using different HelloIDs.
|
||||
// If a working HelloID is found, it is used again for subsequent Dials.
|
||||
// If tcp connection fails or all HelloIDs are tried, returns with last error.
|
||||
//
|
||||
// Usage examples:
|
||||
// Dial("tcp4", "google.com:443", "google.com")
|
||||
// Dial("tcp", "10.23.144.22:443", "mywebserver.org")
|
||||
func (c *Roller) Dial(network, addr, serverName string) (*UConn, error) {
|
||||
helloIDs, err := shuffleClientHelloIDs(c.HelloIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.HelloIDMu.Lock()
|
||||
workingHelloId := c.WorkingHelloID // keep using same helloID, if it works
|
||||
c.HelloIDMu.Unlock()
|
||||
if workingHelloId != nil {
|
||||
for i, ID := range helloIDs {
|
||||
if ID == *workingHelloId {
|
||||
helloIDs[i] = helloIDs[0]
|
||||
helloIDs[0] = *workingHelloId // push working hello ID first
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tcpConn net.Conn
|
||||
for _, helloID := range helloIDs {
|
||||
tcpConn, err = net.DialTimeout(network, addr, c.TcpDialTimeout)
|
||||
if err != nil {
|
||||
return nil, err // on tcp Dial failure return with error right away
|
||||
}
|
||||
|
||||
client := UClient(tcpConn, nil, helloID)
|
||||
client.SetSNI(serverName)
|
||||
client.SetDeadline(time.Now().Add(c.TlsHandshakeTimeout))
|
||||
err = client.Handshake()
|
||||
client.SetDeadline(time.Time{}) // unset timeout
|
||||
if err != nil {
|
||||
continue // on tls Dial error keep trying HelloIDs
|
||||
}
|
||||
|
||||
c.HelloIDMu.Lock()
|
||||
c.WorkingHelloID = &helloID
|
||||
c.HelloIDMu.Unlock()
|
||||
return client, err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// returns a shuffled copy of input
|
||||
func shuffleClientHelloIDs(helloIDs []ClientHelloID) ([]ClientHelloID, error) {
|
||||
perm, err := getRandPerm(len(helloIDs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shuffled := make([]ClientHelloID, len(helloIDs))
|
||||
for i, randI := range perm {
|
||||
shuffled[i] = helloIDs[randI]
|
||||
}
|
||||
return shuffled, nil
|
||||
}
|
@ -1,688 +0,0 @@
|
||||
// Copyright 2017 Google Inc. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
type TLSExtension interface {
|
||||
writeToUConn(*UConn) error
|
||||
|
||||
Len() int // includes header
|
||||
|
||||
// Read reads up to len(p) bytes into p.
|
||||
// It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
|
||||
Read(p []byte) (n int, err error) // implements io.Reader
|
||||
}
|
||||
|
||||
type NPNExtension struct {
|
||||
NextProtos []string
|
||||
}
|
||||
|
||||
func (e *NPNExtension) writeToUConn(uc *UConn) error {
|
||||
uc.config.NextProtos = e.NextProtos
|
||||
uc.HandshakeState.Hello.NextProtoNeg = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *NPNExtension) Len() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (e *NPNExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
b[0] = byte(extensionNextProtoNeg >> 8)
|
||||
b[1] = byte(extensionNextProtoNeg & 0xff)
|
||||
// The length is always 0
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SNIExtension struct {
|
||||
ServerName string // not an array because go crypto/tls doesn't support multiple SNIs
|
||||
}
|
||||
|
||||
func (e *SNIExtension) writeToUConn(uc *UConn) error {
|
||||
uc.config.ServerName = e.ServerName
|
||||
uc.HandshakeState.Hello.ServerName = e.ServerName
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SNIExtension) Len() int {
|
||||
return 4 + 2 + 1 + 2 + len(e.ServerName)
|
||||
}
|
||||
|
||||
func (e *SNIExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// RFC 3546, section 3.1
|
||||
b[0] = byte(extensionServerName >> 8)
|
||||
b[1] = byte(extensionServerName)
|
||||
b[2] = byte((len(e.ServerName) + 5) >> 8)
|
||||
b[3] = byte((len(e.ServerName) + 5))
|
||||
b[4] = byte((len(e.ServerName) + 3) >> 8)
|
||||
b[5] = byte(len(e.ServerName) + 3)
|
||||
// b[6] Server Name Type: host_name (0)
|
||||
b[7] = byte(len(e.ServerName) >> 8)
|
||||
b[8] = byte(len(e.ServerName))
|
||||
copy(b[9:], []byte(e.ServerName))
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type StatusRequestExtension struct {
|
||||
}
|
||||
|
||||
func (e *StatusRequestExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.OcspStapling = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *StatusRequestExtension) Len() int {
|
||||
return 9
|
||||
}
|
||||
|
||||
func (e *StatusRequestExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// RFC 4366, section 3.6
|
||||
b[0] = byte(extensionStatusRequest >> 8)
|
||||
b[1] = byte(extensionStatusRequest)
|
||||
b[2] = 0
|
||||
b[3] = 5
|
||||
b[4] = 1 // OCSP type
|
||||
// Two zero valued uint16s for the two lengths.
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SupportedCurvesExtension struct {
|
||||
Curves []CurveID
|
||||
}
|
||||
|
||||
func (e *SupportedCurvesExtension) writeToUConn(uc *UConn) error {
|
||||
uc.config.CurvePreferences = e.Curves
|
||||
uc.HandshakeState.Hello.SupportedCurves = e.Curves
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SupportedCurvesExtension) Len() int {
|
||||
return 6 + 2*len(e.Curves)
|
||||
}
|
||||
|
||||
func (e *SupportedCurvesExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.1
|
||||
b[0] = byte(extensionSupportedCurves >> 8)
|
||||
b[1] = byte(extensionSupportedCurves)
|
||||
b[2] = byte((2 + 2*len(e.Curves)) >> 8)
|
||||
b[3] = byte((2 + 2*len(e.Curves)))
|
||||
b[4] = byte((2 * len(e.Curves)) >> 8)
|
||||
b[5] = byte((2 * len(e.Curves)))
|
||||
for i, curve := range e.Curves {
|
||||
b[6+2*i] = byte(curve >> 8)
|
||||
b[7+2*i] = byte(curve)
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SupportedPointsExtension struct {
|
||||
SupportedPoints []uint8
|
||||
}
|
||||
|
||||
func (e *SupportedPointsExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.SupportedPoints = e.SupportedPoints
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SupportedPointsExtension) Len() int {
|
||||
return 5 + len(e.SupportedPoints)
|
||||
}
|
||||
|
||||
func (e *SupportedPointsExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// http://tools.ietf.org/html/rfc4492#section-5.5.2
|
||||
b[0] = byte(extensionSupportedPoints >> 8)
|
||||
b[1] = byte(extensionSupportedPoints)
|
||||
b[2] = byte((1 + len(e.SupportedPoints)) >> 8)
|
||||
b[3] = byte((1 + len(e.SupportedPoints)))
|
||||
b[4] = byte((len(e.SupportedPoints)))
|
||||
for i, pointFormat := range e.SupportedPoints {
|
||||
b[5+i] = pointFormat
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SignatureAlgorithmsExtension struct {
|
||||
SupportedSignatureAlgorithms []SignatureScheme
|
||||
}
|
||||
|
||||
func (e *SignatureAlgorithmsExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.SupportedSignatureAlgorithms = e.SupportedSignatureAlgorithms
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SignatureAlgorithmsExtension) Len() int {
|
||||
return 6 + 2*len(e.SupportedSignatureAlgorithms)
|
||||
}
|
||||
|
||||
func (e *SignatureAlgorithmsExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
|
||||
b[0] = byte(extensionSignatureAlgorithms >> 8)
|
||||
b[1] = byte(extensionSignatureAlgorithms)
|
||||
b[2] = byte((2 + 2*len(e.SupportedSignatureAlgorithms)) >> 8)
|
||||
b[3] = byte((2 + 2*len(e.SupportedSignatureAlgorithms)))
|
||||
b[4] = byte((2 * len(e.SupportedSignatureAlgorithms)) >> 8)
|
||||
b[5] = byte((2 * len(e.SupportedSignatureAlgorithms)))
|
||||
for i, sigAndHash := range e.SupportedSignatureAlgorithms {
|
||||
b[6+2*i] = byte(sigAndHash >> 8)
|
||||
b[7+2*i] = byte(sigAndHash)
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type RenegotiationInfoExtension struct {
|
||||
renegotiation RenegotiationSupport
|
||||
SecureRenegotiation []byte // if empty, default []byte{0} is assumed
|
||||
}
|
||||
|
||||
func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error {
|
||||
uc.config.Renegotiation = e.renegotiation
|
||||
switch e.renegotiation {
|
||||
case RenegotiateOnceAsClient:
|
||||
fallthrough
|
||||
case RenegotiateFreelyAsClient:
|
||||
uc.HandshakeState.Hello.SecureRenegotiationSupported = true
|
||||
// Note that if we manage to use this in renegotiation(currently only in initial handshake), we'd have to point
|
||||
// uc.ClientHelloMsg.SecureRenegotiation = chs.C.clientFinished
|
||||
// and probably do something else. It's a mess.
|
||||
case RenegotiateNever:
|
||||
default:
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *RenegotiationInfoExtension) Len() int {
|
||||
switch e.renegotiation {
|
||||
case RenegotiateOnceAsClient:
|
||||
fallthrough
|
||||
case RenegotiateFreelyAsClient:
|
||||
extBodyLen := len(e.SecureRenegotiation)
|
||||
if extBodyLen == 0 {
|
||||
extBodyLen = 1
|
||||
}
|
||||
return 4 + extBodyLen
|
||||
case RenegotiateNever:
|
||||
default:
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (e *RenegotiationInfoExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
switch e.renegotiation {
|
||||
case RenegotiateOnceAsClient:
|
||||
fallthrough
|
||||
case RenegotiateFreelyAsClient:
|
||||
secureRenegBody := e.SecureRenegotiation
|
||||
if len(secureRenegBody) == 0 {
|
||||
secureRenegBody = []byte{0}
|
||||
}
|
||||
extBodyLen := len(secureRenegBody)
|
||||
|
||||
b[0] = byte(extensionRenegotiationInfo >> 8)
|
||||
b[1] = byte(extensionRenegotiationInfo & 0xff)
|
||||
b[2] = byte(extBodyLen >> 8)
|
||||
b[3] = byte(extBodyLen)
|
||||
copy(b[4:], secureRenegBody)
|
||||
|
||||
if len(e.SecureRenegotiation) != 0 {
|
||||
copy(b[5:], e.SecureRenegotiation)
|
||||
}
|
||||
case RenegotiateNever:
|
||||
default:
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type ALPNExtension struct {
|
||||
AlpnProtocols []string
|
||||
}
|
||||
|
||||
func (e *ALPNExtension) writeToUConn(uc *UConn) error {
|
||||
uc.config.NextProtos = e.AlpnProtocols
|
||||
uc.HandshakeState.Hello.AlpnProtocols = e.AlpnProtocols
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *ALPNExtension) Len() int {
|
||||
bLen := 2 + 2 + 2
|
||||
for _, s := range e.AlpnProtocols {
|
||||
bLen += 1 + len(s)
|
||||
}
|
||||
return bLen
|
||||
}
|
||||
|
||||
func (e *ALPNExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
b[0] = byte(extensionALPN >> 8)
|
||||
b[1] = byte(extensionALPN & 0xff)
|
||||
lengths := b[2:]
|
||||
b = b[6:]
|
||||
|
||||
stringsLength := 0
|
||||
for _, s := range e.AlpnProtocols {
|
||||
l := len(s)
|
||||
b[0] = byte(l)
|
||||
copy(b[1:], s)
|
||||
b = b[1+l:]
|
||||
stringsLength += 1 + l
|
||||
}
|
||||
|
||||
lengths[2] = byte(stringsLength >> 8)
|
||||
lengths[3] = byte(stringsLength)
|
||||
stringsLength += 2
|
||||
lengths[0] = byte(stringsLength >> 8)
|
||||
lengths[1] = byte(stringsLength)
|
||||
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SCTExtension struct {
|
||||
}
|
||||
|
||||
func (e *SCTExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.Scts = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SCTExtension) Len() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (e *SCTExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// https://tools.ietf.org/html/rfc6962#section-3.3.1
|
||||
b[0] = byte(extensionSCT >> 8)
|
||||
b[1] = byte(extensionSCT)
|
||||
// zero uint16 for the zero-length extension_data
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type SessionTicketExtension struct {
|
||||
Session *ClientSessionState
|
||||
}
|
||||
|
||||
func (e *SessionTicketExtension) writeToUConn(uc *UConn) error {
|
||||
if e.Session != nil {
|
||||
uc.HandshakeState.Session = e.Session
|
||||
uc.HandshakeState.Hello.SessionTicket = e.Session.sessionTicket
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SessionTicketExtension) Len() int {
|
||||
if e.Session != nil {
|
||||
return 4 + len(e.Session.sessionTicket)
|
||||
}
|
||||
return 4
|
||||
}
|
||||
|
||||
func (e *SessionTicketExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
extBodyLen := e.Len() - 4
|
||||
|
||||
b[0] = byte(extensionSessionTicket >> 8)
|
||||
b[1] = byte(extensionSessionTicket)
|
||||
b[2] = byte(extBodyLen >> 8)
|
||||
b[3] = byte(extBodyLen)
|
||||
if extBodyLen > 0 {
|
||||
copy(b[4:], e.Session.sessionTicket)
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type GenericExtension struct {
|
||||
id uint16
|
||||
data []byte
|
||||
}
|
||||
|
||||
func (e *GenericExtension) writeToUConn(uc *UConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *GenericExtension) Len() int {
|
||||
return 4 + len(e.data)
|
||||
}
|
||||
|
||||
func (e *GenericExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
b[0] = byte(e.id >> 8)
|
||||
b[1] = byte(e.id)
|
||||
b[2] = byte(len(e.data) >> 8)
|
||||
b[3] = byte(len(e.data))
|
||||
if len(e.data) > 0 {
|
||||
copy(b[4:], e.data)
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
/*
|
||||
FAKE EXTENSIONS
|
||||
*/
|
||||
|
||||
type FakeChannelIDExtension struct {
|
||||
}
|
||||
|
||||
func (e *FakeChannelIDExtension) writeToUConn(uc *UConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *FakeChannelIDExtension) Len() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (e *FakeChannelIDExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
|
||||
b[0] = byte(fakeExtensionChannelID >> 8)
|
||||
b[1] = byte(fakeExtensionChannelID & 0xff)
|
||||
// The length is 0
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type UtlsExtendedMasterSecretExtension struct {
|
||||
}
|
||||
|
||||
// TODO: update when this extension is implemented in crypto/tls
|
||||
// but we probably won't have to enable it in Config
|
||||
func (e *UtlsExtendedMasterSecretExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.Ems = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *UtlsExtendedMasterSecretExtension) Len() int {
|
||||
return 4
|
||||
}
|
||||
|
||||
func (e *UtlsExtendedMasterSecretExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// https://tools.ietf.org/html/rfc7627
|
||||
b[0] = byte(utlsExtensionExtendedMasterSecret >> 8)
|
||||
b[1] = byte(utlsExtensionExtendedMasterSecret)
|
||||
// The length is 0
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
var extendedMasterSecretLabel = []byte("extended master secret")
|
||||
|
||||
// extendedMasterFromPreMasterSecret generates the master secret from the pre-master
|
||||
// secret and session hash. See https://tools.ietf.org/html/rfc7627#section-4
|
||||
func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, fh finishedHash) []byte {
|
||||
sessionHash := fh.Sum()
|
||||
masterSecret := make([]byte, masterSecretLength)
|
||||
prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, sessionHash)
|
||||
return masterSecret
|
||||
}
|
||||
|
||||
// GREASE stinks with dead parrots, have to be super careful, and, if possible, not include GREASE
|
||||
// https://github.com/google/boringssl/blob/1c68fa2350936ca5897a66b430ebaf333a0e43f5/ssl/internal.h
|
||||
const (
|
||||
ssl_grease_cipher = iota
|
||||
ssl_grease_group
|
||||
ssl_grease_extension1
|
||||
ssl_grease_extension2
|
||||
ssl_grease_version
|
||||
ssl_grease_ticket_extension
|
||||
ssl_grease_last_index = ssl_grease_ticket_extension
|
||||
)
|
||||
|
||||
// it is responsibility of user not to generate multiple grease extensions with same value
|
||||
type UtlsGREASEExtension struct {
|
||||
Value uint16
|
||||
Body []byte // in Chrome first grease has empty body, second grease has a single zero byte
|
||||
}
|
||||
|
||||
func (e *UtlsGREASEExtension) writeToUConn(uc *UConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// will panic if ssl_grease_last_index[index] is out of bounds.
|
||||
func GetBoringGREASEValue(greaseSeed [ssl_grease_last_index]uint16, index int) uint16 {
|
||||
// GREASE value is back from deterministic to random.
|
||||
// https://github.com/google/boringssl/blob/a365138ac60f38b64bfc608b493e0f879845cb88/ssl/handshake_client.c#L530
|
||||
ret := uint16(greaseSeed[index])
|
||||
/* This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16. */
|
||||
ret = (ret & 0xf0) | 0x0a
|
||||
ret |= ret << 8
|
||||
return ret
|
||||
}
|
||||
|
||||
func (e *UtlsGREASEExtension) Len() int {
|
||||
return 4 + len(e.Body)
|
||||
}
|
||||
|
||||
func (e *UtlsGREASEExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
b[0] = byte(e.Value >> 8)
|
||||
b[1] = byte(e.Value)
|
||||
b[2] = byte(len(e.Body) >> 8)
|
||||
b[3] = byte(len(e.Body))
|
||||
if len(e.Body) > 0 {
|
||||
copy(b[4:], e.Body)
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
type UtlsPaddingExtension struct {
|
||||
PaddingLen int
|
||||
WillPad bool // set to false to disable extension
|
||||
|
||||
// Functor for deciding on padding length based on unpadded ClientHello length.
|
||||
// If willPad is false, then this extension should not be included.
|
||||
GetPaddingLen func(clientHelloUnpaddedLen int) (paddingLen int, willPad bool)
|
||||
}
|
||||
|
||||
func (e *UtlsPaddingExtension) writeToUConn(uc *UConn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *UtlsPaddingExtension) Len() int {
|
||||
if e.WillPad {
|
||||
return 4 + e.PaddingLen
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UtlsPaddingExtension) Update(clientHelloUnpaddedLen int) {
|
||||
if e.GetPaddingLen != nil {
|
||||
e.PaddingLen, e.WillPad = e.GetPaddingLen(clientHelloUnpaddedLen)
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UtlsPaddingExtension) Read(b []byte) (int, error) {
|
||||
if !e.WillPad {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
// https://tools.ietf.org/html/rfc7627
|
||||
b[0] = byte(utlsExtensionPadding >> 8)
|
||||
b[1] = byte(utlsExtensionPadding)
|
||||
b[2] = byte(e.PaddingLen >> 8)
|
||||
b[3] = byte(e.PaddingLen)
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
// https://github.com/google/boringssl/blob/7d7554b6b3c79e707e25521e61e066ce2b996e4c/ssl/t1_lib.c#L2803
|
||||
func BoringPaddingStyle(unpaddedLen int) (int, bool) {
|
||||
if unpaddedLen > 0xff && unpaddedLen < 0x200 {
|
||||
paddingLen := 0x200 - unpaddedLen
|
||||
if paddingLen >= 4+1 {
|
||||
paddingLen -= 4
|
||||
} else {
|
||||
paddingLen = 1
|
||||
}
|
||||
return paddingLen, true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
/* TLS 1.3 */
|
||||
type KeyShareExtension struct {
|
||||
KeyShares []KeyShare
|
||||
}
|
||||
|
||||
func (e *KeyShareExtension) Len() int {
|
||||
return 4 + 2 + e.keySharesLen()
|
||||
}
|
||||
|
||||
func (e *KeyShareExtension) keySharesLen() int {
|
||||
extLen := 0
|
||||
for _, ks := range e.KeyShares {
|
||||
extLen += 4 + len(ks.Data)
|
||||
}
|
||||
return extLen
|
||||
}
|
||||
|
||||
func (e *KeyShareExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
b[0] = byte(extensionKeyShare >> 8)
|
||||
b[1] = byte(extensionKeyShare)
|
||||
keySharesLen := e.keySharesLen()
|
||||
b[2] = byte((keySharesLen + 2) >> 8)
|
||||
b[3] = byte((keySharesLen + 2))
|
||||
b[4] = byte((keySharesLen) >> 8)
|
||||
b[5] = byte((keySharesLen))
|
||||
|
||||
i := 6
|
||||
for _, ks := range e.KeyShares {
|
||||
b[i] = byte(ks.Group >> 8)
|
||||
b[i+1] = byte(ks.Group)
|
||||
b[i+2] = byte(len(ks.Data) >> 8)
|
||||
b[i+3] = byte(len(ks.Data))
|
||||
copy(b[i+4:], ks.Data)
|
||||
i += 4 + len(ks.Data)
|
||||
}
|
||||
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
func (e *KeyShareExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.KeyShares = e.KeyShares
|
||||
return nil
|
||||
}
|
||||
|
||||
type PSKKeyExchangeModesExtension struct {
|
||||
Modes []uint8
|
||||
}
|
||||
|
||||
func (e *PSKKeyExchangeModesExtension) Len() int {
|
||||
return 4 + 1 + len(e.Modes)
|
||||
}
|
||||
|
||||
func (e *PSKKeyExchangeModesExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
if len(e.Modes) > 255 {
|
||||
return 0, errors.New("too many PSK Key Exchange modes")
|
||||
}
|
||||
|
||||
b[0] = byte(extensionPSKModes >> 8)
|
||||
b[1] = byte(extensionPSKModes)
|
||||
|
||||
modesLen := len(e.Modes)
|
||||
b[2] = byte((modesLen + 1) >> 8)
|
||||
b[3] = byte((modesLen + 1))
|
||||
b[4] = byte(modesLen)
|
||||
|
||||
if len(e.Modes) > 0 {
|
||||
copy(b[5:], e.Modes)
|
||||
}
|
||||
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
func (e *PSKKeyExchangeModesExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.PskModes = e.Modes
|
||||
return nil
|
||||
}
|
||||
|
||||
type SupportedVersionsExtension struct {
|
||||
Versions []uint16
|
||||
}
|
||||
|
||||
func (e *SupportedVersionsExtension) writeToUConn(uc *UConn) error {
|
||||
uc.HandshakeState.Hello.SupportedVersions = e.Versions
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *SupportedVersionsExtension) Len() int {
|
||||
return 4 + 1 + (2 * len(e.Versions))
|
||||
}
|
||||
|
||||
func (e *SupportedVersionsExtension) Read(b []byte) (int, error) {
|
||||
if len(b) < e.Len() {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
extLen := 2 * len(e.Versions)
|
||||
if extLen > 255 {
|
||||
return 0, errors.New("too many supported versions")
|
||||
}
|
||||
|
||||
b[0] = byte(extensionSupportedVersions >> 8)
|
||||
b[1] = byte(extensionSupportedVersions)
|
||||
b[2] = byte((extLen + 1) >> 8)
|
||||
b[3] = byte((extLen + 1))
|
||||
b[4] = byte(extLen)
|
||||
|
||||
i := 5
|
||||
for _, sv := range e.Versions {
|
||||
b[i] = byte(sv >> 8)
|
||||
b[i+1] = byte(sv)
|
||||
i += 2
|
||||
}
|
||||
return e.Len(), io.EOF
|
||||
}
|
||||
|
||||
// TODO: FakeCertificateCompressionAlgorithmsExtension
|
||||
// TODO: FakeRecordSizeLimitExtension
|
19
external/update-deps.sh
vendored
19
external/update-deps.sh
vendored
@ -17,25 +17,6 @@ rsync -rv "./github.com/lucas-clemente/quic-go/vendor/github.com/cloudflare/" ".
|
||||
rsync -rv "./github.com/lucas-clemente/quic-go/vendor/github.com/marten-seemann/" "./github.com/marten-seemann/"
|
||||
rm -rf "./github.com/lucas-clemente/quic-go/vendor/"
|
||||
|
||||
rsync -rv "$GOPATH/src/github.com/gorilla/websocket/" "./github.com/gorilla/websocket/"
|
||||
rm -rf ./github.com/gorilla/websocket/\.*
|
||||
rm -rf ./github.com/gorilla/websocket/examples
|
||||
rm "./github.com/gorilla/websocket/.gitignore"
|
||||
rm "./github.com/gorilla/websocket/client_clone_legacy.go"
|
||||
rm "./github.com/gorilla/websocket/compression.go"
|
||||
rm "./github.com/gorilla/websocket/conn_write_legacy.go"
|
||||
rm "./github.com/gorilla/websocket/json.go"
|
||||
rm "./github.com/gorilla/websocket/prepared.go"
|
||||
rm "./github.com/gorilla/websocket/proxy.go"
|
||||
rm "./github.com/gorilla/websocket/trace_17.go"
|
||||
rm "./github.com/gorilla/websocket/trace.go"
|
||||
rm "./github.com/gorilla/websocket/x_net_proxy.go"
|
||||
|
||||
rsync -rv "$GOPATH/src/github.com/refraction-networking/utls/" "./github.com/refraction-networking/utls/"
|
||||
rm -rf ./github.com/refraction-networking/utls/\.*
|
||||
rm -rf ./github.com/refraction-networking/utls/examples
|
||||
rm -rf ./github.com/refraction-networking/utls/testdata
|
||||
rm -rf ./github.com/refraction-networking/utls/testenv
|
||||
|
||||
find . -name "*_test.go" -delete
|
||||
find . -name "*.yml" -delete
|
||||
|
2
go.mod
2
go.mod
@ -4,7 +4,9 @@ require (
|
||||
github.com/golang/mock v1.2.0
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/gorilla/websocket v1.4.1
|
||||
github.com/miekg/dns v1.1.4
|
||||
github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57
|
||||
go.starlark.net v0.0.0-20190919145610-979af19b165c
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||
|
4
go.sum
4
go.sum
@ -10,8 +10,12 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/miekg/dns v1.1.4 h1:rCMZsU2ScVSYcAsOXgmC6+AKOK+6pmQTOcw03nfwYV0=
|
||||
github.com/miekg/dns v1.1.4/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57 h1:SL1K0QAuC1b54KoY1pjPWe6kSlsFHwK9/oC960fKrTY=
|
||||
github.com/refraction-networking/utls v0.0.0-20190909200633-43c36d3c1f57/go.mod h1:tz9gX959MEFfFN5whTIocCLUG57WiILqtdVxI8c6Wj0=
|
||||
go.starlark.net v0.0.0-20190919145610-979af19b165c h1:WR7X1xgXJlXhQBdorVc9Db3RhwG+J/kp6bLuMyJjfVw=
|
||||
go.starlark.net v0.0.0-20190919145610-979af19b165c/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/net"
|
||||
|
||||
utls "v2ray.com/core/external/github.com/refraction-networking/utls"
|
||||
utls "github.com/refraction-networking/utls"
|
||||
)
|
||||
|
||||
//go:generate errorgen
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"v2ray.com/core/common/buf"
|
||||
"v2ray.com/core/common/errors"
|
||||
"v2ray.com/core/common/serial"
|
||||
"v2ray.com/core/external/github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -6,10 +6,10 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/net"
|
||||
"v2ray.com/core/common/session"
|
||||
"v2ray.com/core/external/github.com/gorilla/websocket"
|
||||
"v2ray.com/core/transport/internet"
|
||||
"v2ray.com/core/transport/internet/tls"
|
||||
)
|
||||
|
@ -9,11 +9,11 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"v2ray.com/core/common"
|
||||
"v2ray.com/core/common/net"
|
||||
http_proto "v2ray.com/core/common/protocol/http"
|
||||
"v2ray.com/core/common/session"
|
||||
"v2ray.com/core/external/github.com/gorilla/websocket"
|
||||
"v2ray.com/core/transport/internet"
|
||||
v2tls "v2ray.com/core/transport/internet/tls"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user