utls/u_tls_extensions.go
Gaukas Wang 8094658e76
new: Support TLS-PSK (TLS 1.3) (#231)
* uTLS: X25519Kyber768Draft00 hybrid post-quantum key agreement by cloudflare/go (#222)

* crypto/tls: Add hybrid post-quantum key agreement  (#13)

* import: client-side KEM from cloudflare/go

* import: server-side KEM from cloudflare/go

* fix: modify test to get rid of CFEvents.

Note: uTLS does not promise any server-side functionality, and this change is made to be able to conduct unit tests which requires both side to be able to handle KEM Curves.

Co-authored-by: Christopher Wood <caw@heapingbits.net>
Co-Authored-By: Bas Westerbaan <bas@westerbaan.name>

----

Based on:

* crypto/tls: Add hybrid post-quantum key agreement 

Adds X25519Kyber512Draft00, X25519Kyber768Draft00, and
P256Kyber768Draft00 hybrid post-quantum key agreements with temporary
group identifiers.

The hybrid post-quantum key exchanges uses plain X{25519,448} instead
of HPKE, which we assume will be more likely to be adopted. The order
is chosen to match CECPQ2.

Not enabled by default.

Adds CFEvents to detect `HelloRetryRequest`s and to signal which
key agreement was used.

Co-authored-by: Christopher Wood <caw@heapingbits.net>

 [bas, 1.20.1: also adds P256Kyber768Draft00]
 [pwu, 1.20.4: updated circl to v1.3.3, moved code to cfevent.go]

* crypto: add support for CIRCL signature schemes

* only partially port the commit from cloudflare/go. We would stick to the official x509 at the cost of incompatibility.

Co-Authored-By: Bas Westerbaan <bas@westerbaan.name>
Co-Authored-By: Christopher Patton <3453007+cjpatton@users.noreply.github.com>
Co-Authored-By: Peter Wu <peter@lekensteyn.nl>

* crypto/tls: add new X25519Kyber768Draft00 code point

Ported from cloudflare/go to support the upcoming new post-quantum keyshare.

----

* Point tls.X25519Kyber768Draft00 to the new 0x6399 identifier while the
  old 0xfe31 identifier is available as tls.X25519Kyber768Draft00Old.
* Make sure that the kem.PrivateKey can always be mapped to the CurveID
  that was linked to it. This is needed since we now have two ID
  aliasing to the same scheme, and clients need to be able to detect
  whether the key share presented by the server actually matches the key
  share that the client originally sent.
* Update tests, add the new identifier and remove unnecessary code.

Link: https://mailarchive.ietf.org/arch/msg/tls/HAWpNpgptl--UZNSYuvsjB-Pc2k/
Link: https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/02/
Co-Authored-By: Peter Wu <peter@lekensteyn.nl>
Co-Authored-By: Bas Westerbaan <bas@westerbaan.name>

---------

Co-authored-by: Bas Westerbaan <bas@westerbaan.name>
Co-authored-by: Christopher Patton <3453007+cjpatton@users.noreply.github.com>
Co-authored-by: Peter Wu <peter@lekensteyn.nl>

* new: enable PQ parrots (#225)

* Redesign KeySharesEcdheParameters into KeySharesParameters which supports multiple types of keys.

* Optimize program logic to prevent using unwanted keys

* new: more parrots and safety update (#227)

* new: PQ and other parrots

Add new preset parrots:
- HelloChrome_114_Padding_PSK_Shuf
- HelloChrome_115_PQ
- HelloChrome_115_PQ_PSK

* new: ShuffleChromeTLSExtensions

Implement a new function `ShuffleChromeTLSExtensions(exts []TLSExtension) []TLSExtension`.

* update: include psk parameter for parrot-related functions

Update following functions' prototype to accept an optional pskExtension (of type *FakePreSharedKeyExtension):
- `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID)` => `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`
- `UTLSIdToSpec(id ClientHelloID)` => `UTLSIdToSpec(id ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`

* new: pre-defined error from UTLSIdToSpec

Update UTLSIdToSpec to return more comprehensive errors by pre-defining them, allowing easier error comparing/unwrapping.

* new: UtlsPreSharedKeyExtension

In `u_pre_shared_key.go`, create `PreSharedKeyExtension` as an interface, with 3 implementations:
- `UtlsPreSharedKeyExtension` implements full support for `pre_shared_key` less resuming after seeing HRR.
- `FakePreSharedKeyExtension` uses CipherSuiteID, SessionSecret and Identities to calculate the corresponding binders and send them, without setting the internal states. Therefore if the server accepts the PSK and tries to resume, the connection fails.
- `HardcodedPreSharedKeyExtension` allows user to hardcode Identities and Binders to be sent in the extension without setting the internal states. Therefore if the server accepts the PSK and tries to resume, the connection fails.

TODO: Only one of FakePreSharedKeyExtension and HardcodedPreSharedKeyExtension should be kept, the other one should be just removed. We still need to learn more of the safety of hardcoding both Identities and Binders without recalculating the latter.

* update: PSK minor changes and example

* Updates PSK implementations for more comprehensible interfaces when applying preset/json/raw fingerprints.
* Revert FakePreSharedKeyExtension to the old implementation. Add binder size checking.
* Implement TLS-PSK example

New bug: setting `tls.Config.ClientSessionCache` will cause PSK to fail. Currently users must set only `tls.UtlsPreSharedKeyExtension.ClientSessionCacheOverride`.

* fix: PSK failing if config session cache set

* Fix a bug causing PSK to fail if Config.ClientSessionCache is set.
* Removed `ClientSessionCacheOverride` from `UtlsPreSharedKeyExtension`. Set the `ClientSessionCache` in `Config`!

Co-Authored-By: zeeker999 <13848632+zeeker999@users.noreply.github.com>

* Optimize tls resumption (#235)

* feat: bug fix and refactor

* feat: improve example docs: add detailed explanation about the design feat: add assertion on uApplyPatch

* fix: address comments
feat: add option `OmitEmptyPsk` and throw error on empty psk by default
feat: revert changes to public interfaces

* fix: weird residue caused by merging conflict

* fix: remove merge conflict residue code

---------

Co-authored-by: Bas Westerbaan <bas@westerbaan.name>
Co-authored-by: Christopher Patton <3453007+cjpatton@users.noreply.github.com>
Co-authored-by: Peter Wu <peter@lekensteyn.nl>
Co-authored-by: zeeker999 <13848632+zeeker999@users.noreply.github.com>
Co-authored-by: 3andne <52860475+3andne@users.noreply.github.com>
2023-08-27 12:48:31 -06:00

1849 lines
52 KiB
Go

// 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 (
"encoding/json"
"errors"
"fmt"
"io"
"strings"
"github.com/gaukas/godicttls"
"golang.org/x/crypto/cryptobyte"
)
// ExtensionFromID returns a TLSExtension for the given extension ID.
func ExtensionFromID(id uint16) TLSExtension {
// deep copy
switch id {
case extensionServerName:
return &SNIExtension{}
case extensionStatusRequest:
return &StatusRequestExtension{}
case extensionSupportedCurves:
return &SupportedCurvesExtension{}
case extensionSupportedPoints:
return &SupportedPointsExtension{}
case extensionSignatureAlgorithms:
return &SignatureAlgorithmsExtension{}
case extensionALPN:
return &ALPNExtension{}
case extensionStatusRequestV2:
return &StatusRequestV2Extension{}
case extensionSCT:
return &SCTExtension{}
case utlsExtensionPadding:
return &UtlsPaddingExtension{}
case extensionExtendedMasterSecret:
return &ExtendedMasterSecretExtension{}
case fakeExtensionTokenBinding:
return &FakeTokenBindingExtension{}
case utlsExtensionCompressCertificate:
return &UtlsCompressCertExtension{}
case fakeExtensionDelegatedCredentials:
return &FakeDelegatedCredentialsExtension{}
case extensionSessionTicket:
return &SessionTicketExtension{}
case extensionPreSharedKey:
return (PreSharedKeyExtension)(&FakePreSharedKeyExtension{}) // To use the result, caller needs further inspection to decide between Fake or Utls.
// case extensionEarlyData:
// return &EarlyDataExtension{}
case extensionSupportedVersions:
return &SupportedVersionsExtension{}
// case extensionCookie:
// return &CookieExtension{}
case extensionPSKModes:
return &PSKKeyExchangeModesExtension{}
// case extensionCertificateAuthorities:
// return &CertificateAuthoritiesExtension{}
case extensionSignatureAlgorithmsCert:
return &SignatureAlgorithmsCertExtension{}
case extensionKeyShare:
return &KeyShareExtension{}
case extensionQUICTransportParameters:
return &QUICTransportParametersExtension{}
case extensionNextProtoNeg:
return &NPNExtension{}
case utlsExtensionApplicationSettings:
return &ApplicationSettingsExtension{}
case fakeOldExtensionChannelID:
return &FakeChannelIDExtension{true}
case fakeExtensionChannelID:
return &FakeChannelIDExtension{}
case fakeRecordSizeLimit:
return &FakeRecordSizeLimitExtension{}
case extensionRenegotiationInfo:
return &RenegotiationInfoExtension{}
default:
if isGREASEUint16(id) {
return &UtlsGREASEExtension{}
}
return nil // not returning GenericExtension, it should be handled by caller
}
}
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
}
// TLSExtensionWriter is an interface allowing a TLS extension to be
// auto-constucted/recovered by reading in a byte stream.
type TLSExtensionWriter interface {
TLSExtension
// Write writes the extension data as a byte slice, up to len(b) bytes from b.
// It returns the number of bytes written (0 <= n <= len(b)) and any error encountered.
//
// The implementation MUST NOT silently drop data if consumed less than len(b) bytes,
// instead, it MUST return an error.
Write(b []byte) (n int, err error)
}
type TLSExtensionJSON interface {
TLSExtension
// UnmarshalJSON unmarshals the JSON-encoded data into the extension.
UnmarshalJSON([]byte) error
}
// SNIExtension implements server_name (0)
type SNIExtension struct {
ServerName string // not an array because go crypto/tls doesn't support multiple SNIs
}
func (e *SNIExtension) Len() int {
// Literal IP addresses, absolute FQDNs, and empty strings are not permitted as SNI values.
// See RFC 6066, Section 3.
hostName := hostnameInSNI(e.ServerName)
if len(hostName) == 0 {
return 0
}
return 4 + 2 + 1 + 2 + len(hostName)
}
func (e *SNIExtension) Read(b []byte) (int, error) {
// Literal IP addresses, absolute FQDNs, and empty strings are not permitted as SNI values.
// See RFC 6066, Section 3.
hostName := hostnameInSNI(e.ServerName)
if len(hostName) == 0 {
return 0, io.EOF
}
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(hostName) + 5) >> 8)
b[3] = byte(len(hostName) + 5)
b[4] = byte((len(hostName) + 3) >> 8)
b[5] = byte(len(hostName) + 3)
// b[6] Server Name Type: host_name (0)
b[7] = byte(len(hostName) >> 8)
b[8] = byte(len(hostName))
copy(b[9:], []byte(hostName))
return e.Len(), io.EOF
}
func (e *SNIExtension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
// Write is a no-op for StatusRequestExtension.
// SNI should not be fingerprinted and is user controlled.
func (e *SNIExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 6066, Section 3
var nameList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&nameList) || nameList.Empty() {
return fullLen, errors.New("unable to read server name extension data")
}
var serverName string
for !nameList.Empty() {
var nameType uint8
var serverNameBytes cryptobyte.String
if !nameList.ReadUint8(&nameType) ||
!nameList.ReadUint16LengthPrefixed(&serverNameBytes) ||
serverNameBytes.Empty() {
return fullLen, errors.New("unable to read server name extension data")
}
if nameType != 0 {
continue
}
if len(serverName) != 0 {
return fullLen, errors.New("multiple names of the same name_type in server name extension are prohibited")
}
serverName = string(serverNameBytes)
if strings.HasSuffix(serverName, ".") {
return fullLen, errors.New("SNI value may not include a trailing dot")
}
}
// clientHelloSpec.Extensions = append(clientHelloSpec.Extensions, &SNIExtension{}) // gaukas moved this line out from the loop.
// don't copy SNI from ClientHello to ClientHelloSpec!
return fullLen, nil
}
func (e *SNIExtension) writeToUConn(uc *UConn) error {
uc.config.ServerName = e.ServerName
hostName := hostnameInSNI(e.ServerName)
uc.HandshakeState.Hello.ServerName = hostName
return nil
}
// StatusRequestExtension implements status_request (5)
type StatusRequestExtension struct {
}
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
}
func (e *StatusRequestExtension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
// Write is a no-op for StatusRequestExtension. No data for this extension.
func (e *StatusRequestExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 4366, Section 3.6
var statusType uint8
var ignored cryptobyte.String
if !extData.ReadUint8(&statusType) ||
!extData.ReadUint16LengthPrefixed(&ignored) ||
!extData.ReadUint16LengthPrefixed(&ignored) {
return fullLen, errors.New("unable to read status request extension data")
}
if statusType != statusTypeOCSP {
return fullLen, errors.New("status request extension statusType is not statusTypeOCSP(1)")
}
return fullLen, nil
}
func (e *StatusRequestExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.OcspStapling = true
return nil
}
// SupportedCurvesExtension implements supported_groups (renamed from "elliptic_curves") (10)
type SupportedCurvesExtension struct {
Curves []CurveID
}
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
}
func (e *SupportedCurvesExtension) UnmarshalJSON(data []byte) error {
var namedGroups struct {
NamedGroupList []string `json:"named_group_list"`
}
if err := json.Unmarshal(data, &namedGroups); err != nil {
return err
}
for _, namedGroup := range namedGroups.NamedGroupList {
if namedGroup == "GREASE" {
e.Curves = append(e.Curves, GREASE_PLACEHOLDER)
continue
}
if group, ok := godicttls.DictSupportedGroupsNameIndexed[namedGroup]; ok {
e.Curves = append(e.Curves, CurveID(group))
} else {
return fmt.Errorf("unknown named group: %s", namedGroup)
}
}
return nil
}
func (e *SupportedCurvesExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 4492, sections 5.1.1 and RFC 8446, Section 4.2.7
var curvesBytes cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&curvesBytes) || curvesBytes.Empty() {
return 0, errors.New("unable to read supported curves extension data")
}
curves := []CurveID{}
for !curvesBytes.Empty() {
var curve uint16
if !curvesBytes.ReadUint16(&curve) {
return 0, errors.New("unable to read supported curves extension data")
}
curves = append(curves, CurveID(unGREASEUint16(curve)))
}
e.Curves = curves
return fullLen, nil
}
func (e *SupportedCurvesExtension) writeToUConn(uc *UConn) error {
uc.config.CurvePreferences = e.Curves
uc.HandshakeState.Hello.SupportedCurves = e.Curves
return nil
}
// SupportedPointsExtension implements ec_point_formats (11)
type SupportedPointsExtension struct {
SupportedPoints []uint8
}
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
}
func (e *SupportedPointsExtension) UnmarshalJSON(data []byte) error {
var pointFormatList struct {
ECPointFormatList []string `json:"ec_point_format_list"`
}
if err := json.Unmarshal(data, &pointFormatList); err != nil {
return err
}
for _, pointFormat := range pointFormatList.ECPointFormatList {
if format, ok := godicttls.DictECPointFormatNameIndexed[pointFormat]; ok {
e.SupportedPoints = append(e.SupportedPoints, format)
} else {
return fmt.Errorf("unknown point format: %s", pointFormat)
}
}
return nil
}
func (e *SupportedPointsExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 4492, Section 5.1.2
supportedPoints := []uint8{}
if !readUint8LengthPrefixed(&extData, &supportedPoints) ||
len(supportedPoints) == 0 {
return 0, errors.New("unable to read supported points extension data")
}
e.SupportedPoints = supportedPoints
return fullLen, nil
}
func (e *SupportedPointsExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.SupportedPoints = e.SupportedPoints
return nil
}
// SignatureAlgorithmsExtension implements signature_algorithms (13)
type SignatureAlgorithmsExtension struct {
SupportedSignatureAlgorithms []SignatureScheme
}
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, sigScheme := range e.SupportedSignatureAlgorithms {
b[6+2*i] = byte(sigScheme >> 8)
b[7+2*i] = byte(sigScheme)
}
return e.Len(), io.EOF
}
func (e *SignatureAlgorithmsExtension) UnmarshalJSON(data []byte) error {
var signatureAlgorithms struct {
Algorithms []string `json:"supported_signature_algorithms"`
}
if err := json.Unmarshal(data, &signatureAlgorithms); err != nil {
return err
}
for _, sigScheme := range signatureAlgorithms.Algorithms {
if sigScheme == "GREASE" {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, GREASE_PLACEHOLDER)
continue
}
if scheme, ok := godicttls.DictSignatureSchemeNameIndexed[sigScheme]; ok {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, SignatureScheme(scheme))
} else {
return fmt.Errorf("unknown signature scheme: %s", sigScheme)
}
}
return nil
}
func (e *SignatureAlgorithmsExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 5246, Section 7.4.1.4.1
var sigAndAlgs cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms := []SignatureScheme{}
for !sigAndAlgs.Empty() {
var sigAndAlg uint16
if !sigAndAlgs.ReadUint16(&sigAndAlg) {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms = append(
supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
}
e.SupportedSignatureAlgorithms = supportedSignatureAlgorithms
return fullLen, nil
}
func (e *SignatureAlgorithmsExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.SupportedSignatureAlgorithms = e.SupportedSignatureAlgorithms
return nil
}
// StatusRequestV2Extension implements status_request_v2 (17)
type StatusRequestV2Extension struct {
}
func (e *StatusRequestV2Extension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.OcspStapling = true
return nil
}
func (e *StatusRequestV2Extension) Len() int {
return 13
}
func (e *StatusRequestV2Extension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// RFC 4366, section 3.6
b[0] = byte(extensionStatusRequestV2 >> 8)
b[1] = byte(extensionStatusRequestV2)
b[2] = 0
b[3] = 9
b[4] = 0
b[5] = 7
b[6] = 2 // OCSP type
b[7] = 0
b[8] = 4
// Two zero valued uint16s for the two lengths.
return e.Len(), io.EOF
}
// Write is a no-op for StatusRequestV2Extension. No data for this extension.
func (e *StatusRequestV2Extension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 4366, Section 3.6
var statusType uint8
var ignored cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&ignored) ||
!extData.ReadUint8(&statusType) ||
!extData.ReadUint16LengthPrefixed(&ignored) ||
!extData.ReadUint16LengthPrefixed(&ignored) ||
!extData.ReadUint16LengthPrefixed(&ignored) {
return fullLen, errors.New("unable to read status request v2 extension data")
}
if statusType != statusV2TypeOCSP {
return fullLen, errors.New("status request v2 extension statusType is not statusV2TypeOCSP(2)")
}
return fullLen, nil
}
func (e *StatusRequestV2Extension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
// SignatureAlgorithmsCertExtension implements signature_algorithms_cert (50)
type SignatureAlgorithmsCertExtension struct {
SupportedSignatureAlgorithms []SignatureScheme
}
func (e *SignatureAlgorithmsCertExtension) Len() int {
return 6 + 2*len(e.SupportedSignatureAlgorithms)
}
func (e *SignatureAlgorithmsCertExtension) 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(extensionSignatureAlgorithmsCert >> 8)
b[1] = byte(extensionSignatureAlgorithmsCert)
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
}
// Copied from SignatureAlgorithmsExtension.UnmarshalJSON
func (e *SignatureAlgorithmsCertExtension) UnmarshalJSON(data []byte) error {
var signatureAlgorithms struct {
Algorithms []string `json:"supported_signature_algorithms"`
}
if err := json.Unmarshal(data, &signatureAlgorithms); err != nil {
return err
}
for _, sigScheme := range signatureAlgorithms.Algorithms {
if sigScheme == "GREASE" {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, GREASE_PLACEHOLDER)
continue
}
if scheme, ok := godicttls.DictSignatureSchemeNameIndexed[sigScheme]; ok {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, SignatureScheme(scheme))
} else {
return fmt.Errorf("unknown cert signature scheme: %s", sigScheme)
}
}
return nil
}
// Write implementation copied from SignatureAlgorithmsExtension.Write
//
// Warning: not tested.
func (e *SignatureAlgorithmsCertExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 8446, Section 4.2.3
var sigAndAlgs cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&sigAndAlgs) || sigAndAlgs.Empty() {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms := []SignatureScheme{}
for !sigAndAlgs.Empty() {
var sigAndAlg uint16
if !sigAndAlgs.ReadUint16(&sigAndAlg) {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms = append(
supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
}
e.SupportedSignatureAlgorithms = supportedSignatureAlgorithms
return fullLen, nil
}
func (e *SignatureAlgorithmsCertExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.SupportedSignatureAlgorithms = e.SupportedSignatureAlgorithms
return nil
}
// ALPNExtension implements application_layer_protocol_negotiation (16)
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
}
func (e *ALPNExtension) UnmarshalJSON(b []byte) error {
var protocolNames struct {
ProtocolNameList []string `json:"protocol_name_list"`
}
if err := json.Unmarshal(b, &protocolNames); err != nil {
return err
}
e.AlpnProtocols = protocolNames.ProtocolNameList
return nil
}
func (e *ALPNExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 7301, Section 3.1
var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
return 0, errors.New("unable to read ALPN extension data")
}
alpnProtocols := []string{}
for !protoList.Empty() {
var proto cryptobyte.String
if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
return 0, errors.New("unable to read ALPN extension data")
}
alpnProtocols = append(alpnProtocols, string(proto))
}
e.AlpnProtocols = alpnProtocols
return fullLen, nil
}
// ApplicationSettingsExtension represents the TLS ALPS extension.
// At the time of this writing, this extension is currently a draft:
// https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps-01
type ApplicationSettingsExtension struct {
SupportedProtocols []string
}
func (e *ApplicationSettingsExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *ApplicationSettingsExtension) Len() int {
bLen := 2 + 2 + 2 // Type + Length + ALPS Extension length
for _, s := range e.SupportedProtocols {
bLen += 1 + len(s) // Supported ALPN Length + actual length of protocol
}
return bLen
}
func (e *ApplicationSettingsExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// Read Type.
b[0] = byte(utlsExtensionApplicationSettings >> 8) // hex: 44 dec: 68
b[1] = byte(utlsExtensionApplicationSettings & 0xff) // hex: 69 dec: 105
lengths := b[2:] // get the remaining buffer without Type
b = b[6:] // set the buffer to the buffer without Type, Length and ALPS Extension Length (so only the Supported ALPN list remains)
stringsLength := 0
for _, s := range e.SupportedProtocols {
l := len(s) // Supported ALPN Length
b[0] = byte(l) // Supported ALPN Length in bytes hex: 02 dec: 2
copy(b[1:], s) // copy the Supported ALPN as bytes to the buffer
b = b[1+l:] // set the buffer to the buffer without the Supported ALPN Length and Supported ALPN (so we can continue to the next protocol in this loop)
stringsLength += 1 + l // Supported ALPN Length (the field itself) + Supported ALPN Length (the value)
}
lengths[2] = byte(stringsLength >> 8) // ALPS Extension Length hex: 00 dec: 0
lengths[3] = byte(stringsLength) // ALPS Extension Length hex: 03 dec: 3
stringsLength += 2 // plus ALPS Extension Length field length
lengths[0] = byte(stringsLength >> 8) // Length hex:00 dec: 0
lengths[1] = byte(stringsLength) // Length hex: 05 dec: 5
return e.Len(), io.EOF
}
func (e *ApplicationSettingsExtension) UnmarshalJSON(b []byte) error {
var applicationSettingsSupport struct {
SupportedProtocols []string `json:"supported_protocols"`
}
if err := json.Unmarshal(b, &applicationSettingsSupport); err != nil {
return err
}
e.SupportedProtocols = applicationSettingsSupport.SupportedProtocols
return nil
}
// Write implementation copied from ALPNExtension.Write
func (e *ApplicationSettingsExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps-01
var protoList cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&protoList) || protoList.Empty() {
return 0, errors.New("unable to read ALPN extension data")
}
alpnProtocols := []string{}
for !protoList.Empty() {
var proto cryptobyte.String
if !protoList.ReadUint8LengthPrefixed(&proto) || proto.Empty() {
return 0, errors.New("unable to read ALPN extension data")
}
alpnProtocols = append(alpnProtocols, string(proto))
}
e.SupportedProtocols = alpnProtocols
return fullLen, nil
}
// SCTExtension implements signed_certificate_timestamp (18)
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
}
func (e *SCTExtension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
func (e *SCTExtension) Write(_ []byte) (int, error) {
return 0, nil
}
// GenericExtension allows to include in ClientHello arbitrary unsupported extensions.
// It is not defined in TLS RFCs nor by IANA.
// If a server echoes this extension back, the handshake will likely fail due to no further support.
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
}
func (e *GenericExtension) UnmarshalJSON(b []byte) error {
var genericExtension struct {
Name string `json:"name"`
Data []byte `json:"data"`
}
if err := json.Unmarshal(b, &genericExtension); err != nil {
return err
}
// lookup extension ID by name
if id, ok := godicttls.DictExtTypeNameIndexed[genericExtension.Name]; ok {
e.Id = id
} else {
return fmt.Errorf("unknown extension name %s", genericExtension.Name)
}
e.Data = genericExtension.Data
return nil
}
// ExtendedMasterSecretExtension implements extended_master_secret (23)
//
// Was named as ExtendedMasterSecretExtension, renamed due to crypto/tls
// implemented this extension's support.
type ExtendedMasterSecretExtension struct {
}
// TODO: update when this extension is implemented in crypto/tls
// but we probably won't have to enable it in Config
func (e *ExtendedMasterSecretExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.Ems = true
return nil
}
func (e *ExtendedMasterSecretExtension) Len() int {
return 4
}
func (e *ExtendedMasterSecretExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://tools.ietf.org/html/rfc7627
b[0] = byte(extensionExtendedMasterSecret >> 8)
b[1] = byte(extensionExtendedMasterSecret)
// The length is 0
return e.Len(), io.EOF
}
func (e *ExtendedMasterSecretExtension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
func (e *ExtendedMasterSecretExtension) Write(_ []byte) (int, error) {
// https://tools.ietf.org/html/rfc7627
return 0, nil
}
// 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, sessionHash []byte) []byte {
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
}
func (e *UtlsGREASEExtension) Write(b []byte) (int, error) {
e.Value = GREASE_PLACEHOLDER
e.Body = make([]byte, len(b))
n := copy(e.Body, b)
return n, nil
}
func (e *UtlsGREASEExtension) UnmarshalJSON(b []byte) error {
var jsonObj struct {
Id uint16 `json:"id"`
Data []byte `json:"data"`
KeepID bool `json:"keep_id"`
KeepData bool `json:"keep_data"`
}
if err := json.Unmarshal(b, &jsonObj); err != nil {
return err
}
if jsonObj.Id == 0 {
return nil
}
if isGREASEUint16(jsonObj.Id) {
if jsonObj.KeepID {
e.Value = jsonObj.Id
}
if jsonObj.KeepData {
e.Body = jsonObj.Data
}
return nil
} else {
return errors.New("GREASE extension id must be a GREASE value")
}
}
// UtlsPaddingExtension implements padding (21)
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
}
func (e *UtlsPaddingExtension) UnmarshalJSON(b []byte) error {
var jsonObj struct {
Length uint `json:"len"`
}
if err := json.Unmarshal(b, &jsonObj); err != nil {
return err
}
if jsonObj.Length == 0 {
e.GetPaddingLen = BoringPaddingStyle
} else {
e.PaddingLen = int(jsonObj.Length)
e.WillPad = true
}
return nil
}
func (e *UtlsPaddingExtension) Write(_ []byte) (int, error) {
e.GetPaddingLen = BoringPaddingStyle
return 0, nil
}
// 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
}
// UtlsCompressCertExtension implements compress_certificate (27) and is only implemented client-side
// for server certificates. Alternate certificate message formats
// (https://datatracker.ietf.org/doc/html/rfc7250) are not supported.
//
// See https://datatracker.ietf.org/doc/html/rfc8879#section-3
type UtlsCompressCertExtension struct {
Algorithms []CertCompressionAlgo
}
func (e *UtlsCompressCertExtension) writeToUConn(uc *UConn) error {
uc.certCompressionAlgs = e.Algorithms
return nil
}
func (e *UtlsCompressCertExtension) Len() int {
return 4 + 1 + (2 * len(e.Algorithms))
}
func (e *UtlsCompressCertExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
b[0] = byte(utlsExtensionCompressCertificate >> 8)
b[1] = byte(utlsExtensionCompressCertificate & 0xff)
extLen := 2 * len(e.Algorithms)
if extLen > 255 {
return 0, errors.New("too many certificate compression methods")
}
// Extension data length.
b[2] = byte((extLen + 1) >> 8)
b[3] = byte((extLen + 1) & 0xff)
// Methods length.
b[4] = byte(extLen)
i := 5
for _, compMethod := range e.Algorithms {
b[i] = byte(compMethod >> 8)
b[i+1] = byte(compMethod)
i += 2
}
return e.Len(), io.EOF
}
func (e *UtlsCompressCertExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
methods := []CertCompressionAlgo{}
methodsRaw := new(cryptobyte.String)
if !extData.ReadUint8LengthPrefixed(methodsRaw) {
return 0, errors.New("unable to read cert compression algorithms extension data")
}
for !methodsRaw.Empty() {
var method uint16
if !methodsRaw.ReadUint16(&method) {
return 0, errors.New("unable to read cert compression algorithms extension data")
}
methods = append(methods, CertCompressionAlgo(method))
}
e.Algorithms = methods
return fullLen, nil
}
func (e *UtlsCompressCertExtension) UnmarshalJSON(b []byte) error {
var certificateCompressionAlgorithms struct {
Algorithms []string `json:"algorithms"`
}
if err := json.Unmarshal(b, &certificateCompressionAlgorithms); err != nil {
return err
}
for _, algorithm := range certificateCompressionAlgorithms.Algorithms {
if alg, ok := godicttls.DictCertificateCompressionAlgorithmNameIndexed[algorithm]; ok {
e.Algorithms = append(e.Algorithms, CertCompressionAlgo(alg))
} else {
return fmt.Errorf("unknown certificate compression algorithm %s", algorithm)
}
}
return nil
}
// KeyShareExtension implements key_share (51) and is for TLS 1.3 only.
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) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 8446, Section 4.2.8
var clientShares cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&clientShares) {
return 0, errors.New("unable to read key share extension data")
}
keyShares := []KeyShare{}
for !clientShares.Empty() {
var ks KeyShare
var group uint16
if !clientShares.ReadUint16(&group) ||
!readUint16LengthPrefixed(&clientShares, &ks.Data) ||
len(ks.Data) == 0 {
return 0, errors.New("unable to read key share extension data")
}
ks.Group = CurveID(unGREASEUint16(group))
// if not GREASE, key share data will be discarded as it should
// be generated per connection
if ks.Group != GREASE_PLACEHOLDER {
ks.Data = nil
}
keyShares = append(keyShares, ks)
}
e.KeyShares = keyShares
return fullLen, nil
}
func (e *KeyShareExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.KeyShares = e.KeyShares
return nil
}
func (e *KeyShareExtension) UnmarshalJSON(b []byte) error {
var keyShareClientHello struct {
ClientShares []struct {
Group string `json:"group"`
KeyExchange []uint8 `json:"key_exchange"`
} `json:"client_shares"`
}
if err := json.Unmarshal(b, &keyShareClientHello); err != nil {
return err
}
for _, clientShare := range keyShareClientHello.ClientShares {
if clientShare.Group == "GREASE" {
e.KeyShares = append(e.KeyShares, KeyShare{
Group: GREASE_PLACEHOLDER,
Data: clientShare.KeyExchange,
})
continue
}
if groupID, ok := godicttls.DictSupportedGroupsNameIndexed[clientShare.Group]; ok {
ks := KeyShare{
Group: CurveID(groupID),
Data: clientShare.KeyExchange,
}
e.KeyShares = append(e.KeyShares, ks)
} else {
return fmt.Errorf("unknown group %s", clientShare.Group)
}
}
return nil
}
// QUICTransportParametersExtension implements quic_transport_parameters (57).
//
// Currently, it works as a fake extension and does not support parsing, since
// the QUICConn provided by this package does not really understand these
// parameters.
type QUICTransportParametersExtension struct {
TransportParameters TransportParameters
marshalResult []byte // TransportParameters will be marshaled into this slice
}
func (e *QUICTransportParametersExtension) Len() int {
if e.marshalResult == nil {
e.marshalResult = e.TransportParameters.Marshal()
}
return 4 + len(e.marshalResult)
}
func (e *QUICTransportParametersExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
b[0] = byte(extensionQUICTransportParameters >> 8)
b[1] = byte(extensionQUICTransportParameters)
// e.Len() is called before so that e.marshalResult is set
b[2] = byte((len(e.marshalResult)) >> 8)
b[3] = byte(len(e.marshalResult))
copy(b[4:], e.marshalResult)
return e.Len(), io.EOF
}
func (e *QUICTransportParametersExtension) writeToUConn(*UConn) error {
// no need to set *UConn.quic.transportParams, since it is unused
return nil
}
// PSKKeyExchangeModesExtension implements psk_key_exchange_modes (45).
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) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 8446, Section 4.2.9
// TODO: PSK Modes have their own form of GREASE-ing which is not currently implemented
// the current functionality will NOT re-GREASE/re-randomize these values when using a fingerprinted spec
// https://github.com/refraction-networking/utls/pull/58#discussion_r522354105
// https://tools.ietf.org/html/draft-ietf-tls-grease-01#section-2
pskModes := []uint8{}
if !readUint8LengthPrefixed(&extData, &pskModes) {
return 0, errors.New("unable to read PSK extension data")
}
e.Modes = pskModes
return fullLen, nil
}
func (e *PSKKeyExchangeModesExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.PskModes = e.Modes
return nil
}
func (e *PSKKeyExchangeModesExtension) UnmarshalJSON(b []byte) error {
var pskKeyExchangeModes struct {
Modes []string `json:"ke_modes"`
}
if err := json.Unmarshal(b, &pskKeyExchangeModes); err != nil {
return err
}
for _, mode := range pskKeyExchangeModes.Modes {
if modeID, ok := godicttls.DictPSKKeyExchangeModeNameIndexed[mode]; ok {
e.Modes = append(e.Modes, modeID)
} else {
return fmt.Errorf("unknown PSK Key Exchange Mode %s", mode)
}
}
return nil
}
// SupportedVersionsExtension implements supported_versions (43).
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
}
func (e *SupportedVersionsExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// RFC 8446, Section 4.2.1
var versList cryptobyte.String
if !extData.ReadUint8LengthPrefixed(&versList) || versList.Empty() {
return 0, errors.New("unable to read supported versions extension data")
}
supportedVersions := []uint16{}
for !versList.Empty() {
var vers uint16
if !versList.ReadUint16(&vers) {
return 0, errors.New("unable to read supported versions extension data")
}
supportedVersions = append(supportedVersions, unGREASEUint16(vers))
}
e.Versions = supportedVersions
return fullLen, nil
}
func (e *SupportedVersionsExtension) UnmarshalJSON(b []byte) error {
var supportedVersions struct {
Versions []string `json:"versions"`
}
if err := json.Unmarshal(b, &supportedVersions); err != nil {
return err
}
for _, version := range supportedVersions.Versions {
switch version {
case "GREASE":
e.Versions = append(e.Versions, GREASE_PLACEHOLDER)
case "TLS 1.3":
e.Versions = append(e.Versions, VersionTLS13)
case "TLS 1.2":
e.Versions = append(e.Versions, VersionTLS12)
case "TLS 1.1":
e.Versions = append(e.Versions, VersionTLS11)
case "TLS 1.0":
e.Versions = append(e.Versions, VersionTLS10)
case "SSL 3.0": // deprecated
// e.Versions = append(e.Versions, VersionSSL30)
return fmt.Errorf("SSL 3.0 is deprecated")
default:
return fmt.Errorf("unknown version %s", version)
}
}
return nil
}
// CookieExtension implements cookie (44).
// MUST NOT be part of initial ClientHello
type CookieExtension struct {
Cookie []byte
}
func (e *CookieExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *CookieExtension) Len() int {
return 4 + len(e.Cookie)
}
func (e *CookieExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
b[0] = byte(extensionCookie >> 8)
b[1] = byte(extensionCookie)
b[2] = byte(len(e.Cookie) >> 8)
b[3] = byte(len(e.Cookie))
if len(e.Cookie) > 0 {
copy(b[4:], e.Cookie)
}
return e.Len(), io.EOF
}
func (e *CookieExtension) UnmarshalJSON(data []byte) error {
var cookie struct {
Cookie []uint8 `json:"cookie"`
}
if err := json.Unmarshal(data, &cookie); err != nil {
return err
}
e.Cookie = []byte(cookie.Cookie)
return nil
}
// NPNExtension implements next_protocol_negotiation (Not IANA assigned)
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
}
// Write is a no-op for NPNExtension. NextProtos are not included in the
// ClientHello.
func (e *NPNExtension) Write(_ []byte) (int, error) {
return 0, nil
}
// draft-agl-tls-nextprotoneg-04:
// The "extension_data" field of a "next_protocol_negotiation" extension
// in a "ClientHello" MUST be empty.
func (e *NPNExtension) UnmarshalJSON(_ []byte) error {
return nil
}
// RenegotiationInfoExtension implements renegotiation_info (65281)
type RenegotiationInfoExtension struct {
// Renegotiation field limits how many times client will perform renegotiation: no limit, once, or never.
// The extension still will be sent, even if Renegotiation is set to RenegotiateNever.
Renegotiation RenegotiationSupport // [UTLS] added for internal use only
// RenegotiatedConnection is not yet properly handled, now we
// are just copying it to the client hello.
//
// If this is the initial handshake for a connection, then the
// "renegotiated_connection" field is of zero length in both the
// ClientHello and the ServerHello.
// RenegotiatedConnection []byte
}
func (e *RenegotiationInfoExtension) Len() int {
return 5 // + len(e.RenegotiatedConnection)
}
func (e *RenegotiationInfoExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// dataLen := len(e.RenegotiatedConnection)
extBodyLen := 1 // + len(dataLen)
b[0] = byte(extensionRenegotiationInfo >> 8)
b[1] = byte(extensionRenegotiationInfo & 0xff)
b[2] = byte(extBodyLen >> 8)
b[3] = byte(extBodyLen)
// b[4] = byte(dataLen)
// copy(b[5:], e.RenegotiatedConnection)
return e.Len(), io.EOF
}
func (e *RenegotiationInfoExtension) UnmarshalJSON(_ []byte) error {
e.Renegotiation = RenegotiateOnceAsClient
return nil
}
func (e *RenegotiationInfoExtension) Write(_ []byte) (int, error) {
e.Renegotiation = RenegotiateOnceAsClient // none empty or other modes are unsupported
// extData := cryptobyte.String(b)
// var renegotiatedConnection cryptobyte.String
// if !extData.ReadUint8LengthPrefixed(&renegotiatedConnection) || !extData.Empty() {
// return 0, errors.New("unable to read renegotiation info extension data")
// }
// e.RenegotiatedConnection = make([]byte, len(renegotiatedConnection))
// copy(e.RenegotiatedConnection, renegotiatedConnection)
return 0, nil
}
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
case RenegotiateNever:
default:
}
return nil
}
/*
FAKE EXTENSIONS
*/
type FakeChannelIDExtension struct {
// The extension ID changed from 30031 to 30032. Set to true to use the old extension ID.
OldExtensionID bool
}
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
}
extensionID := fakeExtensionChannelID
if e.OldExtensionID {
extensionID = fakeOldExtensionChannelID
}
// https://tools.ietf.org/html/draft-balfanz-tls-channelid-00
b[0] = byte(extensionID >> 8)
b[1] = byte(extensionID & 0xff)
// The length is 0
return e.Len(), io.EOF
}
func (e *FakeChannelIDExtension) Write(_ []byte) (int, error) {
return 0, nil
}
func (e *FakeChannelIDExtension) UnmarshalJSON(_ []byte) error {
return nil
}
// FakeRecordSizeLimitExtension implements record_size_limit (28)
// but with no support.
type FakeRecordSizeLimitExtension struct {
Limit uint16
}
func (e *FakeRecordSizeLimitExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeRecordSizeLimitExtension) Len() int {
return 6
}
func (e *FakeRecordSizeLimitExtension) 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(fakeRecordSizeLimit >> 8)
b[1] = byte(fakeRecordSizeLimit & 0xff)
b[2] = byte(0)
b[3] = byte(2)
b[4] = byte(e.Limit >> 8)
b[5] = byte(e.Limit & 0xff)
return e.Len(), io.EOF
}
func (e *FakeRecordSizeLimitExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
if !extData.ReadUint16(&e.Limit) {
return 0, errors.New("unable to read record size limit extension data")
}
return fullLen, nil
}
func (e *FakeRecordSizeLimitExtension) UnmarshalJSON(data []byte) error {
var limitAccepter struct {
Limit uint16 `json:"record_size_limit"`
}
if err := json.Unmarshal(data, &limitAccepter); err != nil {
return err
}
e.Limit = limitAccepter.Limit
return nil
}
type DelegatedCredentialsExtension = FakeDelegatedCredentialsExtension
// https://tools.ietf.org/html/rfc8472#section-2
type FakeTokenBindingExtension struct {
MajorVersion, MinorVersion uint8
KeyParameters []uint8
}
func (e *FakeTokenBindingExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeTokenBindingExtension) Len() int {
// extension ID + data length + versions + key parameters length + key parameters
return 2 + 2 + 2 + 1 + len(e.KeyParameters)
}
func (e *FakeTokenBindingExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
dataLen := e.Len() - 4
b[0] = byte(fakeExtensionTokenBinding >> 8)
b[1] = byte(fakeExtensionTokenBinding & 0xff)
b[2] = byte(dataLen >> 8)
b[3] = byte(dataLen & 0xff)
b[4] = e.MajorVersion
b[5] = e.MinorVersion
b[6] = byte(len(e.KeyParameters))
if len(e.KeyParameters) > 0 {
copy(b[7:], e.KeyParameters)
}
return e.Len(), io.EOF
}
func (e *FakeTokenBindingExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
var keyParameters cryptobyte.String
if !extData.ReadUint8(&e.MajorVersion) ||
!extData.ReadUint8(&e.MinorVersion) ||
!extData.ReadUint8LengthPrefixed(&keyParameters) {
return 0, errors.New("unable to read token binding extension data")
}
e.KeyParameters = keyParameters
return fullLen, nil
}
func (e *FakeTokenBindingExtension) UnmarshalJSON(data []byte) error {
var tokenBindingAccepter struct {
TB_ProtocolVersion struct {
Major uint8 `json:"major"`
Minor uint8 `json:"minor"`
} `json:"token_binding_version"`
TokenBindingKeyParameters []string `json:"key_parameters_list"`
}
if err := json.Unmarshal(data, &tokenBindingAccepter); err != nil {
return err
}
e.MajorVersion = tokenBindingAccepter.TB_ProtocolVersion.Major
e.MinorVersion = tokenBindingAccepter.TB_ProtocolVersion.Minor
for _, param := range tokenBindingAccepter.TokenBindingKeyParameters {
switch param {
case "rsa2048_pkcs1.5":
e.KeyParameters = append(e.KeyParameters, 0)
case "rsa2048_pss":
e.KeyParameters = append(e.KeyParameters, 1)
case "ecdsap256":
e.KeyParameters = append(e.KeyParameters, 2)
default:
return fmt.Errorf("unknown token binding key parameter: %s", param)
}
}
return nil
}
// https://datatracker.ietf.org/doc/html/draft-ietf-tls-subcerts-15#section-4.1.1
type FakeDelegatedCredentialsExtension struct {
SupportedSignatureAlgorithms []SignatureScheme
}
func (e *FakeDelegatedCredentialsExtension) writeToUConn(uc *UConn) error {
return nil
}
func (e *FakeDelegatedCredentialsExtension) Len() int {
return 6 + 2*len(e.SupportedSignatureAlgorithms)
}
func (e *FakeDelegatedCredentialsExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
// https://datatracker.ietf.org/doc/html/draft-ietf-tls-subcerts-15#section-4.1.1
b[0] = byte(fakeExtensionDelegatedCredentials >> 8)
b[1] = byte(fakeExtensionDelegatedCredentials)
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
}
func (e *FakeDelegatedCredentialsExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
//https://datatracker.ietf.org/doc/html/draft-ietf-tls-subcerts-15#section-4.1.1
var supportedAlgs cryptobyte.String
if !extData.ReadUint16LengthPrefixed(&supportedAlgs) || supportedAlgs.Empty() {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms := []SignatureScheme{}
for !supportedAlgs.Empty() {
var sigAndAlg uint16
if !supportedAlgs.ReadUint16(&sigAndAlg) {
return 0, errors.New("unable to read signature algorithms extension data")
}
supportedSignatureAlgorithms = append(
supportedSignatureAlgorithms, SignatureScheme(sigAndAlg))
}
e.SupportedSignatureAlgorithms = supportedSignatureAlgorithms
return fullLen, nil
}
// Implementation copied from SignatureAlgorithmsExtension.UnmarshalJSON
func (e *FakeDelegatedCredentialsExtension) UnmarshalJSON(data []byte) error {
var signatureAlgorithms struct {
Algorithms []string `json:"supported_signature_algorithms"`
}
if err := json.Unmarshal(data, &signatureAlgorithms); err != nil {
return err
}
for _, sigScheme := range signatureAlgorithms.Algorithms {
if sigScheme == "GREASE" {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, GREASE_PLACEHOLDER)
continue
}
if scheme, ok := godicttls.DictSignatureSchemeNameIndexed[sigScheme]; ok {
e.SupportedSignatureAlgorithms = append(e.SupportedSignatureAlgorithms, SignatureScheme(scheme))
} else {
return fmt.Errorf("unknown delegated credentials signature scheme: %s", sigScheme)
}
}
return nil
}