0
0
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:
RPRX 2023-02-03 02:25:10 +08:00 committed by yuhan6665
parent da6c695a34
commit 870716f6af
15 changed files with 247 additions and 168 deletions

View File

@ -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
View 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
}

View File

@ -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

View File

@ -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
View File

@ -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
}

View File

@ -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
View File

@ -1,6 +1,6 @@
module github.com/xtls/reality
go 1.19
go 1.20
require (
github.com/pires/go-proxyproto v0.6.2

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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")
}

View File

@ -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))

View File

@ -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
View File

@ -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
View File

@ -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