refactor+feat: Custom Client Handshake + Implement ALPS extension (#142)

* refactor: split `CompressCertExtension` changes

- Split most of changes for `CompressCertExtension` made to `crypto/tls` files out and moved them to `u_` files.
- Edited some `crypto/tls` files to achieve better programmability for uTLS.
- Minor styling fix.

* feat: implement ALPS Extension draft

- Made necessary modifications to existing types to support ALPS.
- Ported `ApplicationSettingsExtension` implementation from `ulixee/utls` by @blakebyrnes with some adaptation.

Co-Authored-By: Blake Byrnes <115056+blakebyrnes@users.noreply.github.com>

* feat: utlsFakeCustomExtension in ALPS

- Introducing `utlsFakeCustomExtension` to enable implementation for custom extensions to be exchanged via ALPS.
- currently it doesn't do anything.

Co-Authored-By: Blake Byrnes <115056+blakebyrnes@users.noreply.github.com>

* fix: magic number in `StatusRequestV2Extension`

- Fixed magic number `17` in `StatusRequestV2Extension` with pre-defined enum `extensionStatusRequestV2`.

Co-authored-by: Blake Byrnes <115056+blakebyrnes@users.noreply.github.com>
This commit is contained in:
Gaukas Wang 2022-11-17 14:04:29 -07:00 committed by GitHub
parent 1b3a9ad4c5
commit fb99df2a2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 375 additions and 140 deletions

View file

@ -6,7 +6,6 @@ package tls
import (
"bytes"
"compress/zlib"
"context"
"crypto"
"crypto/hmac"
@ -14,12 +13,8 @@ import (
"errors"
"fmt"
"hash"
"io"
"sync/atomic"
"time"
"github.com/andybalholm/brotli"
"github.com/klauspost/compress/zstd"
)
type clientHandshakeStateTLS13 struct {
@ -103,6 +98,11 @@ func (hs *clientHandshakeStateTLS13) handshake() error {
if err := hs.readServerFinished(); err != nil {
return err
}
// [UTLS SECTION START]
if err := hs.serverFinishedReceived(); err != nil {
return err
}
// [UTLS SECTION END]
if err := hs.sendClientCertificate(); err != nil {
return err
}
@ -477,6 +477,15 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error {
}
c.clientProtocol = encryptedExtensions.alpnProtocol
// [UTLS SECTION STARTS]
if hs.uconn != nil {
err = hs.utlsReadServerParameters(encryptedExtensions)
if err != nil {
c.sendAlert(alertUnsupportedExtension)
return err
}
}
// [UTLS SECTION ENDS]
return nil
}
@ -516,19 +525,15 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
}
// [UTLS SECTION BEGINS]
receivedCompressedCert := false
// Check to see if we advertised any compression algorithms
if hs.uconn != nil && len(hs.uconn.certCompressionAlgs) > 0 {
// Check to see if the message is a compressed certificate message, otherwise move on.
compressedCertMsg, ok := msg.(*compressedCertificateMsg)
if ok {
receivedCompressedCert = true
hs.transcript.Write(compressedCertMsg.marshal())
msg, err = hs.decompressCert(*compressedCertMsg)
if err != nil {
return fmt.Errorf("tls: failed to decompress certificate message: %w", err)
}
var skipWritingCertToTranscript bool = false
if hs.uconn != nil {
processedMsg, err := hs.utlsReadServerCertificate(msg)
if err != nil {
return err
}
if processedMsg != nil {
skipWritingCertToTranscript = true
msg = processedMsg // msg is now a processed-by-extension certificateMsg
}
}
// [UTLS SECTION ENDS]
@ -544,7 +549,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
}
// [UTLS SECTION BEGINS]
// Previously, this was simply 'hs.transcript.Write(certMsg.marshal())' (without the if).
if !receivedCompressedCert {
if !skipWritingCertToTranscript {
hs.transcript.Write(certMsg.marshal())
}
// [UTLS SECTION ENDS]
@ -570,7 +575,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
// See RFC 8446, Section 4.4.3.
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: certificate used with invalid signature algorithm")
return errors.New("tls: certificate used with invalid signature algorithm -- not implemented")
}
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
if err != nil {
@ -578,7 +583,7 @@ func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
}
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: certificate used with invalid signature algorithm")
return errors.New("tls: certificate used with invalid signature algorithm -- obsolete")
}
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
@ -729,80 +734,6 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
return nil
}
// [UTLS SECTION BEGINS]
func (hs *clientHandshakeStateTLS13) decompressCert(m compressedCertificateMsg) (*certificateMsgTLS13, error) {
var (
decompressed io.Reader
compressed = bytes.NewReader(m.compressedCertificateMessage)
c = hs.c
)
// Check to see if the peer responded with an algorithm we advertised.
supportedAlg := false
for _, alg := range hs.uconn.certCompressionAlgs {
if m.algorithm == uint16(alg) {
supportedAlg = true
}
}
if !supportedAlg {
c.sendAlert(alertBadCertificate)
return nil, fmt.Errorf("unadvertised algorithm (%d)", m.algorithm)
}
switch CertCompressionAlgo(m.algorithm) {
case CertCompressionBrotli:
decompressed = brotli.NewReader(compressed)
case CertCompressionZlib:
rc, err := zlib.NewReader(compressed)
if err != nil {
c.sendAlert(alertBadCertificate)
return nil, fmt.Errorf("failed to open zlib reader: %w", err)
}
defer rc.Close()
decompressed = rc
case CertCompressionZstd:
rc, err := zstd.NewReader(compressed)
if err != nil {
c.sendAlert(alertBadCertificate)
return nil, fmt.Errorf("failed to open zstd reader: %w", err)
}
defer rc.Close()
decompressed = rc
default:
c.sendAlert(alertBadCertificate)
return nil, fmt.Errorf("unsupported algorithm (%d)", m.algorithm)
}
rawMsg := make([]byte, m.uncompressedLength+4) // +4 for message type and uint24 length field
rawMsg[0] = typeCertificate
rawMsg[1] = uint8(m.uncompressedLength >> 16)
rawMsg[2] = uint8(m.uncompressedLength >> 8)
rawMsg[3] = uint8(m.uncompressedLength)
n, err := decompressed.Read(rawMsg[4:])
if err != nil {
c.sendAlert(alertBadCertificate)
return nil, err
}
if n < len(rawMsg)-4 {
// If, after decompression, the specified length does not match the actual length, the party
// receiving the invalid message MUST abort the connection with the "bad_certificate" alert.
// https://datatracker.ietf.org/doc/html/rfc8879#section-4
c.sendAlert(alertBadCertificate)
return nil, fmt.Errorf("decompressed len (%d) does not match specified len (%d)", n, m.uncompressedLength)
}
certMsg := new(certificateMsgTLS13)
if !certMsg.unmarshal(rawMsg) {
return nil, c.sendAlert(alertUnexpectedMessage)
}
return certMsg, nil
}
// [UTLS SECTION ENDS]
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
if !c.isClient {
c.sendAlert(alertUnexpectedMessage)