mirror of
https://github.com/XTLS/REALITY.git
synced 2025-08-22 06:28:35 +00:00
Sync upstream Go 1.20
Excluding `boring` or test files
This commit is contained in:
parent
da6c695a34
commit
870716f6af
@ -2,7 +2,7 @@
|
||||
|
||||
### THE NEXT FUTURE
|
||||
|
||||
Server side implementation of REALITY protocol, a fork of package tls in Go 1.19.5.
|
||||
Server side implementation of REALITY protocol, a fork of package tls in Go 1.20.
|
||||
For client side, please follow https://github.com/XTLS/Xray-core/blob/main/transport/internet/reality/reality.go.
|
||||
|
||||
TODO List: TODO
|
||||
|
95
cache.go
Normal file
95
cache.go
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright 2022 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-Go file.
|
||||
|
||||
package reality
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type cacheEntry struct {
|
||||
refs atomic.Int64
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
// certCache implements an intern table for reference counted x509.Certificates,
|
||||
// implemented in a similar fashion to BoringSSL's CRYPTO_BUFFER_POOL. This
|
||||
// allows for a single x509.Certificate to be kept in memory and referenced from
|
||||
// multiple Conns. Returned references should not be mutated by callers. Certificates
|
||||
// are still safe to use after they are removed from the cache.
|
||||
//
|
||||
// Certificates are returned wrapped in a activeCert struct that should be held by
|
||||
// the caller. When references to the activeCert are freed, the number of references
|
||||
// to the certificate in the cache is decremented. Once the number of references
|
||||
// reaches zero, the entry is evicted from the cache.
|
||||
//
|
||||
// The main difference between this implementation and CRYPTO_BUFFER_POOL is that
|
||||
// CRYPTO_BUFFER_POOL is a more generic structure which supports blobs of data,
|
||||
// rather than specific structures. Since we only care about x509.Certificates,
|
||||
// certCache is implemented as a specific cache, rather than a generic one.
|
||||
//
|
||||
// See https://boringssl.googlesource.com/boringssl/+/master/include/openssl/pool.h
|
||||
// and https://boringssl.googlesource.com/boringssl/+/master/crypto/pool/pool.c
|
||||
// for the BoringSSL reference.
|
||||
type certCache struct {
|
||||
sync.Map
|
||||
}
|
||||
|
||||
var clientCertCache = new(certCache)
|
||||
|
||||
// activeCert is a handle to a certificate held in the cache. Once there are
|
||||
// no alive activeCerts for a given certificate, the certificate is removed
|
||||
// from the cache by a finalizer.
|
||||
type activeCert struct {
|
||||
cert *x509.Certificate
|
||||
}
|
||||
|
||||
// active increments the number of references to the entry, wraps the
|
||||
// certificate in the entry in a activeCert, and sets the finalizer.
|
||||
//
|
||||
// Note that there is a race between active and the finalizer set on the
|
||||
// returned activeCert, triggered if active is called after the ref count is
|
||||
// decremented such that refs may be > 0 when evict is called. We consider this
|
||||
// safe, since the caller holding an activeCert for an entry that is no longer
|
||||
// in the cache is fine, with the only side effect being the memory overhead of
|
||||
// there being more than one distinct reference to a certificate alive at once.
|
||||
func (cc *certCache) active(e *cacheEntry) *activeCert {
|
||||
e.refs.Add(1)
|
||||
a := &activeCert{e.cert}
|
||||
runtime.SetFinalizer(a, func(_ *activeCert) {
|
||||
if e.refs.Add(-1) == 0 {
|
||||
cc.evict(e)
|
||||
}
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// evict removes a cacheEntry from the cache.
|
||||
func (cc *certCache) evict(e *cacheEntry) {
|
||||
cc.Delete(string(e.cert.Raw))
|
||||
}
|
||||
|
||||
// newCert returns a x509.Certificate parsed from der. If there is already a copy
|
||||
// of the certificate in the cache, a reference to the existing certificate will
|
||||
// be returned. Otherwise, a fresh certificate will be added to the cache, and
|
||||
// the reference returned. The returned reference should not be mutated.
|
||||
func (cc *certCache) newCert(der []byte) (*activeCert, error) {
|
||||
if entry, ok := cc.Load(string(der)); ok {
|
||||
return cc.active(entry.(*cacheEntry)), nil
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(der)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entry := &cacheEntry{cert: cert}
|
||||
if entry, loaded := cc.LoadOrStore(string(der), entry); loaded {
|
||||
return cc.active(entry.(*cacheEntry)), nil
|
||||
}
|
||||
return cc.active(entry), nil
|
||||
}
|
@ -466,7 +466,7 @@ func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([
|
||||
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// xoredNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
|
||||
// before each call.
|
||||
type xorNonceAEAD struct {
|
||||
nonceMask [aeadNonceLength]byte
|
||||
|
29
common.go
29
common.go
@ -246,6 +246,8 @@ type ConnectionState struct {
|
||||
// On the client side, it can't be empty. On the server side, it can be
|
||||
// empty if Config.ClientAuth is not RequireAnyClientCert or
|
||||
// RequireAndVerifyClientCert.
|
||||
//
|
||||
// PeerCertificates and its contents should not be modified.
|
||||
PeerCertificates []*x509.Certificate
|
||||
|
||||
// VerifiedChains is a list of one or more chains where the first element is
|
||||
@ -255,6 +257,8 @@ type ConnectionState struct {
|
||||
// On the client side, it's set if Config.InsecureSkipVerify is false. On
|
||||
// the server side, it's set if Config.ClientAuth is VerifyClientCertIfGiven
|
||||
// (and the peer provided a certificate) or RequireAndVerifyClientCert.
|
||||
//
|
||||
// VerifiedChains and its contents should not be modified.
|
||||
VerifiedChains [][]*x509.Certificate
|
||||
|
||||
// SignedCertificateTimestamps is a list of SCTs provided by the peer
|
||||
@ -568,6 +572,8 @@ type Config struct {
|
||||
// If GetCertificate is nil or returns nil, then the certificate is
|
||||
// retrieved from NameToCertificate. If NameToCertificate is nil, the
|
||||
// best element of Certificates will be used.
|
||||
//
|
||||
// Once a Certificate is returned it should not be modified.
|
||||
GetCertificate func(*ClientHelloInfo) (*Certificate, error)
|
||||
|
||||
// GetClientCertificate, if not nil, is called when a server requests a
|
||||
@ -583,6 +589,8 @@ type Config struct {
|
||||
//
|
||||
// GetClientCertificate may be called multiple times for the same
|
||||
// connection if renegotiation occurs or if TLS 1.3 is in use.
|
||||
//
|
||||
// Once a Certificate is returned it should not be modified.
|
||||
GetClientCertificate func(*CertificateRequestInfo) (*Certificate, error)
|
||||
|
||||
// GetConfigForClient, if not nil, is called after a ClientHello is
|
||||
@ -611,6 +619,8 @@ type Config struct {
|
||||
// setting InsecureSkipVerify, or (for a server) when ClientAuth is
|
||||
// RequestClientCert or RequireAnyClientCert, then this callback will
|
||||
// be considered but the verifiedChains argument will always be nil.
|
||||
//
|
||||
// verifiedChains and its contents should not be modified.
|
||||
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error
|
||||
|
||||
// VerifyConnection, if not nil, is called after normal certificate
|
||||
@ -739,7 +749,7 @@ type Config struct {
|
||||
|
||||
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
|
||||
mutex sync.RWMutex
|
||||
// sessionTicketKeys contains zero or more ticket keys. If set, it means the
|
||||
// sessionTicketKeys contains zero or more ticket keys. If set, it means
|
||||
// the keys were set with SessionTicketKey or SetSessionTicketKeys. The
|
||||
// first key is used for new tickets and any subsequent keys can be used to
|
||||
// decrypt old tickets. The slice contents are not protected by the mutex
|
||||
@ -1363,7 +1373,7 @@ func (c *Config) writeKeyLog(label string, clientRandom, secret []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
logLine := []byte(fmt.Sprintf("%s %x %x\n", label, clientRandom, secret))
|
||||
logLine := fmt.Appendf(nil, "%s %x %x\n", label, clientRandom, secret)
|
||||
|
||||
writerMutex.Lock()
|
||||
_, err := c.KeyLogWriter.Write(logLine)
|
||||
@ -1508,3 +1518,18 @@ func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlg
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CertificateVerificationError is returned when certificate verification fails during the handshake.
|
||||
type CertificateVerificationError struct {
|
||||
// UnverifiedCertificates and its contents should not be modified.
|
||||
UnverifiedCertificates []*x509.Certificate
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *CertificateVerificationError) Error() string {
|
||||
return fmt.Sprintf("tls: failed to verify certificate: %s", e.Err)
|
||||
}
|
||||
|
||||
func (e *CertificateVerificationError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
53
conn.go
53
conn.go
@ -35,11 +35,10 @@ type Conn struct {
|
||||
isClient bool
|
||||
handshakeFn func(context.Context) error // (*Conn).clientHandshake or serverHandshake
|
||||
|
||||
// handshakeStatus is 1 if the connection is currently transferring
|
||||
// isHandshakeComplete is true if the connection is currently transferring
|
||||
// application data (i.e. is not currently processing a handshake).
|
||||
// handshakeStatus == 1 implies handshakeErr == nil.
|
||||
// This field is only to be accessed with sync/atomic.
|
||||
handshakeStatus uint32
|
||||
// isHandshakeComplete is true implies handshakeErr == nil.
|
||||
isHandshakeComplete atomic.Bool
|
||||
// constant after handshake; protected by handshakeMutex
|
||||
handshakeMutex sync.Mutex
|
||||
handshakeErr error // error resulting from handshake
|
||||
@ -55,6 +54,9 @@ type Conn struct {
|
||||
ocspResponse []byte // stapled OCSP response
|
||||
scts [][]byte // signed certificate timestamps from server
|
||||
peerCertificates []*x509.Certificate
|
||||
// activeCertHandles contains the cache handles to certificates in
|
||||
// peerCertificates that are used to track active references.
|
||||
activeCertHandles []*activeCert
|
||||
// verifiedChains contains the certificate chains that we built, as
|
||||
// opposed to the ones presented by the server.
|
||||
verifiedChains [][]*x509.Certificate
|
||||
@ -115,10 +117,9 @@ type Conn struct {
|
||||
// handshake, nor deliver application data. Protected by in.Mutex.
|
||||
retryCount int
|
||||
|
||||
// activeCall is an atomic int32; the low bit is whether Close has
|
||||
// been called. the rest of the bits are the number of goroutines
|
||||
// in Conn.Write.
|
||||
activeCall int32
|
||||
// activeCall indicates whether Close has been call in the low bit.
|
||||
// the rest of the bits are the number of goroutines in Conn.Write.
|
||||
activeCall atomic.Int32
|
||||
|
||||
tmp [16]byte
|
||||
}
|
||||
@ -639,7 +640,7 @@ func (c *Conn) readRecordOrCCS(expectChangeCipherSpec bool) error {
|
||||
if c.in.err != nil {
|
||||
return c.in.err
|
||||
}
|
||||
handshakeComplete := c.handshakeComplete()
|
||||
handshakeComplete := c.isHandshakeComplete.Load()
|
||||
|
||||
// This function modifies c.rawInput, which owns the c.input memory.
|
||||
if c.input.Len() != 0 {
|
||||
@ -1154,15 +1155,15 @@ var (
|
||||
func (c *Conn) Write(b []byte) (int, error) {
|
||||
// interlock with Close below
|
||||
for {
|
||||
x := atomic.LoadInt32(&c.activeCall)
|
||||
x := c.activeCall.Load()
|
||||
if x&1 != 0 {
|
||||
return 0, net.ErrClosed
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(&c.activeCall, x, x+2) {
|
||||
if c.activeCall.CompareAndSwap(x, x+2) {
|
||||
break
|
||||
}
|
||||
}
|
||||
defer atomic.AddInt32(&c.activeCall, -2)
|
||||
defer c.activeCall.Add(-2)
|
||||
|
||||
if err := c.Handshake(); err != nil {
|
||||
return 0, err
|
||||
@ -1175,7 +1176,7 @@ func (c *Conn) Write(b []byte) (int, error) {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !c.handshakeComplete() {
|
||||
if !c.isHandshakeComplete.Load() {
|
||||
return 0, alertInternalError
|
||||
}
|
||||
|
||||
@ -1245,7 +1246,7 @@ func (c *Conn) handleRenegotiation() error {
|
||||
c.handshakeMutex.Lock()
|
||||
defer c.handshakeMutex.Unlock()
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 0)
|
||||
c.isHandshakeComplete.Store(false)
|
||||
if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
|
||||
c.handshakes++
|
||||
}
|
||||
@ -1363,11 +1364,11 @@ func (c *Conn) Close() error {
|
||||
// Interlock with Conn.Write above.
|
||||
var x int32
|
||||
for {
|
||||
x = atomic.LoadInt32(&c.activeCall)
|
||||
x = c.activeCall.Load()
|
||||
if x&1 != 0 {
|
||||
return net.ErrClosed
|
||||
}
|
||||
if atomic.CompareAndSwapInt32(&c.activeCall, x, x|1) {
|
||||
if c.activeCall.CompareAndSwap(x, x|1) {
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -1382,7 +1383,7 @@ func (c *Conn) Close() error {
|
||||
}
|
||||
|
||||
var alertErr error
|
||||
if c.handshakeComplete() {
|
||||
if c.isHandshakeComplete.Load() {
|
||||
if err := c.closeNotify(); err != nil {
|
||||
alertErr = fmt.Errorf("tls: failed to send closeNotify alert (but connection was closed anyway): %w", err)
|
||||
}
|
||||
@ -1400,7 +1401,7 @@ var errEarlyCloseWrite = errors.New("tls: CloseWrite called before handshake com
|
||||
// called once the handshake has completed and does not call CloseWrite on the
|
||||
// underlying connection. Most callers should just use Close.
|
||||
func (c *Conn) CloseWrite() error {
|
||||
if !c.handshakeComplete() {
|
||||
if !c.isHandshakeComplete.Load() {
|
||||
return errEarlyCloseWrite
|
||||
}
|
||||
|
||||
@ -1454,7 +1455,7 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||
// Fast sync/atomic-based exit if there is no handshake in flight and the
|
||||
// last one succeeded without an error. Avoids the expensive context setup
|
||||
// and mutex for most Read and Write calls.
|
||||
if c.handshakeComplete() {
|
||||
if c.isHandshakeComplete.Load() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1497,7 +1498,7 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||
if err := c.handshakeErr; err != nil {
|
||||
return err
|
||||
}
|
||||
if c.handshakeComplete() {
|
||||
if c.isHandshakeComplete.Load() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1513,10 +1514,10 @@ func (c *Conn) handshakeContext(ctx context.Context) (ret error) {
|
||||
c.flush()
|
||||
}
|
||||
|
||||
if c.handshakeErr == nil && !c.handshakeComplete() {
|
||||
if c.handshakeErr == nil && !c.isHandshakeComplete.Load() {
|
||||
c.handshakeErr = errors.New("tls: internal error: handshake should have had a result")
|
||||
}
|
||||
if c.handshakeErr != nil && c.handshakeComplete() {
|
||||
if c.handshakeErr != nil && c.isHandshakeComplete.Load() {
|
||||
panic("tls: internal error: handshake returned an error but is marked successful")
|
||||
}
|
||||
|
||||
@ -1532,7 +1533,7 @@ func (c *Conn) ConnectionState() ConnectionState {
|
||||
|
||||
func (c *Conn) connectionStateLocked() ConnectionState {
|
||||
var state ConnectionState
|
||||
state.HandshakeComplete = c.handshakeComplete()
|
||||
state.HandshakeComplete = c.isHandshakeComplete.Load()
|
||||
state.Version = c.vers
|
||||
state.NegotiatedProtocol = c.clientProtocol
|
||||
state.DidResume = c.didResume
|
||||
@ -1576,7 +1577,7 @@ func (c *Conn) VerifyHostname(host string) error {
|
||||
if !c.isClient {
|
||||
return errors.New("tls: VerifyHostname called on TLS server connection")
|
||||
}
|
||||
if !c.handshakeComplete() {
|
||||
if !c.isHandshakeComplete.Load() {
|
||||
return errors.New("tls: handshake has not yet been performed")
|
||||
}
|
||||
if len(c.verifiedChains) == 0 {
|
||||
@ -1584,7 +1585,3 @@ func (c *Conn) VerifyHostname(host string) error {
|
||||
}
|
||||
return c.peerCertificates[0].VerifyHostname(host)
|
||||
}
|
||||
|
||||
func (c *Conn) handshakeComplete() bool {
|
||||
return atomic.LoadUint32(&c.handshakeStatus) == 1
|
||||
}
|
||||
|
@ -156,7 +156,6 @@ func main() {
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
return
|
||||
}
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
|
2
go.mod
2
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/xtls/reality
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/pires/go-proxyproto v0.6.2
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdh"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
@ -19,7 +20,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -36,7 +36,7 @@ type clientHandshakeState struct {
|
||||
|
||||
var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme
|
||||
|
||||
func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
|
||||
func (c *Conn) makeClientHello() (*clientHelloMsg, *ecdh.PrivateKey, error) {
|
||||
config := c.config
|
||||
if len(config.ServerName) == 0 && !config.InsecureSkipVerify {
|
||||
return nil, nil, errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config")
|
||||
@ -125,7 +125,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
|
||||
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
|
||||
}
|
||||
|
||||
var params ecdheParameters
|
||||
var key *ecdh.PrivateKey
|
||||
if hello.supportedVersions[0] == VersionTLS13 {
|
||||
if hasAESGCMHardwareSupport {
|
||||
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
|
||||
@ -134,17 +134,17 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, ecdheParameters, error) {
|
||||
}
|
||||
|
||||
curveID := config.curvePreferences()[0]
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
if _, ok := curveForCurveID(curveID); !ok {
|
||||
return nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err = generateECDHEParameters(config.rand(), curveID)
|
||||
key, err = generateECDHEKey(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: params.PublicKey()}}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
||||
}
|
||||
|
||||
return hello, params, nil
|
||||
return hello, key, nil
|
||||
}
|
||||
|
||||
func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||
@ -156,7 +156,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||
// need to be reset.
|
||||
c.didResume = false
|
||||
|
||||
hello, ecdheParams, err := c.makeClientHello()
|
||||
hello, ecdheKey, err := c.makeClientHello()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -214,7 +214,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) {
|
||||
ctx: ctx,
|
||||
serverHello: serverHello,
|
||||
hello: hello,
|
||||
ecdheParams: ecdheParams,
|
||||
ecdheKey: ecdheKey,
|
||||
session: session,
|
||||
earlySecret: earlySecret,
|
||||
binderKey: binderKey,
|
||||
@ -455,7 +455,7 @@ func (hs *clientHandshakeState) handshake() error {
|
||||
}
|
||||
|
||||
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.hello.random, hs.serverHello.random)
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -629,7 +629,7 @@ func (hs *clientHandshakeState) doFullHandshake() error {
|
||||
}
|
||||
}
|
||||
|
||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
|
||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
|
||||
signOpts := crypto.SignerOpts(sigHash)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
||||
@ -849,14 +849,16 @@ func (hs *clientHandshakeState) sendFinished(out []byte) error {
|
||||
// verifyServerCertificate parses and verifies the provided chain, setting
|
||||
// c.verifiedChains and c.peerCertificates or sending the appropriate alert.
|
||||
func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
|
||||
activeHandles := make([]*activeCert, len(certificates))
|
||||
certs := make([]*x509.Certificate, len(certificates))
|
||||
for i, asn1Data := range certificates {
|
||||
cert, err := x509.ParseCertificate(asn1Data)
|
||||
cert, err := clientCertCache.newCert(asn1Data)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: failed to parse certificate from server: " + err.Error())
|
||||
}
|
||||
certs[i] = cert
|
||||
activeHandles[i] = cert
|
||||
certs[i] = cert.cert
|
||||
}
|
||||
|
||||
if !c.config.InsecureSkipVerify {
|
||||
@ -874,7 +876,7 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
|
||||
c.verifiedChains, err = certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
@ -886,6 +888,7 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error {
|
||||
return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
|
||||
}
|
||||
|
||||
c.activeCertHandles = activeHandles
|
||||
c.peerCertificates = certs
|
||||
|
||||
if c.config.VerifyPeerCertificate != nil {
|
||||
|
@ -8,11 +8,11 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdh"
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"errors"
|
||||
"hash"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -21,7 +21,7 @@ type clientHandshakeStateTLS13 struct {
|
||||
ctx context.Context
|
||||
serverHello *serverHelloMsg
|
||||
hello *clientHelloMsg
|
||||
ecdheParams ecdheParameters
|
||||
ecdheKey *ecdh.PrivateKey
|
||||
|
||||
session *ClientSessionState
|
||||
earlySecret []byte
|
||||
@ -36,7 +36,7 @@ type clientHandshakeStateTLS13 struct {
|
||||
trafficSecret []byte // client_application_traffic_secret_0
|
||||
}
|
||||
|
||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheParams, and,
|
||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.ecdheKey, and,
|
||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
@ -53,7 +53,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
}
|
||||
|
||||
// Consistency check on the presence of a keyShare and its parameters.
|
||||
if hs.ecdheParams == nil || len(hs.hello.keyShares) != 1 {
|
||||
if hs.ecdheKey == nil || len(hs.hello.keyShares) != 1 {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -222,21 +222,21 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
if hs.ecdheParams.CurveID() == curveID {
|
||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); sentID == curveID {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||
}
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
if _, ok := curveForCurveID(curveID); !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(c.config.rand(), curveID)
|
||||
key, err := generateECDHEKey(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.ecdheKey = key
|
||||
hs.hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
||||
}
|
||||
|
||||
hs.hello.raw = nil
|
||||
@ -310,7 +310,7 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not send a key share")
|
||||
}
|
||||
if hs.serverHello.serverShare.group != hs.ecdheParams.CurveID() {
|
||||
if sentID, _ := curveIDForCurve(hs.ecdheKey.Curve()); hs.serverHello.serverShare.group != sentID {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
@ -348,8 +348,13 @@ func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
c := hs.c
|
||||
|
||||
sharedKey := hs.ecdheParams.SharedKey(hs.serverHello.serverShare.data)
|
||||
if sharedKey == nil {
|
||||
peerKey, err := hs.ecdheKey.Curve().NewPublicKey(hs.serverHello.serverShare.data)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
sharedKey, err := hs.ecdheKey.ECDH(peerKey)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
@ -368,7 +373,7 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
serverHandshakeTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, serverSecret)
|
||||
|
||||
err := c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
||||
err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
|
@ -16,7 +16,6 @@ import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -122,7 +121,7 @@ func (hs *serverHandshakeState) handshake() error {
|
||||
}
|
||||
|
||||
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -668,7 +667,7 @@ func (hs *serverHandshakeState) doFullHandshake() error {
|
||||
}
|
||||
}
|
||||
|
||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash, hs.masterSecret)
|
||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
|
||||
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
||||
@ -832,7 +831,7 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error {
|
||||
chains, err := certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: failed to verify client certificate: " + err.Error())
|
||||
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
|
||||
}
|
||||
|
||||
c.verifiedChains = chains
|
||||
|
@ -140,7 +140,7 @@ func (hs *serverHandshakeStateTLS13) handshake() error {
|
||||
return err
|
||||
}
|
||||
|
||||
atomic.StoreUint32(&c.handshakeStatus, 1)
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -264,18 +264,23 @@ GroupSelection:
|
||||
clientKeyShare = &hs.clientHello.keyShares[0]
|
||||
}
|
||||
|
||||
if _, ok := curveForCurveID(selectedGroup); selectedGroup != X25519 && !ok {
|
||||
if _, ok := curveForCurveID(selectedGroup); !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
params, err := generateECDHEParameters(c.config.rand(), selectedGroup)
|
||||
key, err := generateECDHEKey(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 {
|
||||
hs.hello.serverShare = keyShare{group: selectedGroup, data: key.PublicKey().Bytes()}
|
||||
peerKey, err := key.Curve().NewPublicKey(clientKeyShare.data)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid client key share")
|
||||
}
|
||||
hs.sharedKey, err = key.ECDH(peerKey)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid client key share")
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ package reality
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdh"
|
||||
"crypto/md5"
|
||||
"crypto/rsa"
|
||||
"crypto/sha1"
|
||||
@ -157,7 +158,7 @@ func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint1
|
||||
type ecdheKeyAgreement struct {
|
||||
version uint16
|
||||
isRSA bool
|
||||
params ecdheParameters
|
||||
key *ecdh.PrivateKey
|
||||
|
||||
// ckx and preMasterSecret are generated in processServerKeyExchange
|
||||
// and returned in generateClientKeyExchange.
|
||||
@ -177,18 +178,18 @@ func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Cer
|
||||
if curveID == 0 {
|
||||
return nil, errors.New("tls: no supported elliptic curves offered")
|
||||
}
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
if _, ok := curveForCurveID(curveID); !ok {
|
||||
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
|
||||
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||
key, err := generateECDHEKey(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ka.params = params
|
||||
ka.key = key
|
||||
|
||||
// See RFC 4492, Section 5.4.
|
||||
ecdhePublic := params.PublicKey()
|
||||
ecdhePublic := key.PublicKey().Bytes()
|
||||
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
|
||||
serverECDHEParams[0] = 3 // named curve
|
||||
serverECDHEParams[1] = byte(curveID >> 8)
|
||||
@ -259,8 +260,12 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
|
||||
preMasterSecret := ka.params.SharedKey(ckx.ciphertext[1:])
|
||||
if preMasterSecret == nil {
|
||||
peerKey, err := ka.key.Curve().NewPublicKey(ckx.ciphertext[1:])
|
||||
if err != nil {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
preMasterSecret, err := ka.key.ECDH(peerKey)
|
||||
if err != nil {
|
||||
return nil, errClientKeyExchange
|
||||
}
|
||||
|
||||
@ -288,22 +293,26 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||
return errServerKeyExchange
|
||||
}
|
||||
|
||||
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
|
||||
if _, ok := curveForCurveID(curveID); !ok {
|
||||
return errors.New("tls: server selected unsupported curve")
|
||||
}
|
||||
|
||||
params, err := generateECDHEParameters(config.rand(), curveID)
|
||||
key, err := generateECDHEKey(config.rand(), curveID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ka.params = params
|
||||
ka.key = key
|
||||
|
||||
ka.preMasterSecret = params.SharedKey(publicKey)
|
||||
if ka.preMasterSecret == nil {
|
||||
peerKey, err := key.Curve().NewPublicKey(publicKey)
|
||||
if err != nil {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
ka.preMasterSecret, err = key.ECDH(peerKey)
|
||||
if err != nil {
|
||||
return errServerKeyExchange
|
||||
}
|
||||
|
||||
ourPublicKey := params.PublicKey()
|
||||
ourPublicKey := key.PublicKey().Bytes()
|
||||
ka.ckx = new(clientKeyExchangeMsg)
|
||||
ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
|
||||
ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
|
||||
|
102
key_schedule.go
102
key_schedule.go
@ -5,15 +5,13 @@
|
||||
package reality
|
||||
|
||||
import (
|
||||
"crypto/elliptic"
|
||||
"crypto/ecdh"
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
@ -101,99 +99,43 @@ func (c *cipherSuiteTLS13) exportKeyingMaterial(masterSecret []byte, transcript
|
||||
}
|
||||
}
|
||||
|
||||
// ecdheParameters implements Diffie-Hellman with either NIST curves or X25519,
|
||||
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
|
||||
// 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 {
|
||||
privateKey := make([]byte, curve25519.ScalarSize)
|
||||
if _, err := io.ReadFull(rand, privateKey); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicKey, err := curve25519.X25519(privateKey, curve25519.Basepoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &x25519Parameters{privateKey: privateKey, publicKey: publicKey}, nil
|
||||
}
|
||||
|
||||
func generateECDHEKey(rand io.Reader, curveID CurveID) (*ecdh.PrivateKey, error) {
|
||||
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
|
||||
return curve.GenerateKey(rand)
|
||||
}
|
||||
|
||||
func curveForCurveID(id CurveID) (elliptic.Curve, bool) {
|
||||
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
|
||||
switch id {
|
||||
case X25519:
|
||||
return ecdh.X25519(), true
|
||||
case CurveP256:
|
||||
return elliptic.P256(), true
|
||||
return ecdh.P256(), true
|
||||
case CurveP384:
|
||||
return elliptic.P384(), true
|
||||
return ecdh.P384(), true
|
||||
case CurveP521:
|
||||
return elliptic.P521(), true
|
||||
return ecdh.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
|
||||
func curveIDForCurve(curve ecdh.Curve) (CurveID, bool) {
|
||||
switch curve {
|
||||
case ecdh.X25519():
|
||||
return X25519, true
|
||||
case ecdh.P256():
|
||||
return CurveP256, true
|
||||
case ecdh.P384():
|
||||
return CurveP384, true
|
||||
case ecdh.P521():
|
||||
return CurveP521, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
|
||||
xShared, _ := curve.ScalarMult(x, y, p.privateKey)
|
||||
sharedKey := make([]byte, (curve.Params().BitSize+7)/8)
|
||||
return xShared.FillBytes(sharedKey)
|
||||
}
|
||||
|
||||
type x25519Parameters struct {
|
||||
privateKey []byte
|
||||
publicKey []byte
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) CurveID() CurveID {
|
||||
return X25519
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) PublicKey() []byte {
|
||||
return p.publicKey[:]
|
||||
}
|
||||
|
||||
func (p *x25519Parameters) SharedKey(peerPublicKey []byte) []byte {
|
||||
sharedKey, err := curve25519.X25519(p.privateKey, peerPublicKey)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return sharedKey
|
||||
}
|
||||
|
2
prf.go
2
prf.go
@ -215,7 +215,7 @@ func (h finishedHash) serverSum(masterSecret []byte) []byte {
|
||||
|
||||
// hashForClientCertificate returns the handshake messages so far, pre-hashed if
|
||||
// necessary, suitable for signing by a TLS client certificate.
|
||||
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash, masterSecret []byte) []byte {
|
||||
func (h finishedHash) hashForClientCertificate(sigType uint8, hashAlg crypto.Hash) []byte {
|
||||
if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
|
||||
panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
|
||||
}
|
||||
|
4
tls.go
4
tls.go
@ -2,8 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE-Go file.
|
||||
|
||||
// Server side implementation of REALITY protocol, a fork of package tls in Go 1.19.5.
|
||||
// For client side, please follow https://github.com/XTLS/Xray-core.
|
||||
// Server side implementation of REALITY protocol, a fork of package tls in Go 1.20.
|
||||
// For client side, please follow https://github.com/XTLS/Xray-core/blob/main/transport/internet/reality/reality.go.
|
||||
package reality
|
||||
|
||||
// BUG(agl): The crypto/tls package only implements some countermeasures
|
||||
|
Loading…
Reference in New Issue
Block a user