ebc00a95fe
Rather than building for Android using ooni/go, we're now using ooni/oocryto as the TLS dependency. Such a repository only forks crypto/tls and some minor crypto packages and includes the same set of patches that we have been using in ooni/go. This new strategy should be better than the previous one in terms of building for Android, because we can use the vanilla go1.18.2 build. It also seems that it is easier to track and merge from upstream with ooni/oocrypto than it is with ooni/go. Should this assessment be wrong, we can revert back to the previous scenario where we used ooni/go. See https://github.com/ooni/probe/issues/2106 for extra context.
139 lines
4.2 KiB
Go
139 lines
4.2 KiB
Go
package netxlite
|
|
|
|
//
|
|
// Code to use yawning/utls or refraction-networking/utls
|
|
//
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"reflect"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
utls "gitlab.com/yawning/utls.git"
|
|
)
|
|
|
|
// NewTLSHandshakerUTLS creates a new TLS handshaker using
|
|
// gitlab.com/yawning/utls for TLS.
|
|
//
|
|
// The id is the address of something like utls.HelloFirefox_55.
|
|
//
|
|
// The handshaker guarantees:
|
|
//
|
|
// 1. logging
|
|
//
|
|
// 2. error wrapping
|
|
//
|
|
// Passing a nil `id` will make this function panic.
|
|
func NewTLSHandshakerUTLS(logger model.DebugLogger, id *utls.ClientHelloID) model.TLSHandshaker {
|
|
return newTLSHandshaker(&tlsHandshakerConfigurable{
|
|
NewConn: newConnUTLS(id),
|
|
}, logger)
|
|
}
|
|
|
|
// utlsConn implements TLSConn and uses a utls UConn as its underlying connection
|
|
type utlsConn struct {
|
|
*utls.UConn
|
|
testableHandshake func() error
|
|
}
|
|
|
|
// Ensures that a utlsConn implements the TLSConn interface.
|
|
var _ TLSConn = &utlsConn{}
|
|
|
|
// newConnUTLS returns a NewConn function for creating utlsConn instances.
|
|
func newConnUTLS(clientHello *utls.ClientHelloID) func(conn net.Conn, config *tls.Config) (TLSConn, error) {
|
|
return func(conn net.Conn, config *tls.Config) (TLSConn, error) {
|
|
return newConnUTLSWithHelloID(conn, config, clientHello)
|
|
}
|
|
}
|
|
|
|
// errUTLSIncompatibleStdlibConfig indicates that the stdlib config you passed to
|
|
// newConnUTLSWithHelloID contains some fields we don't support.
|
|
var errUTLSIncompatibleStdlibConfig = errors.New("utls: incompatible stdlib config")
|
|
|
|
// newConnUTLSWithHelloID creates a new connection with the given client hello ID.
|
|
func newConnUTLSWithHelloID(conn net.Conn, config *tls.Config, cid *utls.ClientHelloID) (TLSConn, error) {
|
|
supportedFields := map[string]bool{
|
|
"DynamicRecordSizingDisabled": true,
|
|
"InsecureSkipVerify": true,
|
|
"NextProtos": true,
|
|
"RootCAs": true,
|
|
"ServerName": true,
|
|
}
|
|
value := reflect.ValueOf(config).Elem()
|
|
kind := value.Type()
|
|
for idx := 0; idx < value.NumField(); idx++ {
|
|
field := value.Field(idx)
|
|
if field.IsZero() {
|
|
continue
|
|
}
|
|
fieldKind := kind.Field(idx)
|
|
if supportedFields[fieldKind.Name] {
|
|
continue
|
|
}
|
|
err := fmt.Errorf("%w: field %s is nonzero", errUTLSIncompatibleStdlibConfig, fieldKind.Name)
|
|
return nil, err
|
|
}
|
|
uConfig := &utls.Config{
|
|
DynamicRecordSizingDisabled: config.DynamicRecordSizingDisabled,
|
|
InsecureSkipVerify: config.InsecureSkipVerify,
|
|
RootCAs: config.RootCAs,
|
|
NextProtos: config.NextProtos,
|
|
ServerName: config.ServerName,
|
|
}
|
|
tlsConn := utls.UClient(conn, uConfig, *cid)
|
|
return &utlsConn{UConn: tlsConn}, nil
|
|
}
|
|
|
|
// ErrUTLSHandshakePanic indicates that there was panic handshaking
|
|
// when we were using the yawning/utls library for parroting.
|
|
// See https://github.com/ooni/probe/issues/1770 for more information.
|
|
var ErrUTLSHandshakePanic = errors.New("utls: handshake panic")
|
|
|
|
func (c *utlsConn) HandshakeContext(ctx context.Context) (err error) {
|
|
errch := make(chan error, 1)
|
|
go func() {
|
|
defer func() {
|
|
// See https://github.com/ooni/probe/issues/1770
|
|
if recover() != nil {
|
|
errch <- ErrUTLSHandshakePanic
|
|
}
|
|
}()
|
|
errch <- c.handshakefn()()
|
|
}()
|
|
select {
|
|
case err = <-errch:
|
|
case <-ctx.Done():
|
|
err = ctx.Err()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (c *utlsConn) handshakefn() func() error {
|
|
if c.testableHandshake != nil {
|
|
return c.testableHandshake
|
|
}
|
|
return c.UConn.Handshake
|
|
}
|
|
|
|
func (c *utlsConn) ConnectionState() tls.ConnectionState {
|
|
uState := c.Conn.ConnectionState()
|
|
return tls.ConnectionState{
|
|
Version: uState.Version,
|
|
HandshakeComplete: uState.HandshakeComplete,
|
|
DidResume: uState.DidResume,
|
|
CipherSuite: uState.CipherSuite,
|
|
NegotiatedProtocol: uState.NegotiatedProtocol,
|
|
NegotiatedProtocolIsMutual: uState.NegotiatedProtocolIsMutual,
|
|
ServerName: uState.ServerName,
|
|
PeerCertificates: uState.PeerCertificates,
|
|
VerifiedChains: uState.VerifiedChains,
|
|
SignedCertificateTimestamps: uState.SignedCertificateTimestamps,
|
|
OCSPResponse: uState.OCSPResponse,
|
|
TLSUnique: uState.TLSUnique,
|
|
}
|
|
}
|