fix(nix): update vendorHash and vendor dir for new deps

This commit is contained in:
Alexander
2026-04-10 18:25:19 +02:00
parent da59d8f83b
commit 9dc664a3ba
2533 changed files with 1304328 additions and 2 deletions
+19
View File
@@ -0,0 +1,19 @@
language: go
go:
- "1.11.x"
sudo: required
dist: trusty
install:
- go get -t ./...
- go get golang.org/x/lint/golint
# Install gometalinter
- go get github.com/alecthomas/gometalinter
script:
- go test -race -v .
- gometalinter --install
- gometalinter --disable-all -E vet -E ineffassign --tests .
- gometalinter --disable-all -E gofmt -E misspell -E goimports --tests u_*
+23
View File
@@ -0,0 +1,23 @@
# How to Contribute
We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.
## Contributor License Agreement
Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution,
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.
You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.
## Code reviews
All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.
+69
View File
@@ -0,0 +1,69 @@
# How this package works
### Chapter 1: [Making private things public](./u_public.go)
There are numerous handshake-related structs in crypto/tls, most of which are either private or have private fields.
One of them — `clientHandshakeState` — has private function `handshake()`,
which is called in the beginning of default handshake.
Unfortunately, user will not be able to directly access this struct outside of tls package.
As a result, we decided to employ following workaround: declare public copies of private structs.
Now user is free to manipulate fields of public `ClientHandshakeState`.
Then, right before handshake, we can shallow-copy public state into private `clientHandshakeState`,
call `handshake()` on it and carry on with default Golang handshake process.
After handshake is done we shallow-copy private state back to public, allowing user to read results of handshake.
### Chapter 2: [TLSExtension](./u_tls_extensions.go)
The way we achieve reasonable flexibilty with extensions is inspired by
[ztls'](https://github.com/zmap/zcrypto/blob/master/tls/handshake_extensions.go) design.
However, our design has several differences, so we wrote it from scratch.
This design allows us to have an array of `TLSExtension` objects and then marshal them in order:
```Golang
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
}
```
`writeToUConn()` applies appropriate per-extension changes to `UConn`.
`Len()` provides the size of marshaled extension, so we can allocate appropriate buffer beforehand,
catch out-of-bound errors easily and guide size-dependent extensions such as padding.
`Read(buffer []byte)` _writes(see: io.Reader interface)_ marshaled extensions into provided buffer.
This avoids extra allocations.
### Chapter 3: [UConn](./u_conn.go)
`UConn` extends standard `tls.Conn`. Most notably, it stores slice with `TLSExtension`s and public
`ClientHandshakeState`.
Whenever `UConn.BuildHandshakeState()` gets called (happens automatically in `UConn.Handshake()`
or could be called manually), config will be applied according to chosen `ClientHelloID`.
From contributor's view there are 2 main behaviors:
* `HelloGolang` simply calls default Golang's [`makeClientHello()`](./handshake_client.go)
and directly stores it into `HandshakeState.Hello`. utls-specific stuff is ignored.
* Other ClientHelloIDs fill `UConn.Hello.{Random, CipherSuites, CompressionMethods}` and `UConn.Extensions` with
per-parrot setup, which then gets applied to appropriate standard tls structs,
and then marshaled by utls into `HandshakeState.Hello`.
### Chapter 4: Tests
Tests exist, but coverage is very limited. What's covered is a conjunction of
* TLS 1.2
* Working parrots without any unsupported extensions (only Android 5.1 at this time)
* Ciphersuites offered by parrot.
* Ciphersuites supported by Golang
* Simple conversation with reference implementation of OpenSSL.
(e.g. no automatic checks for renegotiations, parroting quality and such)
plus we test some other minor things.
Basically, current tests aim to provide a sanity check.
# Merging upstream
```Bash
git remote add -f golang git@github.com:golang/go.git
git checkout -b golang-upstream golang/master
git subtree split -P src/crypto/tls/ -b golang-tls-upstream
git checkout master
git merge --no-commit golang-tls-upstream
```
+27
View File
@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+315
View File
@@ -0,0 +1,315 @@
# ![uTLS](logo_small.png) uTLS
[![Build Status](https://github.com/refraction-networking/utls/actions/workflows/go.yml/badge.svg?branch=master)](https://github.com/refraction-networking/utls/actions/workflows/go.yml)
[![godoc](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/refraction-networking/utls#UConn)
---
uTLS is a fork of "crypto/tls", which provides ClientHello fingerprinting resistance, low-level access to handshake, fake session tickets and some other features. Handshake is still performed by "crypto/tls", this library merely changes ClientHello part of it and provides low-level access.
**Minimum Go Version**: Go 1.21
If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, ~~you can contact one of developers personally via sergey.frolov@colorado.edu~~.
You can contact one of developers personally via gaukas.wang@colorado.edu.
Documentation below may not keep up with all the changes and new features at all times,
so you are encouraged to use [godoc](https://godoc.org/github.com/refraction-networking/utls#UConn).
*Note: Information provided below in this README.md could be obsolete. We welcome
any contributions to refresh the documentations in addition to code contributions.*
# Features
## Low-level access to handshake
* Read/write access to all bits of client hello message.
* Read access to fields of ClientHandshakeState, which, among other things, includes ServerHello and MasterSecret.
* Read keystream. Can be used, for example, to "write" something in ciphertext.
## ClientHello fingerprinting resistance
Golang's ClientHello has a very unique fingerprint, which especially sticks out on mobile clients,
where Golang is not too popular yet.
Some members of anti-censorship community are concerned that their tools could be trivially blocked based on
ClientHello with relatively small collateral damage. There are multiple solutions to this issue.
**It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.**
[utls.Roller](#roller) does this automatically.
### Randomized Fingerprint
Randomized Fingerprints are supposedly good at defeating blacklists, since
those fingerprints have random ciphersuites and extensions in random order.
Note that all used ciphersuites and extensions are fully supported by uTLS,
which provides a solid moving target without any compatibility or parrot-is-dead attack risks.
But note that there's a small chance that generated fingerprint won't work,
so you may want to keep generating until a working one is found,
and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints.
[utls.Roller](#roller) reuses working fingerprint automatically.
#### Generating randomized fingerprints
To generate a randomized fingerprint, simply do:
```Golang
uTlsConn := tls.UClient(tcpConn, &config, tls.HelloRandomized)
```
you can use `helloRandomizedALPN` or `helloRandomizedNoALPN` to ensure presence or absence of
ALPN(Application-Layer Protocol Negotiation) extension.
It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN).
If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).
#### Reusing randomized fingerprint
```Golang
// oldConn is an old connection that worked before, so we want to reuse it
// newConn is a new connection we'd like to establish
newConn := tls.UClient(tcpConn, &config, oldConn.ClientHelloID)
```
### Parroting
This package can be used to parrot ClientHello of popular browsers.
There are some caveats to this parroting:
* We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls.
This is not a problem, if you fully control the server and turn unsupported things off on server side.
* Parroting could be imperfect, and there is no parroting beyond ClientHello.
#### Compatibility risks of available parrots
| Parrot | Ciphers* | Signature* | Unsupported extensions | TLS Fingerprint ID |
| ------------- | -------- | ---------- | ---------------------- | --------------------------------------------- |
| Chrome 62 | no | no | ChannelID | [0a4a74aeebd1bb66](https://tlsfingerprint.io/id/0a4a74aeebd1bb66) |
| Chrome 70 | no | no | ChannelID, Encrypted Certs | [bc4c7e42f4961cd7](https://tlsfingerprint.io/id/bc4c7e42f4961cd7) |
| Chrome 72 | no | no | ChannelID, Encrypted Certs | [bbf04e5f1881f506](https://tlsfingerprint.io/id/bbf04e5f1881f506) |
| Chrome 83 | no | no | ChannelID, Encrypted Certs | [9c673fd64a32c8dc](https://tlsfingerprint.io/id/9c673fd64a32c8dc) |
| Firefox 56 | very low | no | None | [c884bad7f40bee56](https://tlsfingerprint.io/id/c884bad7f40bee56) |
| Firefox 65 | very low | no | MaxRecordSize | [6bfedc5d5c740d58](https://tlsfingerprint.io/id/6bfedc5d5c740d58) |
| iOS 11.1 | low** | no | None | [71a81bafd58e1301](https://tlsfingerprint.io/id/71a81bafd58e1301) |
| iOS 12.1 | low** | no | None | [ec55e5b4136c7949](https://tlsfingerprint.io/id/ec55e5b4136c7949) |
\* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild,
*visibly breaking the connection*.
\*\* No risk, if `utls.EnableWeakCiphers()` is called prior to using it.
#### Parrots FAQ
> Does it really look like, say, Google Chrome with all the [GREASE](https://tools.ietf.org/html/draft-davidben-tls-grease-01) and stuff?
It LGTM, but please open up Wireshark and check. If you see something — [say something](https://github.com/refraction-networking/utls/issues).
> Aren't there side channels? Everybody knows that the ~~bird is a word~~[parrot is dead](https://people.cs.umass.edu/~amir/papers/parrot.pdf)
There sure are. If you found one that approaches practicality at line speed — [please tell us](https://github.com/refraction-networking/utls/issues).
However, there is a difference between this sort of parroting and techniques like SkypeMorth.
Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol
could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry).
It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.
### Custom Handshake
It is possible to create custom handshake by
1) Use `HelloCustom` as an argument for `UClient()` to get empty config
2) Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
3) Configure and add various [TLS Extensions](u_tls_extensions.go) to UConn.Extensions: they will be marshaled in order.
4) Set Session and SessionCache, as needed.
If you need to manually control all the bytes on the wire(certainly not recommended!),
you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself.
In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup
and not confuse "crypto/tls", which will be processing response from server.
### Fingerprinting Captured Client Hello
You can use a captured client hello to generate new ones that mimic/have the same properties as the original.
The generated client hellos _should_ look like they were generated from the same client software as the original fingerprinted bytes.
In order to do this:
1) Create a `ClientHelloSpec` from the raw bytes of the original client hello
2) Use `HelloCustom` as an argument for `UClient()` to get empty config
3) Use `ApplyPreset` with the generated `ClientHelloSpec` to set the appropriate connection properties
```
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}
```
The `rawCapturedClientHelloBytes` should be the full tls record, including the record type/version/length header.
## Roller
A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.
```Golang
// NewRoller creates Roller object with default range of HelloIDs to cycle
// through until a working/unblocked one is found.
func NewRoller() (*Roller, error)
```
```Golang
// Dial attempts to connect to given address using different HelloIDs.
// If a working HelloID is found, it is used again for subsequent Dials.
// If tcp connection fails or all HelloIDs are tried, returns with last error.
//
// Usage examples:
//
// Dial("tcp4", "google.com:443", "google.com")
// Dial("tcp", "10.23.144.22:443", "mywebserver.org")
func (c *Roller) Dial(network, addr, serverName string) (*UConn, error)
```
## Fake Session Tickets
Fake session tickets is a very nifty trick that allows power users to hide parts of handshake, which may have some very fingerprintable features of handshake, and saves 1 RTT.
Currently, there is a simple function to set session ticket to any desired state:
```Golang
// If you want you session tickets to be reused - use same cache on following connections
func (uconn *UConn) SetSessionState(session *ClientSessionState)
```
Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:
```Golang
// If you want you session tickets to be reused - use same cache on following connections
func (uconn *UConn) SetSessionCache(cache ClientSessionCache)
```
## Custom TLS extensions
If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed `*tls.GenericExtension` into your own struct and override `Len()` and `Read()` methods. For example, [DelegatedCredentials](https://datatracker.ietf.org/doc/draft-ietf-tls-subcerts/) extension can be implemented as follows:
```Golang
const FakeDelegatedCredentials uint16 = 0x0022
type FakeDelegatedCredentialsExtension struct {
*tls.GenericExtension
SignatureAlgorithms []tls.SignatureScheme
}
func (e *FakeDelegatedCredentialsExtension) Len() int {
return 6 + 2*len(e.SignatureAlgorithms)
}
func (e *FakeDelegatedCredentialsExtension) Read(b []byte) (n int, err error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
offset := 0
appendUint16 := func(val uint16) {
b[offset] = byte(val >> 8)
b[offset+1] = byte(val & 0xff)
offset += 2
}
// Extension type
appendUint16(FakeDelegatedCredentials)
algosLength := 2 * len(e.SignatureAlgorithms)
// Extension data length
appendUint16(uint16(algosLength) + 2)
// Algorithms list length
appendUint16(uint16(algosLength))
// Algorithms list
for _, a := range e.SignatureAlgorithms {
appendUint16(uint16(a))
}
return e.Len(), io.EOF
}
```
Then it can be used just like normal extension:
```Golang
&tls.ClientHelloSpec{
//...
Extensions: []tls.TLSExtension{
//...
&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}
```
# Client Hello IDs
See full list of `clientHelloID` values [here](https://godoc.org/github.com/refraction-networking/utls#ClientHelloID).
There are different behaviors you can get, depending on your `clientHelloID`:
1. ```utls.HelloRandomized``` adds/reorders extensions, ciphersuites, etc. randomly.
`HelloRandomized` adds ALPN in a percentage of cases, you may want to use `HelloRandomizedALPN` or
`HelloRandomizedNoALPN` to choose specific behavior explicitly, as ALPN might affect application layer.
2. ```utls.HelloGolang```
HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL
overwrite your changes to Hello(Config, Session are fine).
You might want to call BuildHandshakeState() before applying any changes.
UConn.Extensions will be completely ignored.
3. ```utls.HelloCustom```
will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
4. The rest will will parrot given browser. Such parrots include, for example:
* `utls.HelloChrome_Auto`- parrots recommended(usually latest) Google Chrome version
* `utls.HelloChrome_58` - parrots Google Chrome 58
* `utls.HelloFirefox_Auto` - parrots recommended(usually latest) Firefox version
* `utls.HelloFirefox_55` - parrots Firefox 55
# Usage
## Examples
Find basic examples [here](examples/examples.go).
Here's a more [advanced example](https://github.com/sergeyfrolov/gotapdance/blob//9a777f35a04b0c4c5dacd30bca0e9224eb737b5e/tapdance/conn_raw.go#L275-L292) showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.
### Migrating from "crypto/tls"
Here's how default "crypto/tls" is typically used:
```Golang
dialConn, err := net.Dial("tcp", "172.217.11.46:443")
if err != nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config := tls.Config{ServerName: "www.google.com"}
tlsConn := tls.Client(dialConn, &config)
n, err = tlsConn.Write("Hello, World!")
//...
```
To start using using uTLS:
1. Import this library (e.g. `import tls "github.com/refraction-networking/utls"`)
2. Pick the [Client Hello ID](#client-hello-ids)
3. Simply substitute `tlsConn := tls.Client(dialConn, &config)`
with `tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)`
### Customizing handshake
Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:
```Golang
cRandom := []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret := make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security
// Create a session ticket that wasn't actually issued by the server.
sessionState := utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)
```
For other customizations there are following functions
```
// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
```
```
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error
```
## Contributors' guide
Please refer to [this document](./CONTRIBUTORS_GUIDE.md) if you're interested in internals
## Credits
The initial development of uTLS was completed during an internship at [Google Jigsaw](https://jigsaw.google.com/). This is not an official Google product.
+11
View File
@@ -0,0 +1,11 @@
# Security Policy
## Supported Versions
We will only maintain one branch which is the master branch. Unless otherwise requested, no security patches will be applied to older Major/Minor versions.
## Reporting a Vulnerability
For a vulnerability of low to no severity, which causing no threats to security, you may report it openly to us by [opening an issue](https://github.com/refraction-networking/utls/issues/new)
If the vulnerability you are reporting inflicts some security impact, please [do so privately](https://github.com/refraction-networking/utls/security/advisories/new).
+111
View File
@@ -0,0 +1,111 @@
// Copyright 2009 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 file.
package tls
import "strconv"
// An AlertError is a TLS alert.
//
// When using a QUIC transport, QUICConn methods will return an error
// which wraps AlertError rather than sending a TLS alert.
type AlertError uint8
func (e AlertError) Error() string {
return alert(e).String()
}
type alert uint8
const (
// alert level
alertLevelWarning = 1
alertLevelError = 2
)
const (
alertCloseNotify alert = 0
alertUnexpectedMessage alert = 10
alertBadRecordMAC alert = 20
alertDecryptionFailed alert = 21
alertRecordOverflow alert = 22
alertDecompressionFailure alert = 30
alertHandshakeFailure alert = 40
alertBadCertificate alert = 42
alertUnsupportedCertificate alert = 43
alertCertificateRevoked alert = 44
alertCertificateExpired alert = 45
alertCertificateUnknown alert = 46
alertIllegalParameter alert = 47
alertUnknownCA alert = 48
alertAccessDenied alert = 49
alertDecodeError alert = 50
alertDecryptError alert = 51
alertExportRestriction alert = 60
alertProtocolVersion alert = 70
alertInsufficientSecurity alert = 71
alertInternalError alert = 80
alertInappropriateFallback alert = 86
alertUserCanceled alert = 90
alertNoRenegotiation alert = 100
alertMissingExtension alert = 109
alertUnsupportedExtension alert = 110
alertCertificateUnobtainable alert = 111
alertUnrecognizedName alert = 112
alertBadCertificateStatusResponse alert = 113
alertBadCertificateHashValue alert = 114
alertUnknownPSKIdentity alert = 115
alertCertificateRequired alert = 116
alertNoApplicationProtocol alert = 120
alertECHRequired alert = 121
)
var alertText = map[alert]string{
alertCloseNotify: "close notify",
alertUnexpectedMessage: "unexpected message",
alertBadRecordMAC: "bad record MAC",
alertDecryptionFailed: "decryption failed",
alertRecordOverflow: "record overflow",
alertDecompressionFailure: "decompression failure",
alertHandshakeFailure: "handshake failure",
alertBadCertificate: "bad certificate",
alertUnsupportedCertificate: "unsupported certificate",
alertCertificateRevoked: "revoked certificate",
alertCertificateExpired: "expired certificate",
alertCertificateUnknown: "unknown certificate",
alertIllegalParameter: "illegal parameter",
alertUnknownCA: "unknown certificate authority",
alertAccessDenied: "access denied",
alertDecodeError: "error decoding message",
alertDecryptError: "error decrypting message",
alertExportRestriction: "export restriction",
alertProtocolVersion: "protocol version not supported",
alertInsufficientSecurity: "insufficient security level",
alertInternalError: "internal error",
alertInappropriateFallback: "inappropriate fallback",
alertUserCanceled: "user canceled",
alertNoRenegotiation: "no renegotiation",
alertMissingExtension: "missing extension",
alertUnsupportedExtension: "unsupported extension",
alertCertificateUnobtainable: "certificate unobtainable",
alertUnrecognizedName: "unrecognized name",
alertBadCertificateStatusResponse: "bad certificate status response",
alertBadCertificateHashValue: "bad certificate hash value",
alertUnknownPSKIdentity: "unknown PSK identity",
alertCertificateRequired: "certificate required",
alertNoApplicationProtocol: "no application protocol",
alertECHRequired: "encrypted client hello required",
}
func (e alert) String() string {
s, ok := alertText[e]
if ok {
return "tls: " + s
}
return "tls: alert(" + strconv.Itoa(int(e)) + ")"
}
func (e alert) Error() string {
return e.String()
}
+295
View File
@@ -0,0 +1,295 @@
// Copyright 2017 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 file.
package tls
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rsa"
"errors"
"fmt"
"hash"
"io"
)
// verifyHandshakeSignature verifies a signature against pre-hashed
// (if required) handshake contents.
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
}
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
return errors.New("ECDSA verification failure")
}
case signatureEd25519:
pubKey, ok := pubkey.(ed25519.PublicKey)
if !ok {
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
}
if !ed25519.Verify(pubKey, signed, sig) {
return errors.New("Ed25519 verification failure")
}
case signaturePKCS1v15:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
}
if err := rsa.VerifyPKCS1v15(pubKey, hashFunc, signed, sig); err != nil {
return err
}
case signatureRSAPSS:
pubKey, ok := pubkey.(*rsa.PublicKey)
if !ok {
return fmt.Errorf("expected an RSA public key, got %T", pubkey)
}
signOpts := &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash}
if err := rsa.VerifyPSS(pubKey, hashFunc, signed, sig, signOpts); err != nil {
return err
}
default:
return errors.New("internal error: unknown signature type")
}
return nil
}
const (
serverSignatureContext = "TLS 1.3, server CertificateVerify\x00"
clientSignatureContext = "TLS 1.3, client CertificateVerify\x00"
)
var signaturePadding = []byte{
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
}
// signedMessage returns the pre-hashed (if necessary) message to be signed by
// certificate keys in TLS 1.3. See RFC 8446, Section 4.4.3.
func signedMessage(sigHash crypto.Hash, context string, transcript hash.Hash) []byte {
if sigHash == directSigning {
b := &bytes.Buffer{}
b.Write(signaturePadding)
io.WriteString(b, context)
b.Write(transcript.Sum(nil))
return b.Bytes()
}
h := sigHash.New()
h.Write(signaturePadding)
io.WriteString(h, context)
h.Write(transcript.Sum(nil))
return h.Sum(nil)
}
// typeAndHashFromSignatureScheme returns the corresponding signature type and
// crypto.Hash for a given TLS SignatureScheme.
func typeAndHashFromSignatureScheme(signatureAlgorithm SignatureScheme) (sigType uint8, hash crypto.Hash, err error) {
switch signatureAlgorithm {
case PKCS1WithSHA1, PKCS1WithSHA256, PKCS1WithSHA384, PKCS1WithSHA512:
sigType = signaturePKCS1v15
case PSSWithSHA256, PSSWithSHA384, PSSWithSHA512:
sigType = signatureRSAPSS
case ECDSAWithSHA1, ECDSAWithP256AndSHA256, ECDSAWithP384AndSHA384, ECDSAWithP521AndSHA512:
sigType = signatureECDSA
case Ed25519:
sigType = signatureEd25519
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
switch signatureAlgorithm {
case PKCS1WithSHA1, ECDSAWithSHA1:
hash = crypto.SHA1
case PKCS1WithSHA256, PSSWithSHA256, ECDSAWithP256AndSHA256:
hash = crypto.SHA256
case PKCS1WithSHA384, PSSWithSHA384, ECDSAWithP384AndSHA384:
hash = crypto.SHA384
case PKCS1WithSHA512, PSSWithSHA512, ECDSAWithP521AndSHA512:
hash = crypto.SHA512
case Ed25519:
hash = directSigning
default:
return 0, 0, fmt.Errorf("unsupported signature algorithm: %v", signatureAlgorithm)
}
return sigType, hash, nil
}
// legacyTypeAndHashFromPublicKey returns the fixed signature type and crypto.Hash for
// a given public key used with TLS 1.0 and 1.1, before the introduction of
// signature algorithm negotiation.
func legacyTypeAndHashFromPublicKey(pub crypto.PublicKey) (sigType uint8, hash crypto.Hash, err error) {
switch pub.(type) {
case *rsa.PublicKey:
return signaturePKCS1v15, crypto.MD5SHA1, nil
case *ecdsa.PublicKey:
return signatureECDSA, crypto.SHA1, nil
case ed25519.PublicKey:
// RFC 8422 specifies support for Ed25519 in TLS 1.0 and 1.1,
// but it requires holding on to a handshake transcript to do a
// full signature, and not even OpenSSL bothers with the
// complexity, so we can't even test it properly.
return 0, 0, fmt.Errorf("tls: Ed25519 public keys are not supported before TLS 1.2")
default:
return 0, 0, fmt.Errorf("tls: unsupported public key: %T", pub)
}
}
var rsaSignatureSchemes = []struct {
scheme SignatureScheme
minModulusBytes int
maxVersion uint16
}{
// RSA-PSS is used with PSSSaltLengthEqualsHash, and requires
// emLen >= hLen + sLen + 2
{PSSWithSHA256, crypto.SHA256.Size()*2 + 2, VersionTLS13},
{PSSWithSHA384, crypto.SHA384.Size()*2 + 2, VersionTLS13},
{PSSWithSHA512, crypto.SHA512.Size()*2 + 2, VersionTLS13},
// PKCS #1 v1.5 uses prefixes from hashPrefixes in crypto/rsa, and requires
// emLen >= len(prefix) + hLen + 11
// TLS 1.3 dropped support for PKCS #1 v1.5 in favor of RSA-PSS.
{PKCS1WithSHA256, 19 + crypto.SHA256.Size() + 11, VersionTLS12},
{PKCS1WithSHA384, 19 + crypto.SHA384.Size() + 11, VersionTLS12},
{PKCS1WithSHA512, 19 + crypto.SHA512.Size() + 11, VersionTLS12},
{PKCS1WithSHA1, 15 + crypto.SHA1.Size() + 11, VersionTLS12},
}
// signatureSchemesForCertificate returns the list of supported SignatureSchemes
// for a given certificate, based on the public key and the protocol version,
// and optionally filtered by its explicit SupportedSignatureAlgorithms.
//
// This function must be kept in sync with supportedSignatureAlgorithms.
// FIPS filtering is applied in the caller, selectSignatureScheme.
func signatureSchemesForCertificate(version uint16, cert *Certificate) []SignatureScheme {
priv, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return nil
}
var sigAlgs []SignatureScheme
switch pub := priv.Public().(type) {
case *ecdsa.PublicKey:
if version != VersionTLS13 {
// In TLS 1.2 and earlier, ECDSA algorithms are not
// constrained to a single curve.
sigAlgs = []SignatureScheme{
ECDSAWithP256AndSHA256,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512,
ECDSAWithSHA1,
}
break
}
switch pub.Curve {
case elliptic.P256():
sigAlgs = []SignatureScheme{ECDSAWithP256AndSHA256}
case elliptic.P384():
sigAlgs = []SignatureScheme{ECDSAWithP384AndSHA384}
case elliptic.P521():
sigAlgs = []SignatureScheme{ECDSAWithP521AndSHA512}
default:
return nil
}
case *rsa.PublicKey:
size := pub.Size()
sigAlgs = make([]SignatureScheme, 0, len(rsaSignatureSchemes))
for _, candidate := range rsaSignatureSchemes {
if size >= candidate.minModulusBytes && version <= candidate.maxVersion {
sigAlgs = append(sigAlgs, candidate.scheme)
}
}
case ed25519.PublicKey:
sigAlgs = []SignatureScheme{Ed25519}
default:
return nil
}
if cert.SupportedSignatureAlgorithms != nil {
var filteredSigAlgs []SignatureScheme
for _, sigAlg := range sigAlgs {
if isSupportedSignatureAlgorithm(sigAlg, cert.SupportedSignatureAlgorithms) {
filteredSigAlgs = append(filteredSigAlgs, sigAlg)
}
}
return filteredSigAlgs
}
return sigAlgs
}
// selectSignatureScheme picks a SignatureScheme from the peer's preference list
// that works with the selected certificate. It's only called for protocol
// versions that support signature algorithms, so TLS 1.2 and 1.3.
func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureScheme) (SignatureScheme, error) {
supportedAlgs := signatureSchemesForCertificate(vers, c)
if len(supportedAlgs) == 0 {
return 0, unsupportedCertificateError(c)
}
if len(peerAlgs) == 0 && vers == VersionTLS12 {
// For TLS 1.2, if the client didn't send signature_algorithms then we
// can assume that it supports SHA1. See RFC 5246, Section 7.4.1.4.1.
peerAlgs = []SignatureScheme{PKCS1WithSHA1, ECDSAWithSHA1}
}
// Pick signature scheme in the peer's preference order, as our
// preference order is not configurable.
for _, preferredAlg := range peerAlgs {
// [uTLS] SECTION BEGIN
// if fips140tls.Required() && !isSupportedSignatureAlgorithm(preferredAlg, defaultSupportedSignatureAlgorithmsFIPS) {
// continue
// }
// [uTLS] SECTION END
if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) {
return preferredAlg, nil
}
}
return 0, errors.New("tls: peer doesn't support any of the certificate's signature algorithms")
}
// unsupportedCertificateError returns a helpful error for certificates with
// an unsupported private key.
func unsupportedCertificateError(cert *Certificate) error {
switch cert.PrivateKey.(type) {
case rsa.PrivateKey, ecdsa.PrivateKey:
return fmt.Errorf("tls: unsupported certificate: private key is %T, expected *%T",
cert.PrivateKey, cert.PrivateKey)
case *ed25519.PrivateKey:
return fmt.Errorf("tls: unsupported certificate: private key is *ed25519.PrivateKey, expected ed25519.PrivateKey")
}
signer, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return fmt.Errorf("tls: certificate private key (%T) does not implement crypto.Signer",
cert.PrivateKey)
}
switch pub := signer.Public().(type) {
case *ecdsa.PublicKey:
switch pub.Curve {
case elliptic.P256():
case elliptic.P384():
case elliptic.P521():
default:
return fmt.Errorf("tls: unsupported certificate curve (%s)", pub.Curve.Params().Name)
}
case *rsa.PublicKey:
return fmt.Errorf("tls: certificate RSA key size too small for supported signature algorithms")
case ed25519.PublicKey:
default:
return fmt.Errorf("tls: unsupported certificate key (%T)", pub)
}
if cert.SupportedSignatureAlgorithms != nil {
return fmt.Errorf("tls: peer doesn't support the certificate custom signature algorithms")
}
return fmt.Errorf("tls: internal error: unsupported key (%T)", cert.PrivateKey)
}
+95
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 file.
package tls
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 an 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 globalCertCache = 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 an 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
}
+725
View File
@@ -0,0 +1,725 @@
// Copyright 2010 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 file.
package tls
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/hmac"
"crypto/rc4"
"crypto/sha1"
"crypto/sha256"
"fmt"
"hash"
"runtime"
_ "unsafe" // for linkname
"github.com/refraction-networking/utls/internal/boring"
"golang.org/x/sys/cpu"
"golang.org/x/crypto/chacha20poly1305"
)
// CipherSuite is a TLS cipher suite. Note that most functions in this package
// accept and expose cipher suite IDs instead of this type.
type CipherSuite struct {
ID uint16
Name string
// Supported versions is the list of TLS protocol versions that can
// negotiate this cipher suite.
SupportedVersions []uint16
// Insecure is true if the cipher suite has known security issues
// due to its primitives, design, or implementation.
Insecure bool
}
var (
supportedUpToTLS12 = []uint16{VersionTLS10, VersionTLS11, VersionTLS12}
supportedOnlyTLS12 = []uint16{VersionTLS12}
supportedOnlyTLS13 = []uint16{VersionTLS13}
)
// CipherSuites returns a list of cipher suites currently implemented by this
// package, excluding those with security issues, which are returned by
// [InsecureCipherSuites].
//
// The list is sorted by ID. Note that the default cipher suites selected by
// this package might depend on logic that can't be captured by a static list,
// and might not match those returned by this function.
func CipherSuites() []*CipherSuite {
return []*CipherSuite{
{TLS_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256", supportedOnlyTLS13, false},
{TLS_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384", supportedOnlyTLS13, false},
{TLS_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256", supportedOnlyTLS13, false},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, false},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, false},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
}
}
// InsecureCipherSuites returns a list of cipher suites currently implemented by
// this package and which have security issues.
//
// Most applications should not use the cipher suites in this list, and should
// only use those returned by [CipherSuites].
func InsecureCipherSuites() []*CipherSuite {
// This list includes RC4, CBC_SHA256, and 3DES cipher suites. See
// cipherSuitesPreferenceOrder for details.
return []*CipherSuite{
{TLS_RSA_WITH_RC4_128_SHA, "TLS_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
{TLS_RSA_WITH_AES_128_CBC_SHA, "TLS_RSA_WITH_AES_128_CBC_SHA", supportedUpToTLS12, true},
{TLS_RSA_WITH_AES_256_CBC_SHA, "TLS_RSA_WITH_AES_256_CBC_SHA", supportedUpToTLS12, true},
{TLS_RSA_WITH_AES_128_CBC_SHA256, "TLS_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
{TLS_RSA_WITH_AES_128_GCM_SHA256, "TLS_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, true},
{TLS_RSA_WITH_AES_256_GCM_SHA384, "TLS_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, true},
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, "TLS_ECDHE_RSA_WITH_RC4_128_SHA", supportedUpToTLS12, true},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true},
}
}
// CipherSuiteName returns the standard name for the passed cipher suite ID
// (e.g. "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256"), or a fallback representation
// of the ID value if the cipher suite is not implemented by this package.
func CipherSuiteName(id uint16) string {
for _, c := range CipherSuites() {
if c.ID == id {
return c.Name
}
}
for _, c := range InsecureCipherSuites() {
if c.ID == id {
return c.Name
}
}
return fmt.Sprintf("0x%04X", id)
}
const (
// suiteECDHE indicates that the cipher suite involves elliptic curve
// Diffie-Hellman. This means that it should only be selected when the
// client indicates that it supports ECC with a curve and point format
// that we're happy with.
suiteECDHE = 1 << iota
// suiteECSign indicates that the cipher suite involves an ECDSA or
// EdDSA signature and therefore may only be selected when the server's
// certificate is ECDSA or EdDSA. If this is not set then the cipher suite
// is RSA based.
suiteECSign
// suiteTLS12 indicates that the cipher suite should only be advertised
// and accepted when using TLS 1.2.
suiteTLS12
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
// handshake hash.
suiteSHA384
)
// A cipherSuite is a TLS 1.01.2 cipher suite, and defines the key exchange
// mechanism, as well as the cipher+MAC pair or the AEAD.
type cipherSuite struct {
id uint16
// the lengths, in bytes, of the key material needed for each component.
keyLen int
macLen int
ivLen int
ka func(version uint16) keyAgreement
// flags is a bitmask of the suite* values, above.
flags int
cipher func(key, iv []byte, isRead bool) any
mac func(key []byte) hash.Hash
aead func(key, fixedNonce []byte) aead
}
var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order doesn't matter.
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, 32, 0, 12, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheRSAKA, suiteECDHE | suiteTLS12, cipherAES, macSHA256, nil},
{TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdheECDSAKA, suiteECDHE | suiteECSign | suiteTLS12, cipherAES, macSHA256, nil},
{TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheRSAKA, suiteECDHE, cipherAES, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_128_GCM_SHA256, 16, 0, 4, rsaKA, suiteTLS12, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_256_GCM_SHA384, 32, 0, 4, rsaKA, suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
{TLS_RSA_WITH_AES_128_CBC_SHA256, 16, 32, 16, rsaKA, suiteTLS12, cipherAES, macSHA256, nil},
{TLS_RSA_WITH_AES_128_CBC_SHA, 16, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_RSA_WITH_AES_256_CBC_SHA, 32, 20, 16, rsaKA, 0, cipherAES, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
}
// selectCipherSuite returns the first TLS 1.01.2 cipher suite from ids which
// is also in supportedIDs and passes the ok filter.
func selectCipherSuite(ids, supportedIDs []uint16, ok func(*cipherSuite) bool) *cipherSuite {
for _, id := range ids {
candidate := cipherSuiteByID(id)
if candidate == nil || !ok(candidate) {
continue
}
for _, suppID := range supportedIDs {
if id == suppID {
return candidate
}
}
}
return nil
}
// A cipherSuiteTLS13 defines only the pair of the AEAD algorithm and hash
// algorithm to be used with HKDF. See RFC 8446, Appendix B.4.
type cipherSuiteTLS13 struct {
id uint16
keyLen int
aead func(key, fixedNonce []byte) aead
hash crypto.Hash
}
// cipherSuitesTLS13 should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
// - github.com/quic-go/quic-go
// - github.com/sagernet/quic-go
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname cipherSuitesTLS13
var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map.
{TLS_AES_128_GCM_SHA256, 16, aeadAESGCMTLS13, crypto.SHA256},
{TLS_CHACHA20_POLY1305_SHA256, 32, aeadChaCha20Poly1305, crypto.SHA256},
{TLS_AES_256_GCM_SHA384, 32, aeadAESGCMTLS13, crypto.SHA384},
}
// cipherSuitesPreferenceOrder is the order in which we'll select (on the
// server) or advertise (on the client) TLS 1.01.2 cipher suites.
//
// Cipher suites are filtered but not reordered based on the application and
// peer's preferences, meaning we'll never select a suite lower in this list if
// any higher one is available. This makes it more defensible to keep weaker
// cipher suites enabled, especially on the server side where we get the last
// word, since there are no known downgrade attacks on cipher suites selection.
//
// The list is sorted by applying the following priority rules, stopping at the
// first (most important) applicable one:
//
// - Anything else comes before RC4
//
// RC4 has practically exploitable biases. See https://www.rc4nomore.com.
//
// - Anything else comes before CBC_SHA256
//
// SHA-256 variants of the CBC ciphersuites don't implement any Lucky13
// countermeasures. See https://www.isg.rhul.ac.uk/tls/Lucky13.html and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
//
// - Anything else comes before 3DES
//
// 3DES has 64-bit blocks, which makes it fundamentally susceptible to
// birthday attacks. See https://sweet32.info.
//
// - ECDHE comes before anything else
//
// Once we got the broken stuff out of the way, the most important
// property a cipher suite can have is forward secrecy. We don't
// implement FFDHE, so that means ECDHE.
//
// - AEADs come before CBC ciphers
//
// Even with Lucky13 countermeasures, MAC-then-Encrypt CBC cipher suites
// are fundamentally fragile, and suffered from an endless sequence of
// padding oracle attacks. See https://eprint.iacr.org/2015/1129,
// https://www.imperialviolet.org/2014/12/08/poodleagain.html, and
// https://blog.cloudflare.com/yet-another-padding-oracle-in-openssl-cbc-ciphersuites/.
//
// - AES comes before ChaCha20
//
// When AES hardware is available, AES-128-GCM and AES-256-GCM are faster
// than ChaCha20Poly1305.
//
// When AES hardware is not available, AES-128-GCM is one or more of: much
// slower, way more complex, and less safe (because not constant time)
// than ChaCha20Poly1305.
//
// We use this list if we think both peers have AES hardware, and
// cipherSuitesPreferenceOrderNoAES otherwise.
//
// - AES-128 comes before AES-256
//
// The only potential advantages of AES-256 are better multi-target
// margins, and hypothetical post-quantum properties. Neither apply to
// TLS, and AES-256 is slower due to its four extra rounds (which don't
// contribute to the advantages above).
//
// - ECDSA comes before RSA
//
// The relative order of ECDSA and RSA cipher suites doesn't matter,
// as they depend on the certificate. Pick one to get a stable order.
var cipherSuitesPreferenceOrder = []uint16{
// AEADs w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
// CBC w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
// AEADs w/o ECDHE
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_256_GCM_SHA384,
// CBC w/o ECDHE
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
// 3DES
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
// CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA256,
// RC4
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_RC4_128_SHA,
}
var cipherSuitesPreferenceOrderNoAES = []uint16{
// ChaCha20Poly1305
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
// AES-GCM w/ ECDHE
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
// The rest of cipherSuitesPreferenceOrder.
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_RSA_WITH_AES_128_GCM_SHA256,
TLS_RSA_WITH_AES_256_GCM_SHA384,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_RSA_WITH_3DES_EDE_CBC_SHA,
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_RC4_128_SHA,
}
// disabledCipherSuites are not used unless explicitly listed in Config.CipherSuites.
var disabledCipherSuites = map[uint16]bool{
// CBC_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: true,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: true,
TLS_RSA_WITH_AES_128_CBC_SHA256: true,
// RC4
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: true,
TLS_ECDHE_RSA_WITH_RC4_128_SHA: true,
TLS_RSA_WITH_RC4_128_SHA: true,
}
// rsaKexCiphers contains the ciphers which use RSA based key exchange,
// which we also disable by default unless a GODEBUG is set.
var rsaKexCiphers = map[uint16]bool{
TLS_RSA_WITH_RC4_128_SHA: true,
TLS_RSA_WITH_3DES_EDE_CBC_SHA: true,
TLS_RSA_WITH_AES_128_CBC_SHA: true,
TLS_RSA_WITH_AES_256_CBC_SHA: true,
TLS_RSA_WITH_AES_128_CBC_SHA256: true,
TLS_RSA_WITH_AES_128_GCM_SHA256: true,
TLS_RSA_WITH_AES_256_GCM_SHA384: true,
}
// tdesCiphers contains 3DES ciphers,
// which we also disable by default unless a GODEBUG is set.
var tdesCiphers = map[uint16]bool{
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: true,
TLS_RSA_WITH_3DES_EDE_CBC_SHA: true,
}
var (
// Keep in sync with crypto/internal/fips140/aes/gcm.supportsAESGCM.
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH
hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le"
hasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64
)
var aesgcmCiphers = map[uint16]bool{
// TLS 1.2
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: true,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: true,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: true,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: true,
// TLS 1.3
TLS_AES_128_GCM_SHA256: true,
TLS_AES_256_GCM_SHA384: true,
}
// aesgcmPreferred returns whether the first known cipher in the preference list
// is an AES-GCM cipher, implying the peer has hardware support for it.
func aesgcmPreferred(ciphers []uint16) bool {
for _, cID := range ciphers {
if c := cipherSuiteByID(cID); c != nil {
return aesgcmCiphers[cID]
}
if c := cipherSuiteTLS13ByID(cID); c != nil {
return aesgcmCiphers[cID]
}
}
return false
}
func cipherRC4(key, iv []byte, isRead bool) any {
cipher, _ := rc4.NewCipher(key)
return cipher
}
func cipher3DES(key, iv []byte, isRead bool) any {
block, _ := des.NewTripleDESCipher(key)
if isRead {
return cipher.NewCBCDecrypter(block, iv)
}
return cipher.NewCBCEncrypter(block, iv)
}
func cipherAES(key, iv []byte, isRead bool) any {
block, _ := aes.NewCipher(key)
if isRead {
return cipher.NewCBCDecrypter(block, iv)
}
return cipher.NewCBCEncrypter(block, iv)
}
// macSHA1 returns a SHA-1 based constant time MAC.
func macSHA1(key []byte) hash.Hash {
h := sha1.New
// The BoringCrypto SHA1 does not have a constant-time
// checksum function, so don't try to use it.
if !boring.Enabled {
h = newConstantTimeHash(h)
}
return hmac.New(h, key)
}
// macSHA256 returns a SHA-256 based MAC. This is only supported in TLS 1.2 and
// is currently only used in disabled-by-default cipher suites.
func macSHA256(key []byte) hash.Hash {
return hmac.New(sha256.New, key)
}
type aead interface {
cipher.AEAD
// explicitNonceLen returns the number of bytes of explicit nonce
// included in each record. This is eight for older AEADs and
// zero for modern ones.
explicitNonceLen() int
}
const (
aeadNonceLength = 12
noncePrefixLength = 4
)
// prefixNonceAEAD wraps an AEAD and prefixes a fixed portion of the nonce to
// each call.
type prefixNonceAEAD struct {
// nonce contains the fixed part of the nonce in the first four bytes.
nonce [aeadNonceLength]byte
aead cipher.AEAD
}
func (f *prefixNonceAEAD) NonceSize() int { return aeadNonceLength - noncePrefixLength }
func (f *prefixNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *prefixNonceAEAD) explicitNonceLen() int { return f.NonceSize() }
func (f *prefixNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
copy(f.nonce[4:], nonce)
return f.aead.Seal(out, f.nonce[:], plaintext, additionalData)
}
func (f *prefixNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
copy(f.nonce[4:], nonce)
return f.aead.Open(out, f.nonce[:], ciphertext, additionalData)
}
// xorNonceAEAD wraps an AEAD by XORing in a fixed pattern to the nonce
// before each call.
type xorNonceAEAD struct {
nonceMask [aeadNonceLength]byte
aead cipher.AEAD
}
func (f *xorNonceAEAD) NonceSize() int { return 8 } // 64-bit sequence number
func (f *xorNonceAEAD) Overhead() int { return f.aead.Overhead() }
func (f *xorNonceAEAD) explicitNonceLen() int { return 0 }
func (f *xorNonceAEAD) Seal(out, nonce, plaintext, additionalData []byte) []byte {
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
result := f.aead.Seal(out, f.nonceMask[:], plaintext, additionalData)
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
return result
}
func (f *xorNonceAEAD) Open(out, nonce, ciphertext, additionalData []byte) ([]byte, error) {
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
result, err := f.aead.Open(out, f.nonceMask[:], ciphertext, additionalData)
for i, b := range nonce {
f.nonceMask[4+i] ^= b
}
return result, err
}
func aeadAESGCM(key, noncePrefix []byte) aead {
if len(noncePrefix) != noncePrefixLength {
panic("tls: internal error: wrong nonce length")
}
aes, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
var aead cipher.AEAD
if boring.Enabled {
aead, err = boring.NewGCMTLS(aes)
} else {
boring.Unreachable()
// [uTLS] SECTION BEGIN
// aead, err = gcm.NewGCMForTLS12(aes.(*fipsaes.Block))
aead, err = cipher.NewGCM(aes)
// [uTLS] SECTION END
}
if err != nil {
panic(err)
}
ret := &prefixNonceAEAD{aead: aead}
copy(ret.nonce[:], noncePrefix)
return ret
}
// aeadAESGCMTLS13 should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
// - github.com/xtls/xray-core
// - github.com/v2fly/v2ray-core
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname aeadAESGCMTLS13
func aeadAESGCMTLS13(key, nonceMask []byte) aead {
if len(nonceMask) != aeadNonceLength {
panic("tls: internal error: wrong nonce length")
}
aes, err := aes.NewCipher(key)
if err != nil {
panic(err)
}
var aead cipher.AEAD
if boring.Enabled {
aead, err = boring.NewGCMTLS13(aes)
} else {
boring.Unreachable()
// [uTLS] SECTION BEGIN
// aead, err = gcm.NewGCMForTLS13(aes.(*fipsaes.Block))
aead, err = cipher.NewGCM(aes)
// [uTLS] SECTION END
}
if err != nil {
panic(err)
}
ret := &xorNonceAEAD{aead: aead}
copy(ret.nonceMask[:], nonceMask)
return ret
}
func aeadChaCha20Poly1305(key, nonceMask []byte) aead {
if len(nonceMask) != aeadNonceLength {
panic("tls: internal error: wrong nonce length")
}
aead, err := chacha20poly1305.New(key)
if err != nil {
panic(err)
}
ret := &xorNonceAEAD{aead: aead}
copy(ret.nonceMask[:], nonceMask)
return ret
}
type constantTimeHash interface {
hash.Hash
ConstantTimeSum(b []byte) []byte
}
// cthWrapper wraps any hash.Hash that implements ConstantTimeSum, and replaces
// with that all calls to Sum. It's used to obtain a ConstantTimeSum-based HMAC.
type cthWrapper struct {
h constantTimeHash
}
func (c *cthWrapper) Size() int { return c.h.Size() }
func (c *cthWrapper) BlockSize() int { return c.h.BlockSize() }
func (c *cthWrapper) Reset() { c.h.Reset() }
func (c *cthWrapper) Write(p []byte) (int, error) { return c.h.Write(p) }
func (c *cthWrapper) Sum(b []byte) []byte { return c.h.ConstantTimeSum(b) }
func newConstantTimeHash(h func() hash.Hash) func() hash.Hash {
boring.Unreachable()
return func() hash.Hash {
return &cthWrapper{h().(constantTimeHash)}
}
}
// tls10MAC implements the TLS 1.0 MAC function. RFC 2246, Section 6.2.3.
func tls10MAC(h hash.Hash, out, seq, header, data, extra []byte) []byte {
h.Reset()
h.Write(seq)
h.Write(header)
h.Write(data)
res := h.Sum(out)
if extra != nil {
h.Write(extra)
}
return res
}
func rsaKA(version uint16) keyAgreement {
return rsaKeyAgreement{}
}
func ecdheECDSAKA(version uint16) keyAgreement {
return &ecdheKeyAgreement{
isRSA: false,
version: version,
}
}
func ecdheRSAKA(version uint16) keyAgreement {
return &ecdheKeyAgreement{
isRSA: true,
version: version,
}
}
// mutualCipherSuite returns a cipherSuite given a list of supported
// ciphersuites and the id requested by the peer.
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
for _, id := range have {
if id == want {
return cipherSuiteByID(id)
}
}
return nil
}
func cipherSuiteByID(id uint16) *cipherSuite {
for _, cipherSuite := range utlsSupportedCipherSuites {
if cipherSuite.id == id {
return cipherSuite
}
}
return nil
}
func mutualCipherSuiteTLS13(have []uint16, want uint16) *cipherSuiteTLS13 {
for _, id := range have {
if id == want {
return cipherSuiteTLS13ByID(id)
}
}
return nil
}
func cipherSuiteTLS13ByID(id uint16) *cipherSuiteTLS13 {
for _, cipherSuite := range cipherSuitesTLS13 {
if cipherSuite.id == id {
return cipherSuite
}
}
return nil
}
// A list of cipher suite IDs that are, or have been, implemented by this
// package.
//
// See https://www.iana.org/assignments/tls-parameters/tls-parameters.xml
const (
// TLS 1.0 - 1.2 cipher suites.
TLS_RSA_WITH_RC4_128_SHA uint16 = 0x0005
TLS_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0x000a
TLS_RSA_WITH_AES_128_CBC_SHA uint16 = 0x002f
TLS_RSA_WITH_AES_256_CBC_SHA uint16 = 0x0035
TLS_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0x003c
TLS_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0x009c
TLS_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0x009d
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA uint16 = 0xc007
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA uint16 = 0xc009
TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA uint16 = 0xc00a
TLS_ECDHE_RSA_WITH_RC4_128_SHA uint16 = 0xc011
TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA uint16 = 0xc012
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA uint16 = 0xc013
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA uint16 = 0xc014
TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc023
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 uint16 = 0xc027
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02f
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
// TLS 1.3 cipher suites.
TLS_AES_128_GCM_SHA256 uint16 = 0x1301
TLS_AES_256_GCM_SHA384 uint16 = 0x1302
TLS_CHACHA20_POLY1305_SHA256 uint16 = 0x1303
// TLS_FALLBACK_SCSV isn't a standard cipher suite but an indicator
// that the client is doing version fallback. See RFC 7507.
TLS_FALLBACK_SCSV uint16 = 0x5600
// Legacy names for the corresponding cipher suites with the correct _SHA256
// suffix, retained for backward compatibility.
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
)
File diff suppressed because it is too large Load Diff
+120
View File
@@ -0,0 +1,120 @@
// Code generated by "stringer -linecomment -type=SignatureScheme,CurveID,ClientAuthType -output=common_string.go"; DO NOT EDIT.
package tls
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[PKCS1WithSHA256-1025]
_ = x[PKCS1WithSHA384-1281]
_ = x[PKCS1WithSHA512-1537]
_ = x[PSSWithSHA256-2052]
_ = x[PSSWithSHA384-2053]
_ = x[PSSWithSHA512-2054]
_ = x[ECDSAWithP256AndSHA256-1027]
_ = x[ECDSAWithP384AndSHA384-1283]
_ = x[ECDSAWithP521AndSHA512-1539]
_ = x[Ed25519-2055]
_ = x[PKCS1WithSHA1-513]
_ = x[ECDSAWithSHA1-515]
}
const (
_SignatureScheme_name_0 = "PKCS1WithSHA1"
_SignatureScheme_name_1 = "ECDSAWithSHA1"
_SignatureScheme_name_2 = "PKCS1WithSHA256"
_SignatureScheme_name_3 = "ECDSAWithP256AndSHA256"
_SignatureScheme_name_4 = "PKCS1WithSHA384"
_SignatureScheme_name_5 = "ECDSAWithP384AndSHA384"
_SignatureScheme_name_6 = "PKCS1WithSHA512"
_SignatureScheme_name_7 = "ECDSAWithP521AndSHA512"
_SignatureScheme_name_8 = "PSSWithSHA256PSSWithSHA384PSSWithSHA512Ed25519"
)
var (
_SignatureScheme_index_8 = [...]uint8{0, 13, 26, 39, 46}
)
func (i SignatureScheme) String() string {
switch {
case i == 513:
return _SignatureScheme_name_0
case i == 515:
return _SignatureScheme_name_1
case i == 1025:
return _SignatureScheme_name_2
case i == 1027:
return _SignatureScheme_name_3
case i == 1281:
return _SignatureScheme_name_4
case i == 1283:
return _SignatureScheme_name_5
case i == 1537:
return _SignatureScheme_name_6
case i == 1539:
return _SignatureScheme_name_7
case 2052 <= i && i <= 2055:
i -= 2052
return _SignatureScheme_name_8[_SignatureScheme_index_8[i]:_SignatureScheme_index_8[i+1]]
default:
return "SignatureScheme(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[CurveP256-23]
_ = x[CurveP384-24]
_ = x[CurveP521-25]
_ = x[X25519-29]
_ = x[X25519MLKEM768-4588]
}
const (
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
_CurveID_name_1 = "X25519"
_CurveID_name_2 = "X25519MLKEM768"
)
var (
_CurveID_index_0 = [...]uint8{0, 9, 18, 27}
)
func (i CurveID) String() string {
switch {
case 23 <= i && i <= 25:
i -= 23
return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]]
case i == 29:
return _CurveID_name_1
case i == 4588:
return _CurveID_name_2
default:
return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")"
}
}
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[NoClientCert-0]
_ = x[RequestClientCert-1]
_ = x[RequireAnyClientCert-2]
_ = x[VerifyClientCertIfGiven-3]
_ = x[RequireAndVerifyClientCert-4]
}
const _ClientAuthType_name = "NoClientCertRequestClientCertRequireAnyClientCertVerifyClientCertIfGivenRequireAndVerifyClientCert"
var _ClientAuthType_index = [...]uint8{0, 12, 29, 49, 72, 98}
func (i ClientAuthType) String() string {
if i < 0 || i >= ClientAuthType(len(_ClientAuthType_index)-1) {
return "ClientAuthType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ClientAuthType_name[_ClientAuthType_index[i]:_ClientAuthType_index[i+1]]
}
File diff suppressed because it is too large Load Diff
+140
View File
@@ -0,0 +1,140 @@
// Copyright 2024 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 file.
package tls
import (
"slices"
_ "unsafe" // for linkname
)
// Defaults are collected in this file to allow distributions to more easily patch
// them to apply local policies.
// var tlsmlkem = godebug.New("tlsmlkem") [uTLS]
// defaultCurvePreferences is the default set of supported key exchanges, as
// well as the preference order.
func defaultCurvePreferences() []CurveID {
// [uTLS section begins]
// if tlsmlkem.Value() == "0" {
// return []CurveID{X25519, CurveP256, CurveP384, CurveP521}
// }
// [uTLS section ends]
return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521}
}
// defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that
// the code advertises as supported in a TLS 1.2+ ClientHello and in a TLS 1.2+
// CertificateRequest. The two fields are merged to match with TLS 1.3.
// Note that in TLS 1.2, the ECDSA algorithms are not constrained to P-256, etc.
var defaultSupportedSignatureAlgorithms = []SignatureScheme{
PSSWithSHA256,
ECDSAWithP256AndSHA256,
Ed25519,
PSSWithSHA384,
PSSWithSHA512,
PKCS1WithSHA256,
PKCS1WithSHA384,
PKCS1WithSHA512,
ECDSAWithP384AndSHA384,
ECDSAWithP521AndSHA512,
PKCS1WithSHA1,
ECDSAWithSHA1,
}
// [uTLS section begins]
// var tlsrsakex = godebug.New("tlsrsakex")
// var tls3des = godebug.New("tls3des")
// [uTLS section ends]
func defaultCipherSuites() []uint16 {
suites := slices.Clone(cipherSuitesPreferenceOrder)
return slices.DeleteFunc(suites, func(c uint16) bool {
return disabledCipherSuites[c] ||
// [uTLS section begins]
// tlsrsakex.Value() != "1" && rsaKexCiphers[c] ||
// tls3des.Value() != "1" && tdesCiphers[c]
rsaKexCiphers[c] ||
tdesCiphers[c]
// [uTLS section ends]
})
}
// defaultCipherSuitesTLS13 is also the preference order, since there are no
// disabled by default TLS 1.3 cipher suites. The same AES vs ChaCha20 logic as
// cipherSuitesPreferenceOrder applies.
//
// defaultCipherSuitesTLS13 should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
// - github.com/quic-go/quic-go
// - github.com/sagernet/quic-go
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname defaultCipherSuitesTLS13
var defaultCipherSuitesTLS13 = []uint16{
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
TLS_CHACHA20_POLY1305_SHA256,
}
// defaultCipherSuitesTLS13NoAES should be an internal detail,
// but widely used packages access it using linkname.
// Notable members of the hall of shame include:
// - github.com/quic-go/quic-go
// - github.com/sagernet/quic-go
//
// Do not remove or change the type signature.
// See go.dev/issue/67401.
//
//go:linkname defaultCipherSuitesTLS13NoAES
var defaultCipherSuitesTLS13NoAES = []uint16{
TLS_CHACHA20_POLY1305_SHA256,
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
}
// The FIPS-only policies below match BoringSSL's
// ssl_compliance_policy_fips_202205, which is based on NIST SP 800-52r2.
// https://cs.opensource.google/boringssl/boringssl/+/master:ssl/ssl_lib.cc;l=3289;drc=ea7a88fa
var defaultSupportedVersionsFIPS = []uint16{
VersionTLS12,
VersionTLS13,
}
// defaultCurvePreferencesFIPS are the FIPS-allowed curves,
// in preference order (most preferable first).
var defaultCurvePreferencesFIPS = []CurveID{CurveP256, CurveP384}
// defaultSupportedSignatureAlgorithmsFIPS currently are a subset of
// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1.
var defaultSupportedSignatureAlgorithmsFIPS = []SignatureScheme{
PSSWithSHA256,
PSSWithSHA384,
PSSWithSHA512,
PKCS1WithSHA256,
ECDSAWithP256AndSHA256,
PKCS1WithSHA384,
ECDSAWithP384AndSHA384,
PKCS1WithSHA512,
}
// defaultCipherSuitesFIPS are the FIPS-allowed cipher suites.
var defaultCipherSuitesFIPS = []uint16{
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
}
// defaultCipherSuitesTLS13FIPS are the FIPS-allowed cipher suites for TLS 1.3.
var defaultCipherSuitesTLS13FIPS = []uint16{
TLS_AES_128_GCM_SHA256,
TLS_AES_256_GCM_SHA384,
}
+28
View File
@@ -0,0 +1,28 @@
BSD 3-Clause License
Copyright (c) 2023, Gaukas Wang
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+12
View File
@@ -0,0 +1,12 @@
# Dict TLS
This is a vendored version of [godicttls](https://github.com/gaukas/godicttls)
Below is a copy of the original README.md
# godicttls
Dictionary for TLS written in Go providing bidirectional mapping values to their names, plus enum convenience for values.
Last Update with data fetched from [IANA](www.iana.org) in March 2023:
- Transport Layer Security (TLS) Parameters [link](https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml)
- Transport Layer Security (TLS) Extensions [link](https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml)
+118
View File
@@ -0,0 +1,118 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-6
// last updated: March 2023
const (
Alert_close_notify uint8 = 0
Alert_unexpected_message uint8 = 10
Alert_bad_record_mac uint8 = 20
Alert_decryption_failed uint8 = 21
Alert_record_overflow uint8 = 22
Alert_decompression_failure uint8 = 30
Alert_handshake_failure uint8 = 40
Alert_no_certificate uint8 = 41
Alert_bad_certificate uint8 = 42
Alert_unsupported_certificate uint8 = 43
Alert_certificate_revoked uint8 = 44
Alert_certificate_expired uint8 = 45
Alert_certificate_unknown uint8 = 46
Alert_illegal_parameter uint8 = 47
Alert_unknown_ca uint8 = 48
Alert_access_denied uint8 = 49
Alert_decode_error uint8 = 50
Alert_decrypt_error uint8 = 51
Alert_too_many_cids_requested uint8 = 52
Alert_export_restriction uint8 = 60
Alert_protocol_version uint8 = 70
Alert_insufficient_security uint8 = 71
Alert_internal_error uint8 = 80
Alert_inappropriate_fallback uint8 = 86
Alert_user_canceled uint8 = 90
Alert_no_renegotiation uint8 = 100
Alert_missing_extension uint8 = 109
Alert_unsupported_extension uint8 = 110
Alert_certificate_unobtainable uint8 = 111
Alert_unrecognized_name uint8 = 112
Alert_bad_certificate_status_response uint8 = 113
Alert_bad_certificate_hash_value uint8 = 114
Alert_unknown_psk_identity uint8 = 115
Alert_certificate_required uint8 = 116
Alert_no_application_protocol uint8 = 120
)
var DictAlertValueIndexed = map[uint8]string{
0: "close_notify",
10: "unexpected_message",
20: "bad_record_mac",
21: "decryption_failed",
22: "record_overflow",
30: "decompression_failure",
40: "handshake_failure",
41: "no_certificate",
42: "bad_certificate",
43: "unsupported_certificate",
44: "certificate_revoked",
45: "certificate_expired",
46: "certificate_unknown",
47: "illegal_parameter",
48: "unknown_ca",
49: "access_denied",
50: "decode_error",
51: "decrypt_error",
52: "too_many_cids_requested",
60: "export_restriction",
70: "protocol_version",
71: "insufficient_security",
80: "internal_error",
86: "inappropriate_fallback",
90: "user_canceled",
100: "no_renegotiation",
109: "missing_extension",
110: "unsupported_extension",
111: "certificate_unobtainable",
112: "unrecognized_name",
113: "bad_certificate_status_response",
114: "bad_certificate_hash_value",
115: "unknown_psk_identity",
116: "certificate_required",
120: "no_application_protocol",
}
var DictAlertNameIndexed = map[string]uint8{
"close_notify": 0,
"unexpected_message": 10,
"bad_record_mac": 20,
"decryption_failed": 21,
"record_overflow": 22,
"decompression_failure": 30,
"handshake_failure": 40,
"no_certificate": 41,
"bad_certificate": 42,
"unsupported_certificate": 43,
"certificate_revoked": 44,
"certificate_expired": 45,
"certificate_unknown": 46,
"illegal_parameter": 47,
"unknown_ca": 48,
"access_denied": 49,
"decode_error": 50,
"decrypt_error": 51,
"too_many_cids_requested": 52,
"export_restriction": 60,
"protocol_version": 70,
"insufficient_security": 71,
"internal_error": 80,
"inappropriate_fallback": 86,
"user_canceled": 90,
"no_renegotiation": 100,
"missing_extension": 109,
"unsupported_extension": 110,
"certificate_unobtainable": 111,
"unrecognized_name": 112,
"bad_certificate_status_response": 113,
"bad_certificate_hash_value": 114,
"unknown_psk_identity": 115,
"certificate_required": 116,
"no_application_protocol": 120,
}
@@ -0,0 +1,35 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#authorization-data
// last updated: March 2023
const (
AuthData_x509_attr_cert uint16 = 0
AuthData_saml_assertion uint16 = 1
AuthData_x509_attr_cert_url uint16 = 2
AuthData_saml_assertion_url uint16 = 3
AuthData_keynote_assertion_list uint16 = 64
AuthData_keynote_assertion_list_url uint16 = 65
AuthData_dtcp_authorization uint16 = 66
)
var DictAuthorizationDataFormatValueIndexed = map[uint16]string{
0: "x509_attr_cert",
1: "saml_assertion",
2: "x509_attr_cert_url",
3: "saml_assertion_url",
64: "keynote_assertion_list",
65: "keynote_assertion_list_url",
66: "dtcp_authorization",
}
var DictAuthorizationDataFormatNameIndexed = map[string]uint16{
"x509_attr_cert": 0,
"saml_assertion": 1,
"x509_attr_cert_url": 2,
"saml_assertion_url": 3,
"Unassigned": 0,
"keynote_assertion_list": 64,
"keynote_assertion_list_url": 65,
"dtcp_authorization": 66,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#cachedinformationtype
// last updated: March 2023
const (
CachedInformationType_cert uint8 = 1
CachedInformationType_cert_req uint8 = 2
)
var DictCachedInformationTypeValueIndexed = map[uint8]string{
1: "cert",
2: "cert_req",
}
var DictCachedInformationTypeNameIndexed = map[string]uint8{
"cert": 1,
"cert_req": 2,
}
@@ -0,0 +1,22 @@
package dicttls
// source: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-certificate-compression-algorithm-ids
// last updated: March 2023
const (
CertCompAlg_zlib uint16 = 1
CertCompAlg_brotli uint16 = 2
CertCompAlg_zstd uint16 = 3
)
var DictCertificateCompressionAlgorithmValueIndexed = map[uint16]string{
1: "zlib",
2: "brotli",
3: "zstd",
}
var DictCertificateCompressionAlgorithmNameIndexed = map[string]uint16{
"zlib": 1,
"brotli": 2,
"zstd": 3,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#certificate-status
// last updated: March 2023
const (
CertStatusType_ocsp uint8 = 1
CertStatusType_ocsp_multi uint8 = 2
)
var DictCertificateStatusTypeValueIndexed = map[uint8]string{
1: "ocsp",
2: "ocsp_multi",
}
var DictCertificateStatusTypeNameIndexed = map[string]uint8{
"ocsp": 1,
"ocsp_multi": 2,
}
@@ -0,0 +1,25 @@
package dicttls
// source: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-3
// last updated: March 2023
const (
CertType_X509 uint8 = 0
CertType_OpenPGP uint8 = 1
CertType_Raw_Public_Key uint8 = 2
CertType_1609Dot2 uint8 = 3
)
var DictCertificateTypeValueIndexed = map[uint8]string{
0: "X509",
1: "OpenPGP",
2: "Raw Public Key",
3: "1609Dot2",
}
var DictCertificateTypeNameIndexed = map[string]uint8{
"X509": 0,
"OpenPGP": 1,
"Raw Public Key": 2,
"1609Dot2": 3,
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,49 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-2
// last updated: March 2023
const (
ClientCertTypeIdentifier_rsa_sign uint8 = 1
ClientCertTypeIdentifier_dss_sign uint8 = 2
ClientCertTypeIdentifier_rsa_fixed_dh uint8 = 3
ClientCertTypeIdentifier_dss_fixed_dh uint8 = 4
ClientCertTypeIdentifier_rsa_ephemeral_dh uint8 = 5
ClientCertTypeIdentifier_dss_ephemeral_dh uint8 = 6
ClientCertTypeIdentifier_fortezza_dms uint8 = 20
ClientCertTypeIdentifier_ecdsa_sign uint8 = 64
ClientCertTypeIdentifier_rsa_fixed_ecdh uint8 = 65
ClientCertTypeIdentifier_ecdsa_fixed_ecdh uint8 = 66
ClientCertTypeIdentifier_gost_sign256 uint8 = 67
ClientCertTypeIdentifier_gost_sign512 uint8 = 68
)
var DictClientCertificateTypeIdentifierValueIndexed = map[uint8]string{
1: "rsa_sign",
2: "dss_sign",
3: "rsa_fixed_dh",
4: "dss_fixed_dh",
5: "rsa_ephemeral_dh",
6: "dss_ephemeral_dh",
20: "fortezza_dms",
64: "ecdsa_sign",
65: "rsa_fixed_ecdh",
66: "ecdsa_fixed_ecdh",
67: "gost_sign256",
68: "gost_sign512",
}
var DictClientCertificateTypeIdentifierNameIndexed = map[string]uint8{
"rsa_sign": 1,
"dss_sign": 2,
"rsa_fixed_dh": 3,
"dss_fixed_dh": 4,
"rsa_ephemeral_dh": 5,
"dss_ephemeral_dh": 6,
"fortezza_dms": 20,
"ecdsa_sign": 64,
"rsa_fixed_ecdh": 65,
"ecdsa_fixed_ecdh": 66,
"gost_sign256": 67,
"gost_sign512": 68,
}
+22
View File
@@ -0,0 +1,22 @@
package dicttls
// source: https://www.iana.org/assignments/comp-meth-ids/comp-meth-ids-2.csv
// last updated: March 2023
const (
CompMeth_NULL uint8 = 0
CompMeth_DEFLATE uint8 = 1
CompMeth_LZS uint8 = 64
)
var DictCompMethValueIndexed = map[uint8]string{
0: "NULL",
1: "DEFLATE",
64: "LZS",
}
var DictCompMethNameIndexed = map[string]uint8{
"NULL": 0,
"DEFLATE": 1,
"LZS": 64,
}
+34
View File
@@ -0,0 +1,34 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-5
// last updated: March 2023
const (
ContentType_change_cipher_spec uint8 = 20
ContentType_alert uint8 = 21
ContentType_handshake uint8 = 22
ContentType_application_data uint8 = 23
ContentType_heartbeat uint8 = 24
ContentType_tls12_cid uint8 = 25
ContentType_ACK uint8 = 26
)
var DictContentTypeValueIndexed = map[uint8]string{
20: "change_cipher_spec",
21: "alert",
22: "handshake",
23: "application_data",
24: "heartbeat",
25: "tls12_cid",
26: "ACK",
}
var DictContentTypeNameIndexed = map[string]uint8{
"change_cipher_spec": 20,
"alert": 21,
"handshake": 22,
"application_data": 23,
"heartbeat": 24,
"tls12_cid": 25,
"ACK": 26,
}
+22
View File
@@ -0,0 +1,22 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-10
// last updated: March 2023
const (
ECCurve_explicit_prime uint16 = 1
ECCurve_explicit_char2 uint16 = 2
ECCurve_named_curve uint16 = 3
)
var DictECCurveTypeValueIndexed = map[uint16]string{
1: "explicit_prime",
2: "explicit_char2",
3: "named_curve",
}
var DictECCurveTypeNameIndexed = map[string]uint16{
"explicit_prime": 1,
"explicit_char2": 2,
"named_curve": 3,
}
@@ -0,0 +1,22 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-9
// last updated: March 2023
const (
ECPoint_uncompressed uint8 = 0
ECPoint_ansiX962_compressed_prime uint8 = 1
ECPoint_ansiX962_compressed_char2 uint8 = 2
)
var DictECPointFormatValueIndexed = map[uint8]string{
0: "uncompressed",
1: "ansiX962_compressed_prime",
2: "ansiX962_compressed_char2",
}
var DictECPointFormatNameIndexed = map[string]uint8{
"uncompressed": 0,
"ansiX962_compressed_prime": 1,
"ansiX962_compressed_char2": 2,
}
+212
View File
@@ -0,0 +1,212 @@
package dicttls
// source: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#tls-extensiontype-values-1
// last updated: March 2023
const (
ExtType_server_name uint16 = 0
ExtType_max_fragment_length uint16 = 1
ExtType_client_certificate_url uint16 = 2
ExtType_trusted_ca_keys uint16 = 3
ExtType_truncated_hmac uint16 = 4
ExtType_status_request uint16 = 5
ExtType_user_mapping uint16 = 6
ExtType_client_authz uint16 = 7
ExtType_server_authz uint16 = 8
ExtType_cert_type uint16 = 9
ExtType_supported_groups uint16 = 10
ExtType_ec_point_formats uint16 = 11
ExtType_srp uint16 = 12
ExtType_signature_algorithms uint16 = 13
ExtType_use_srtp uint16 = 14
ExtType_heartbeat uint16 = 15
ExtType_application_layer_protocol_negotiation uint16 = 16
ExtType_status_request_v2 uint16 = 17
ExtType_signed_certificate_timestamp uint16 = 18
ExtType_client_certificate_type uint16 = 19
ExtType_server_certificate_type uint16 = 20
ExtType_padding uint16 = 21
ExtType_encrypt_then_mac uint16 = 22
ExtType_extended_master_secret uint16 = 23
ExtType_token_binding uint16 = 24
ExtType_cached_info uint16 = 25
ExtType_tls_lts uint16 = 26
ExtType_compress_certificate uint16 = 27
ExtType_record_size_limit uint16 = 28
ExtType_pwd_protect uint16 = 29
ExtType_pwd_clear uint16 = 30
ExtType_password_salt uint16 = 31
ExtType_ticket_pinning uint16 = 32
ExtType_tls_cert_with_extern_psk uint16 = 33
ExtType_delegated_credentials uint16 = 34 // IANA name: delegated_credentials, IETF name: delegated_credential
ExtType_session_ticket uint16 = 35
ExtType_TLMSP uint16 = 36
ExtType_TLMSP_proxying uint16 = 37
ExtType_TLMSP_delegate uint16 = 38
ExtType_supported_ekt_ciphers uint16 = 39
ExtType_pre_shared_key uint16 = 41
ExtType_early_data uint16 = 42
ExtType_supported_versions uint16 = 43
ExtType_cookie uint16 = 44
ExtType_psk_key_exchange_modes uint16 = 45
ExtType_certificate_authorities uint16 = 47
ExtType_oid_filters uint16 = 48
ExtType_post_handshake_auth uint16 = 49
ExtType_signature_algorithms_cert uint16 = 50
ExtType_key_share uint16 = 51
ExtType_transparency_info uint16 = 52
ExtType_connection_id_deprecated uint16 = 53 // deprecated
ExtType_connection_id uint16 = 54
ExtType_external_id_hash uint16 = 55
ExtType_external_session_id uint16 = 56
ExtType_quic_transport_parameters uint16 = 57
ExtType_ticket_request uint16 = 58
ExtType_dnssec_chain uint16 = 59
ExtType_renegotiation_info uint16 = 65281
)
// Not IANA assigned
const (
ExtType_next_protocol_negotiation uint16 = 13172 // https://datatracker.ietf.org/doc/html/draft-agl-tls-nextprotoneg-04
ExtType_application_settings uint16 = 17513 // https://www.ietf.org/archive/id/draft-vvv-tls-alps-01.html
ExtType_application_settings_new uint16 = 17613 // https://www.ietf.org/archive/id/draft-vvv-tls-alps-01.html
ExtType_channel_id_old uint16 = 30031 // https://datatracker.ietf.org/doc/html/draft-balfanz-tls-channelid-01
ExtType_channel_id uint16 = 30032 // https://datatracker.ietf.org/doc/html/draft-balfanz-tls-channelid-01
)
var DictExtTypeValueIndexed = map[uint16]string{
0: "server_name",
1: "max_fragment_length",
2: "client_certificate_url",
3: "trusted_ca_keys",
4: "truncated_hmac",
5: "status_request",
6: "user_mapping",
7: "client_authz",
8: "server_authz",
9: "cert_type",
10: "supported_groups",
11: "ec_point_formats",
12: "srp",
13: "signature_algorithms",
14: "use_srtp",
15: "heartbeat",
16: "application_layer_protocol_negotiation",
17: "status_request_v2",
18: "signed_certificate_timestamp",
19: "client_certificate_type",
20: "server_certificate_type",
21: "padding",
22: "encrypt_then_mac",
23: "extended_master_secret",
24: "token_binding",
25: "cached_info",
26: "tls_lts",
27: "compress_certificate",
28: "record_size_limit",
29: "pwd_protect",
30: "pwd_clear",
31: "password_salt",
32: "ticket_pinning",
33: "tls_cert_with_extern_psk",
34: "delegated_credentials", // IANA name: delegated_credentials, IETF name: delegated_credential
35: "session_ticket",
36: "TLMSP",
37: "TLMSP_proxying",
38: "TLMSP_delegate",
39: "supported_ekt_ciphers",
41: "pre_shared_key",
42: "early_data",
43: "supported_versions",
44: "cookie",
45: "psk_key_exchange_modes",
47: "certificate_authorities",
48: "oid_filters",
49: "post_handshake_auth",
50: "signature_algorithms_cert",
51: "key_share",
52: "transparency_info",
53: "connection_id_deprecated", // deprecated
54: "connection_id",
55: "external_id_hash",
56: "external_session_id",
57: "quic_transport_parameters",
58: "ticket_request",
59: "dnssec_chain",
65281: "renegotiation_info",
13172: "next_protocol_negotiation",
17513: "application_settings",
17613: "application_settings_new",
30031: "channel_id_old",
30032: "channel_id",
}
var DictExtTypeNameIndexed = map[string]uint16{
"server_name": 0,
"max_fragment_length": 1,
"client_certificate_url": 2,
"trusted_ca_keys": 3,
"truncated_hmac": 4,
"status_request": 5,
"user_mapping": 6,
"client_authz": 7,
"server_authz": 8,
"cert_type": 9,
"supported_groups": 10,
"ec_point_formats": 11,
"srp": 12,
"signature_algorithms": 13,
"use_srtp": 14,
"heartbeat": 15,
"application_layer_protocol_negotiation": 16,
"status_request_v2": 17,
"signed_certificate_timestamp": 18,
"client_certificate_type": 19,
"server_certificate_type": 20,
"padding": 21,
"encrypt_then_mac": 22,
"extended_master_secret": 23,
"token_binding": 24,
"cached_info": 25,
"tls_lts": 26,
"compress_certificate": 27,
"record_size_limit": 28,
"pwd_protect": 29,
"pwd_clear": 30,
"password_salt": 31,
"ticket_pinning": 32,
"tls_cert_with_extern_psk": 33,
"delegated_credentials": 34, // IANA name: delegated_credentials
"delegated_credential": 34, // IETF name: delegated_credential
"session_ticket": 35,
"TLMSP": 36,
"TLMSP_proxying": 37,
"TLMSP_delegate": 38,
"supported_ekt_ciphers": 39,
"pre_shared_key": 41,
"early_data": 42,
"supported_versions": 43,
"cookie": 44,
"psk_key_exchange_modes": 45,
"certificate_authorities": 47,
"oid_filters": 48,
"post_handshake_auth": 49,
"signature_algorithms_cert": 50,
"key_share": 51,
"transparency_info": 52,
"connection_id_deprecated": 53, // deprecated
"connection_id": 54,
"external_id_hash": 55,
"external_session_id": 56,
"quic_transport_parameters": 57,
"ticket_request": 58,
"dnssec_chain": 59,
"renegotiation_info": 65281,
"next_protocol_negotiation": 13172,
"application_settings": 17513,
"application_settings_new": 17613,
"channel_id_old": 30031,
"channel_id": 30032,
}
+96
View File
@@ -0,0 +1,96 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-7
// last updated: March 2023
const (
HandshakeType_hello_request uint8 = 0
HandshakeType_client_hello uint8 = 1
HandshakeType_server_hello uint8 = 2
HandshakeType_hello_verify_request uint8 = 3
HandshakeType_new_session_ticket uint8 = 4
HandshakeType_end_of_early_data uint8 = 5
HandshakeType_hello_retry_request uint8 = 6
HandshakeType_encrypted_extensions uint8 = 8
HandshakeType_request_connection_id uint8 = 9
HandshakeType_new_connection_id uint8 = 10
HandshakeType_certificate uint8 = 11
HandshakeType_server_key_exchange uint8 = 12
HandshakeType_certificate_request uint8 = 13
HandshakeType_server_hello_done uint8 = 14
HandshakeType_certificate_verify uint8 = 15
HandshakeType_client_key_exchange uint8 = 16
HandshakeType_client_certificate_request uint8 = 17
HandshakeType_finished uint8 = 20
HandshakeType_certificate_url uint8 = 21
HandshakeType_certificate_status uint8 = 22
HandshakeType_supplemental_data uint8 = 23
HandshakeType_key_update uint8 = 24
HandshakeType_compressed_certificate uint8 = 25
HandshakeType_ekt_key uint8 = 26
HandshakeType_message_hash uint8 = 254
// Not IANA assigned
HandshakeType_next_protocol uint8 = 67
)
var DictHandshakeTypeValueIndexed = map[uint8]string{
0: "hello_request",
1: "client_hello",
2: "server_hello",
3: "hello_verify_request",
4: "new_session_ticket",
5: "end_of_early_data",
6: "hello_retry_request",
7: "Unassigned",
8: "encrypted_extensions",
9: "request_connection_id",
10: "new_connection_id",
11: "certificate",
12: "server_key_exchange",
13: "certificate_request",
14: "server_hello_done",
15: "certificate_verify",
16: "client_key_exchange",
17: "client_certificate_request",
20: "finished",
21: "certificate_url",
22: "certificate_status",
23: "supplemental_data",
24: "key_update",
25: "compressed_certificate",
26: "ekt_key",
254: "message_hash",
67: "next_protocol",
}
var DictHandshakeTypeNameIndexed = map[string]uint8{
"hello_request": 0,
"client_hello": 1,
"server_hello": 2,
"hello_verify_request": 3,
"new_session_ticket": 4,
"end_of_early_data": 5,
"hello_retry_request": 6,
"encrypted_extensions": 8,
"request_connection_id": 9,
"new_connection_id": 10,
"certificate": 11,
"server_key_exchange": 12,
"certificate_request": 13,
"server_hello_done": 14,
"certificate_verify": 15,
"client_key_exchange": 16,
"client_certificate_request": 17,
"finished": 20,
"certificate_url": 21,
"certificate_status": 22,
"supplemental_data": 23,
"key_update": 24,
"compressed_certificate": 25,
"ekt_key": 26,
"message_hash": 254,
"next_protocol": 67,
}
+39
View File
@@ -0,0 +1,39 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18
// last updated: March 2023
const (
HashAlg_none uint8 = 0 // deprecated in TLS 1.3
HashAlg_md5 uint8 = 1 // deprecated in TLS 1.3
HashAlg_sha1 uint8 = 2
HashAlg_sha224 uint8 = 3 // deprecated in TLS 1.3
HashAlg_sha256 uint8 = 4
HashAlg_sha384 uint8 = 5
HashAlg_sha512 uint8 = 6
HashAlg_Intrinsic uint8 = 8
)
var DictHashAlgorithmValueIndexed = map[uint8]string{
0: "none",
1: "md5",
2: "sha1",
3: "sha224",
4: "sha256",
5: "sha384",
6: "sha512",
7: "Reserved",
8: "Intrinsic",
}
var DictHashAlgorithmNameIndexed = map[string]uint8{
"none": 0,
"md5": 1,
"sha1": 2,
"sha224": 3,
"sha256": 4,
"sha384": 5,
"sha512": 6,
"Reserved": 7,
"Intrinsic": 8,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/heartbeat-message-types.csv
// last updated: March 2023
const (
HeartbeatMessage_request uint8 = 1
HeartbeatMessage_response uint8 = 2
)
var DictHeartbeatMessageTypeValueIndexed = map[uint8]string{
1: "heartbeat_request",
2: "heartbeat_response",
}
var DictHeartbeatMessageTypeNameIndexed = map[string]uint8{
"heartbeat_request": 1,
"heartbeat_response": 2,
}
+19
View File
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/heartbeat-modes.csv
// last updated: March 2023
const (
HeartbeatMode_peer_allowed_to_send uint8 = 1
HeartbeatMode_peer_not_allowed_to_send uint8 = 2
)
var DictHeartbeatModeValueIndexed = map[uint8]string{
1: "peer_allowed_to_send",
2: "peer_not_allowed_to_send",
}
var DictHeartbeatModeNameIndexed = map[string]uint8{
"peer_allowed_to_send": 1,
"peer_not_allowed_to_send": 2,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/hpke/hpke.xhtml
// last updated: December 2023
const (
AEAD_AES_128_GCM uint16 = 0x0001 // NIST Special Publication 800-38D
AEAD_AES_256_GCM uint16 = 0x0002 // NIST Special Publication 800-38D
AEAD_CHACHA20_POLY1305 uint16 = 0x0003 // RFC 8439
AEAD_EXPORT_ONLY uint16 = 0xFFFF // RFC 9180
)
var DictAEADIdentifierValueIndexed = map[uint16]string{
0x0000: "Reserved", // RFC 9180
0x0001: "AES-128-GCM",
0x0002: "AES-256-GCM",
0x0003: "ChaCha20Poly1305",
0xFFFF: "Export-only", // RFC 9180
}
@@ -0,0 +1,24 @@
package dicttls
// source: https://www.iana.org/assignments/hpke/hpke.xhtml
// last updated: December 2023
const (
HKDF_SHA256 uint16 = 0x0001
HKDF_SHA384 uint16 = 0x0002
HKDF_SHA512 uint16 = 0x0003
)
var DictKDFIdentifierValueIndexed = map[uint16]string{
0x0000: "Reserved", // RFC 9180
0x0001: "HKDF_SHA256",
0x0002: "HKDF_SHA384",
0x0003: "HKDF_SHA512",
}
var DictKDFIdentifierNameIndexed = map[string]uint16{
"Reserved": 0x0000, // RFC 9180
"HKDF_SHA256": 0x0001,
"HKDF_SHA384": 0x0002,
"HKDF_SHA512": 0x0003,
}
@@ -0,0 +1,53 @@
package dicttls
// source: https://www.iana.org/assignments/hpke/hpke.xhtml
// last updated: December 2023
const (
DHKEM_P256_HKDF_SHA256 uint16 = 0x0010 // RFC 5869
DHKEM_P384_HKDF_SHA384 uint16 = 0x0011 // RFC 5869
DHKEM_P521_HKDF_SHA512 uint16 = 0x0012 // RFC 5869
DHKEM_CP256_HKDF_SHA256 uint16 = 0x0013 // RFC 6090
DHKEM_CP384_HKDF_SHA384 uint16 = 0x0014 // RFC 6090
DHKEM_CP521_HKDF_SHA512 uint16 = 0x0015 // RFC 6090
DHKEM_SECP256K1_HKDF_SHA256 uint16 = 0x0016 // draft-wahby-cfrg-hpke-kem-secp256k1-01
DHKEM_X25519_HKDF_SHA256 uint16 = 0x0020 // RFC 7748
DHKEM_X448_HKDF_SHA512 uint16 = 0x0021 // RFC 7748
X25519_KYBER768_DRAFT00 uint16 = 0x0030 // draft-westerbaan-cfrg-hpke-xyber768d00-02
)
var DictKEMIdentifierValueIndexed = map[uint16]string{
0x0000: "Reserved", // RFC 9180
0x0010: "DHKEM(P-256, HKDF-SHA256)",
0x0011: "DHKEM(P-384, HKDF-SHA384)",
0x0012: "DHKEM(P-521, HKDF-SHA512)",
0x0013: "DHKEM(CP-256, HKDF-SHA256)",
0x0014: "DHKEM(CP-384, HKDF-SHA384)",
0x0015: "DHKEM(CP-521, HKDF-SHA512)",
0x0016: "DHKEM(secp256k1, HKDF-SHA256)",
0x0020: "DHKEM(X25519, HKDF-SHA256)",
0x0021: "DHKEM(X448, HKDF-SHA512)",
0x0030: "X25519Kyber768Draft00",
}
var DictKEMIdentifierNameIndexed = map[string]uint16{
"Reserved": 0x0000, // RFC 9180
"DHKEM(P-256, HKDF-SHA256)": 0x0010,
"DHKEM(P-384, HKDF-SHA384)": 0x0011,
"DHKEM(P-521, HKDF-SHA512)": 0x0012,
"DHKEM(CP-256, HKDF-SHA256)": 0x0013,
"DHKEM(CP-384, HKDF-SHA384)": 0x0014,
"DHKEM(CP-521, HKDF-SHA512)": 0x0015,
"DHKEM(secp256k1, HKDF-SHA256)": 0x0016,
"DHKEM(X25519, HKDF-SHA256)": 0x0020,
"DHKEM(X448, HKDF-SHA512)": 0x0021,
"X25519Kyber768Draft00": 0x0030,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-pskkeyexchangemode.csv
// last updated: March 2023
const (
PSKKeyExchangeMode_psk_ke uint8 = 0
PSKKeyExchangeMode_psk_dhe_ke uint8 = 1
)
var DictPSKKeyExchangeModeValueIndexed = map[uint8]string{
0: "psk_ke",
1: "psk_dhe_ke",
}
var DictPSKKeyExchangeModeNameIndexed = map[string]uint8{
"psk_ke": 0,
"psk_dhe_ke": 1,
}
@@ -0,0 +1,112 @@
package dicttls
// source: https://www.iana.org/assignments/quic/quic.xhtml#quic-frame-types
// last updated: July 2023
const (
QUICFrameType_PADDING uint8 = 0x00
QUICFrameType_PING uint8 = 0x01
QUICFrameType_ACK uint8 = 0x02
QUICFrameType_ACK_ecn uint8 = 0x03
QUICFrameType_RESET_STREAM uint8 = 0x04
QUICFrameType_STOP_SENDING uint8 = 0x05
QUICFrameType_CRYPTO uint8 = 0x06
QUICFrameType_NEW_TOKEN uint8 = 0x07
QUICFrameType_STREAM uint8 = 0x08
QUICFrameType_STREAM_fin uint8 = 0x09
QUICFrameType_STREAM_len uint8 = 0x0a
QUICFrameType_STREAM_len_fin uint8 = 0x0b
QUICFrameType_STREAM_off uint8 = 0x0c
QUICFrameType_STREAM_off_fin uint8 = 0x0d
QUICFrameType_STREAM_off_len uint8 = 0x0e
QUICFrameType_STREAM_off_len_fin uint8 = 0x0f
QUICFrameType_MAX_DATA uint8 = 0x10
QUICFrameType_MAX_STREAM_DATA uint8 = 0x11
QUICFrameType_MAX_STREAMS_bidi uint8 = 0x12
QUICFrameType_MAX_STREAMS_uni uint8 = 0x13
QUICFrameType_DATA_BLOCKED uint8 = 0x14
QUICFrameType_STREAM_DATA_BLOCKED uint8 = 0x15
QUICFrameType_STREAMS_BLOCKED_bidi uint8 = 0x16
QUICFrameType_STREAMS_BLOCKED_uni uint8 = 0x17
QUICFrameType_NEW_CONNECTION_ID uint8 = 0x18
QUICFrameType_RETIRE_CONNECTION_ID uint8 = 0x19
QUICFrameType_PATH_CHALLENGE uint8 = 0x1a
QUICFrameType_PATH_RESPONSE uint8 = 0x1b
QUICFrameType_CONNECTION_CLOSE uint8 = 0x1c
QUICFrameType_CONNECTION_CLOSE_app uint8 = 0x1d
QUICFrameType_HANDSHAKE_DONE uint8 = 0x1e
QUICFrameType_DATAGRAM uint8 = 0x30 // RFC9221
QUICFrameType_DATAGRAM_len uint8 = 0x31 // RFC9221
)
var DictQUICFrameTypeValueIndexed = map[uint8]string{
0x00: "PADDING",
0x01: "PING",
0x02: "ACK",
0x03: "ACK_ecn",
0x04: "RESET_STREAM",
0x05: "STOP_SENDING",
0x06: "CRYPTO",
0x07: "NEW_TOKEN",
0x08: "STREAM",
0x09: "STREAM_fin",
0x0a: "STREAM_len",
0x0b: "STREAM_len_fin",
0x0c: "STREAM_off",
0x0d: "STREAM_off_fin",
0x0e: "STREAM_off_len",
0x0f: "STREAM_off_len_fin",
0x10: "MAX_DATA",
0x11: "MAX_STREAM_DATA",
0x12: "MAX_STREAMS_bidi",
0x13: "MAX_STREAMS_uni",
0x14: "DATA_BLOCKED",
0x15: "STREAM_DATA_BLOCKED",
0x16: "STREAMS_BLOCKED_bidi",
0x17: "STREAMS_BLOCKED_uni",
0x18: "NEW_CONNECTION_ID",
0x19: "RETIRE_CONNECTION_ID",
0x1a: "PATH_CHALLENGE",
0x1b: "PATH_RESPONSE",
0x1c: "CONNECTION_CLOSE",
0x1d: "CONNECTION_CLOSE_app",
0x1e: "HANDSHAKE_DONE",
0x30: "DATAGRAM",
0x31: "DATAGRAM_len",
}
var DictQUICFrameTypeNameIndexed = map[string]uint8{
"PADDING": 0x00,
"PING": 0x01,
"ACK": 0x02,
"ACK_ecn": 0x03,
"RESET_STREAM": 0x04,
"STOP_SENDING": 0x05,
"CRYPTO": 0x06,
"NEW_TOKEN": 0x07,
"STREAM": 0x08,
"STREAM_fin": 0x09,
"STREAM_len": 0x0a,
"STREAM_len_fin": 0x0b,
"STREAM_off": 0x0c,
"STREAM_off_fin": 0x0d,
"STREAM_off_len": 0x0e,
"STREAM_off_len_fin": 0x0f,
"MAX_DATA": 0x10,
"MAX_STREAM_DATA": 0x11,
"MAX_STREAMS_bidi": 0x12,
"MAX_STREAMS_uni": 0x13,
"DATA_BLOCKED": 0x14,
"STREAM_DATA_BLOCKED": 0x15,
"STREAMS_BLOCKED_bidi": 0x16,
"STREAMS_BLOCKED_uni": 0x17,
"NEW_CONNECTION_ID": 0x18,
"RETIRE_CONNECTION_ID": 0x19,
"PATH_CHALLENGE": 0x1a,
"PATH_RESPONSE": 0x1b,
"CONNECTION_CLOSE": 0x1c,
"CONNECTION_CLOSE_app": 0x1d,
"HANDSHAKE_DONE": 0x1e,
"DATAGRAM": 0x30,
"DATAGRAM_len": 0x31,
}
@@ -0,0 +1,70 @@
package dicttls
// source: https://www.iana.org/assignments/quic/quic.xhtml#quic-transport-error-codes
// last updated: July 2023
const (
QUICTransportErrorCode_NO_ERROR uint16 = 0x0000
QUICTransportErrorCode_INTERNAL_ERROR uint16 = 0x0001
QUICTransportErrorCode_CONNECTION_REFUSED uint16 = 0x0002
QUICTransportErrorCode_FLOW_CONTROL_ERROR uint16 = 0x0003
QUICTransportErrorCode_STREAM_LIMIT_ERROR uint16 = 0x0004
QUICTransportErrorCode_STREAM_STATE_ERROR uint16 = 0x0005
QUICTransportErrorCode_FINAL_SIZE_ERROR uint16 = 0x0006
QUICTransportErrorCode_FRAME_ENCODING_ERROR uint16 = 0x0007
QUICTransportErrorCode_TRANSPORT_PARAMETER_ERROR uint16 = 0x0008
QUICTransportErrorCode_CONNECTION_ID_LIMIT_ERROR uint16 = 0x0009
QUICTransportErrorCode_PROTOCOL_VIOLATION uint16 = 0x000A
QUICTransportErrorCode_INVALID_TOKEN uint16 = 0x000B
QUICTransportErrorCode_APPLICATION_ERROR uint16 = 0x000C
QUICTransportErrorCode_CRYPTO_BUFFER_EXCEEDED uint16 = 0x000D
QUICTransportErrorCode_KEY_UPDATE_ERROR uint16 = 0x000E
QUICTransportErrorCode_AEAD_LIMIT_REACHED uint16 = 0x000F
QUICTransportErrorCode_NO_VIABLE_PATH uint16 = 0x0010
QUICTransportErrorCode_VERSION_NEGOTIATION_ERROR uint16 = 0x0011 // RFC9368
QUICTransportErrorCode_CRYPTO_ERROR uint16 = 0x0100 // 0x0100-0x01FF, use with bitwise operator
)
var DictQUICTransportErrorCodeValueIndexed = map[uint16]string{
0x0000: "NO_ERROR",
0x0001: "INTERNAL_ERROR",
0x0002: "CONNECTION_REFUSED",
0x0003: "FLOW_CONTROL_ERROR",
0x0004: "STREAM_LIMIT_ERROR",
0x0005: "STREAM_STATE_ERROR",
0x0006: "FINAL_SIZE_ERROR",
0x0007: "FRAME_ENCODING_ERROR",
0x0008: "TRANSPORT_PARAMETER_ERROR",
0x0009: "CONNECTION_ID_LIMIT_ERROR",
0x000A: "PROTOCOL_VIOLATION",
0x000B: "INVALID_TOKEN",
0x000C: "APPLICATION_ERROR",
0x000D: "CRYPTO_BUFFER_EXCEEDED",
0x000E: "KEY_UPDATE_ERROR",
0x000F: "AEAD_LIMIT_REACHED",
0x0010: "NO_VIABLE_PATH",
0x0011: "VERSION_NEGOTIATION_ERROR",
0x0100: "CRYPTO_ERROR",
}
var DictQUICTransportErrorCodeNameIndexed = map[string]uint16{
"NO_ERROR": 0x0000,
"INTERNAL_ERROR": 0x0001,
"CONNECTION_REFUSED": 0x0002,
"FLOW_CONTROL_ERROR": 0x0003,
"STREAM_LIMIT_ERROR": 0x0004,
"STREAM_STATE_ERROR": 0x0005,
"FINAL_SIZE_ERROR": 0x0006,
"FRAME_ENCODING_ERROR": 0x0007,
"TRANSPORT_PARAMETER_ERROR": 0x0008,
"CONNECTION_ID_LIMIT_ERROR": 0x0009,
"PROTOCOL_VIOLATION": 0x000A,
"INVALID_TOKEN": 0x000B,
"APPLICATION_ERROR": 0x000C,
"CRYPTO_BUFFER_EXCEEDED": 0x000D,
"KEY_UPDATE_ERROR": 0x000E,
"AEAD_LIMIT_REACHED": 0x000F,
"NO_VIABLE_PATH": 0x0010,
"VERSION_NEGOTIATION_ERROR": 0x0011,
"CRYPTO_ERROR": 0x0100,
}
@@ -0,0 +1,91 @@
package dicttls
// source: https://www.iana.org/assignments/quic/quic.xhtml#quic-transport
// last updated: July 2023
const (
QUICTransportParameter_original_destination_connection_id uint64 = 0x00
QUICTransportParameter_max_idle_timeout uint64 = 0x01
QUICTransportParameter_stateless_reset_token uint64 = 0x02
QUICTransportParameter_max_udp_payload_size uint64 = 0x03
QUICTransportParameter_initial_max_data uint64 = 0x04
QUICTransportParameter_initial_max_stream_data_bidi_local uint64 = 0x05
QUICTransportParameter_initial_max_stream_data_bidi_remote uint64 = 0x06
QUICTransportParameter_initial_max_stream_data_uni uint64 = 0x07
QUICTransportParameter_initial_max_streams_bidi uint64 = 0x08
QUICTransportParameter_initial_max_streams_uni uint64 = 0x09
QUICTransportParameter_ack_delay_exponent uint64 = 0x0a
QUICTransportParameter_max_ack_delay uint64 = 0x0b
QUICTransportParameter_disable_active_migration uint64 = 0x0c
QUICTransportParameter_preferred_address uint64 = 0x0d
QUICTransportParameter_active_connection_id_limit uint64 = 0x0e
QUICTransportParameter_initial_source_connection_id uint64 = 0x0f
QUICTransportParameter_retry_source_connection_id uint64 = 0x10
QUICTransportParameter_version_information uint64 = 0x11 // RFC9368
QUICTransportParameter_max_datagram_frame_size uint64 = 0x20 // RFC9221
QUICTransportParameter_discard uint64 = 0x173e // David_Schinazi: Receiver silently discards. https://github.com/quicwg/base-drafts/wiki/Quantum-Readiness-test
QUICTransportParameter_google_handshake_message uint64 = 0x26ab // Google: Used to carry Google internal handshake message
QUICTransportParameter_grease_quic_bit uint64 = 0x2ab2 // RFC9287
QUICTransportParameter_initial_rtt uint64 = 0x3127 // Google: Initial RTT in microseconds
QUICTransportParameter_google_connection_options uint64 = 0x3128 // Google: Google connection options for experimentation
QUICTransportParameter_user_agent uint64 = 0x3129 // Google: User agent string (deprecated)
QUICTransportParameter_google_version uint64 = 0x4752 // Google: Google QUIC version downgrade prevention
)
var DictQUICTransportParameterValueIndexed = map[uint64]string{
0x00: "original_destination_connection_id",
0x01: "max_idle_timeout",
0x02: "stateless_reset_token",
0x03: "max_udp_payload_size",
0x04: "initial_max_data",
0x05: "initial_max_stream_data_bidi_local",
0x06: "initial_max_stream_data_bidi_remote",
0x07: "initial_max_stream_data_uni",
0x08: "initial_max_streams_bidi",
0x09: "initial_max_streams_uni",
0x0a: "ack_delay_exponent",
0x0b: "max_ack_delay",
0x0c: "disable_active_migration",
0x0d: "preferred_address",
0x0e: "active_connection_id_limit",
0x0f: "initial_source_connection_id",
0x10: "retry_source_connection_id",
0x11: "version_information",
0x20: "max_datagram_frame_size",
0x173e: "discard",
0x26ab: "google handshake message",
0x2ab2: "grease_quic_bit",
0x3127: "initial_rtt",
0x3128: "google_connection_options",
0x3129: "user_agent",
0x4752: "google_version",
}
var DictQUICTransportParameterNameIndexed = map[string]uint64{
"original_destination_connection_id": 0x00,
"max_idle_timeout": 0x01,
"stateless_reset_token": 0x02,
"max_udp_payload_size": 0x03,
"initial_max_data": 0x04,
"initial_max_stream_data_bidi_local": 0x05,
"initial_max_stream_data_bidi_remote": 0x06,
"initial_max_stream_data_uni": 0x07,
"initial_max_streams_bidi": 0x08,
"initial_max_streams_uni": 0x09,
"ack_delay_exponent": 0x0a,
"max_ack_delay": 0x0b,
"disable_active_migration": 0x0c,
"preferred_address": 0x0d,
"active_connection_id_limit": 0x0e,
"initial_source_connection_id": 0x0f,
"retry_source_connection_id": 0x10,
"version_information": 0x11,
"max_datagram_frame_size": 0x20,
"discard": 0x173e,
"google handshake message": 0x26ab,
"grease_quic_bit": 0x2ab2,
"initial_rtt": 0x3127,
"google_connection_options": 0x3128,
"user_agent": 0x3129,
"google_version": 0x4752,
}
@@ -0,0 +1,41 @@
package dicttls
// Note: values in this file was used in TLS 1.2's signature_algorithms extension
// in combination with the values in hashalgorithm.go.
// signature_algorithms extension in TLS 1.3 uses values in signaturescheme.go
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16
// last updated: March 2023
const (
SigAlg_anonymous uint8 = 0 // deprecated in TLS 1.3
SigAlg_rsa uint8 = 1
SigAlg_dsa uint8 = 2 // deprecated in TLS 1.3
SigAlg_ecdsa uint8 = 3
SigAlg_ed25519 uint8 = 7
SigAlg_ed448 uint8 = 8
SigAlg_gostr34102012_256 uint8 = 64 // value changed in TLS 1.3, to 0x0709-0x070C
SigAlg_gostr34102012_512 uint8 = 65 // value changed in TLS 1.3, to 0x070D-0x070F
)
var DictSignatureAlgorithmValueIndexed = map[uint8]string{
0: "anonymous",
1: "rsa",
2: "dsa",
3: "ecdsa",
7: "ed25519",
8: "ed448",
64: "gostr34102012_256",
65: "gostr34102012_512",
}
var DictSignatureAlgorithmNameIndexed = map[string]uint8{
"anonymous": 0,
"rsa": 1,
"dsa": 2,
"ecdsa": 3,
"ed25519": 7,
"ed448": 8,
"gostr34102012_256": 64,
"gostr34102012_512": 65,
}
+116
View File
@@ -0,0 +1,116 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-signaturescheme.csv
// last updated: March 2023
const (
SigScheme_rsa_pkcs1_sha1 uint16 = 0x0201
SigScheme_ecdsa_sha1 uint16 = 0x0203
SigScheme_rsa_pkcs1_sha256 uint16 = 0x0401
SigScheme_ecdsa_secp256r1_sha256 uint16 = 0x0403
SigScheme_rsa_pkcs1_sha256_legacy uint16 = 0x0420
SigScheme_rsa_pkcs1_sha384 uint16 = 0x0501
SigScheme_ecdsa_secp384r1_sha384 uint16 = 0x0503
SigScheme_rsa_pkcs1_sha384_legacy uint16 = 0x0520
SigScheme_rsa_pkcs1_sha512 uint16 = 0x0601
SigScheme_ecdsa_secp521r1_sha512 uint16 = 0x0603
SigScheme_rsa_pkcs1_sha512_legacy uint16 = 0x0620
SigScheme_eccsi_sha256 uint16 = 0x0704
SigScheme_iso_ibs1 uint16 = 0x0705
SigScheme_iso_ibs2 uint16 = 0x0706
SigScheme_iso_chinese_ibs uint16 = 0x0707
SigScheme_sm2sig_sm3 uint16 = 0x0708
SigScheme_gostr34102012_256a uint16 = 0x0709
SigScheme_gostr34102012_256b uint16 = 0x070A
SigScheme_gostr34102012_256c uint16 = 0x070B
SigScheme_gostr34102012_256d uint16 = 0x070C
SigScheme_gostr34102012_512a uint16 = 0x070D
SigScheme_gostr34102012_512b uint16 = 0x070E
SigScheme_gostr34102012_512c uint16 = 0x070F
SigScheme_rsa_pss_rsae_sha256 uint16 = 0x0804
SigScheme_rsa_pss_rsae_sha384 uint16 = 0x0805
SigScheme_rsa_pss_rsae_sha512 uint16 = 0x0806
SigScheme_ed25519 uint16 = 0x0807
SigScheme_ed448 uint16 = 0x0808
SigScheme_rsa_pss_pss_sha256 uint16 = 0x0809
SigScheme_rsa_pss_pss_sha384 uint16 = 0x080A
SigScheme_rsa_pss_pss_sha512 uint16 = 0x080B
SigScheme_ecdsa_brainpoolP256r1tls13_sha256 uint16 = 0x081A
SigScheme_ecdsa_brainpoolP384r1tls13_sha384 uint16 = 0x081B
SigScheme_ecdsa_brainpoolP512r1tls13_sha512 uint16 = 0x081C
)
var DictSignatureSchemeValueIndexed = map[uint16]string{
0x0201: "rsa_pkcs1_sha1",
0x0203: "ecdsa_sha1",
0x0401: "rsa_pkcs1_sha256",
0x0403: "ecdsa_secp256r1_sha256",
0x0420: "rsa_pkcs1_sha256_legacy",
0x0501: "rsa_pkcs1_sha384",
0x0503: "ecdsa_secp384r1_sha384",
0x0520: "rsa_pkcs1_sha384_legacy",
0x0601: "rsa_pkcs1_sha512",
0x0603: "ecdsa_secp521r1_sha512",
0x0620: "rsa_pkcs1_sha512_legacy",
0x0704: "eccsi_sha256",
0x0705: "iso_ibs1",
0x0706: "iso_ibs2",
0x0707: "iso_chinese_ibs",
0x0708: "sm2sig_sm3",
0x0709: "gostr34102012_256a",
0x070A: "gostr34102012_256b",
0x070B: "gostr34102012_256c",
0x070C: "gostr34102012_256d",
0x070D: "gostr34102012_512a",
0x070E: "gostr34102012_512b",
0x070F: "gostr34102012_512c",
0x0804: "rsa_pss_rsae_sha256",
0x0805: "rsa_pss_rsae_sha384",
0x0806: "rsa_pss_rsae_sha512",
0x0807: "ed25519",
0x0808: "ed448",
0x0809: "rsa_pss_pss_sha256",
0x080A: "rsa_pss_pss_sha384",
0x080B: "rsa_pss_pss_sha512",
0x081A: "ecdsa_brainpoolP256r1tls13_sha256",
0x081B: "ecdsa_brainpoolP384r1tls13_sha384",
0x081C: "ecdsa_brainpoolP512r1tls13_sha512",
}
var DictSignatureSchemeNameIndexed = map[string]uint16{
"rsa_pkcs1_sha1": 0x0201,
"Reserved for backward compatibility": 0x0202,
"ecdsa_sha1": 0x0203,
"rsa_pkcs1_sha256": 0x0401,
"ecdsa_secp256r1_sha256": 0x0403,
"rsa_pkcs1_sha256_legacy": 0x0420,
"rsa_pkcs1_sha384": 0x0501,
"ecdsa_secp384r1_sha384": 0x0503,
"rsa_pkcs1_sha384_legacy": 0x0520,
"rsa_pkcs1_sha512": 0x0601,
"ecdsa_secp521r1_sha512": 0x0603,
"rsa_pkcs1_sha512_legacy": 0x0620,
"eccsi_sha256": 0x0704,
"iso_ibs1": 0x0705,
"iso_ibs2": 0x0706,
"iso_chinese_ibs": 0x0707,
"sm2sig_sm3": 0x0708,
"gostr34102012_256a": 0x0709,
"gostr34102012_256b": 0x070A,
"gostr34102012_256c": 0x070B,
"gostr34102012_256d": 0x070C,
"gostr34102012_512a": 0x070D,
"gostr34102012_512b": 0x070E,
"gostr34102012_512c": 0x070F,
"rsa_pss_rsae_sha256": 0x0804,
"rsa_pss_rsae_sha384": 0x0805,
"rsa_pss_rsae_sha512": 0x0806,
"ed25519": 0x0807,
"ed448": 0x0808,
"rsa_pss_pss_sha256": 0x0809,
"rsa_pss_pss_sha384": 0x080A,
"rsa_pss_pss_sha512": 0x080B,
"ecdsa_brainpoolP256r1tls13_sha256": 0x081A,
"ecdsa_brainpoolP384r1tls13_sha384": 0x081B,
"ecdsa_brainpoolP512r1tls13_sha512": 0x081C,
}
@@ -0,0 +1,19 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-12
// last updated: March 2023
const (
SupplementalDataType_user_mapping_data uint16 = 0
SupplementalDataType_authz_data uint16 = 16386
)
var DictSupplementalDataFormatValueIndexed = map[uint16]string{
0: "user_mapping_data",
16386: "authz_data",
}
var DictSupplementalDataFormatNameIndexed = map[string]uint16{
"user_mapping_data": 0,
"authz_data": 16386,
}
@@ -0,0 +1,157 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
// last updated: March 2023
const (
SupportedGroups_sect163k1 uint16 = 1
SupportedGroups_sect163r1 uint16 = 2
SupportedGroups_sect163r2 uint16 = 3
SupportedGroups_sect193r1 uint16 = 4
SupportedGroups_sect193r2 uint16 = 5
SupportedGroups_sect233k1 uint16 = 6
SupportedGroups_sect233r1 uint16 = 7
SupportedGroups_sect239k1 uint16 = 8
SupportedGroups_sect283k1 uint16 = 9
SupportedGroups_sect283r1 uint16 = 10
SupportedGroups_sect409k1 uint16 = 11
SupportedGroups_sect409r1 uint16 = 12
SupportedGroups_sect571k1 uint16 = 13
SupportedGroups_sect571r1 uint16 = 14
SupportedGroups_secp160k1 uint16 = 15
SupportedGroups_secp160r1 uint16 = 16
SupportedGroups_secp160r2 uint16 = 17
SupportedGroups_secp192k1 uint16 = 18
SupportedGroups_secp192r1 uint16 = 19
SupportedGroups_secp224k1 uint16 = 20
SupportedGroups_secp224r1 uint16 = 21
SupportedGroups_secp256k1 uint16 = 22
SupportedGroups_secp256r1 uint16 = 23
SupportedGroups_secp384r1 uint16 = 24
SupportedGroups_secp521r1 uint16 = 25
SupportedGroups_brainpoolP256r1 uint16 = 26
SupportedGroups_brainpoolP384r1 uint16 = 27
SupportedGroups_brainpoolP512r1 uint16 = 28
SupportedGroups_x25519 uint16 = 29
SupportedGroups_x448 uint16 = 30
SupportedGroups_brainpoolP256r1tls13 uint16 = 31
SupportedGroups_brainpoolP384r1tls13 uint16 = 32
SupportedGroups_brainpoolP512r1tls13 uint16 = 33
SupportedGroups_GC256A uint16 = 34
SupportedGroups_GC256B uint16 = 35
SupportedGroups_GC256C uint16 = 36
SupportedGroups_GC256D uint16 = 37
SupportedGroups_GC512A uint16 = 38
SupportedGroups_GC512B uint16 = 39
SupportedGroups_GC512C uint16 = 40
SupportedGroups_curveSM2 uint16 = 41
SupportedGroups_ffdhe2048 uint16 = 256
SupportedGroups_ffdhe3072 uint16 = 257
SupportedGroups_ffdhe4096 uint16 = 258
SupportedGroups_ffdhe6144 uint16 = 259
SupportedGroups_ffdhe8192 uint16 = 260
SupportedGroups_arbitrary_explicit_prime_curves uint16 = 65281
SupportedGroups_arbitrary_explicit_char2_curves uint16 = 65282
)
var DictSupportedGroupsValueIndexed = map[uint16]string{
1: "sect163k1",
2: "sect163r1",
3: "sect163r2",
4: "sect193r1",
5: "sect193r2",
6: "sect233k1",
7: "sect233r1",
8: "sect239k1",
9: "sect283k1",
10: "sect283r1",
11: "sect409k1",
12: "sect409r1",
13: "sect571k1",
14: "sect571r1",
15: "secp160k1",
16: "secp160r1",
17: "secp160r2",
18: "secp192k1",
19: "secp192r1",
20: "secp224k1",
21: "secp224r1",
22: "secp256k1",
23: "secp256r1",
24: "secp384r1",
25: "secp521r1",
26: "brainpoolP256r1",
27: "brainpoolP384r1",
28: "brainpoolP512r1",
29: "x25519",
30: "x448",
31: "brainpoolP256r1tls13",
32: "brainpoolP384r1tls13",
33: "brainpoolP512r1tls13",
34: "GC256A",
35: "GC256B",
36: "GC256C",
37: "GC256D",
38: "GC512A",
39: "GC512B",
40: "GC512C",
41: "curveSM2",
256: "ffdhe2048",
257: "ffdhe3072",
258: "ffdhe4096",
259: "ffdhe6144",
260: "ffdhe8192",
65281: "arbitrary_explicit_prime_curves",
65282: "arbitrary_explicit_char2_curves",
}
var DictSupportedGroupsNameIndexed = map[string]uint16{
"sect163k1": 1,
"sect163r1": 2,
"sect163r2": 3,
"sect193r1": 4,
"sect193r2": 5,
"sect233k1": 6,
"sect233r1": 7,
"sect239k1": 8,
"sect283k1": 9,
"sect283r1": 10,
"sect409k1": 11,
"sect409r1": 12,
"sect571k1": 13,
"sect571r1": 14,
"secp160k1": 15,
"secp160r1": 16,
"secp160r2": 17,
"secp192k1": 18,
"secp192r1": 19,
"secp224k1": 20,
"secp224r1": 21,
"secp256k1": 22,
"secp256r1": 23,
"secp384r1": 24,
"secp521r1": 25,
"brainpoolP256r1": 26,
"brainpoolP384r1": 27,
"brainpoolP512r1": 28,
"x25519": 29,
"x448": 30,
"brainpoolP256r1tls13": 31,
"brainpoolP384r1tls13": 32,
"brainpoolP512r1tls13": 33,
"GC256A": 34,
"GC256B": 35,
"GC256C": 36,
"GC256D": 37,
"GC512A": 38,
"GC512B": 39,
"GC512C": 40,
"curveSM2": 41,
"ffdhe2048": 256,
"ffdhe3072": 257,
"ffdhe4096": 258,
"ffdhe6144": 259,
"ffdhe8192": 260,
"arbitrary_explicit_prime_curves": 65281,
"arbitrary_explicit_char2_curves": 65282,
}
@@ -0,0 +1,16 @@
package dicttls
// source: https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-14
// last updated: March 2023
const (
UserMappingType_upn_domain_hint uint8 = 64
)
var DictUserMappingTypeValueIndexed = map[uint8]string{
64: "upn_domain_hint",
}
var DictUserMappingTypeNameIndexed = map[string]uint8{
"upn_domain_hint": 64,
}
+646
View File
@@ -0,0 +1,646 @@
// Copyright 2024 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 file.
package tls
import (
"bytes"
"errors"
"fmt"
"slices"
"strings"
"github.com/refraction-networking/utls/internal/hpke"
"golang.org/x/crypto/cryptobyte"
)
// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS.
// We need this so that when we insert them into ECHConfigs the ordering
// is stable.
var sortedSupportedAEADs []uint16
func init() {
for aeadID := range hpke.SupportedAEADs {
sortedSupportedAEADs = append(sortedSupportedAEADs, aeadID)
}
slices.Sort(sortedSupportedAEADs)
}
type echCipher struct {
KDFID uint16
AEADID uint16
}
type echExtension struct {
Type uint16
Data []byte
}
type echConfig struct {
raw []byte
Version uint16
Length uint16
ConfigID uint8
KemID uint16
PublicKey []byte
SymmetricCipherSuite []echCipher
MaxNameLength uint8
PublicName []byte
Extensions []echExtension
}
var errMalformedECHConfig = errors.New("tls: malformed ECHConfigList")
func parseECHConfig(enc []byte) (skip bool, ec echConfig, err error) {
s := cryptobyte.String(enc)
ec.raw = []byte(enc)
if !s.ReadUint16(&ec.Version) {
return false, echConfig{}, errMalformedECHConfig
}
if !s.ReadUint16(&ec.Length) {
return false, echConfig{}, errMalformedECHConfig
}
if len(ec.raw) < int(ec.Length)+4 {
return false, echConfig{}, errMalformedECHConfig
}
ec.raw = ec.raw[:ec.Length+4]
if ec.Version != extensionEncryptedClientHello {
s.Skip(int(ec.Length))
return true, echConfig{}, nil
}
if !s.ReadUint8(&ec.ConfigID) {
return false, echConfig{}, errMalformedECHConfig
}
if !s.ReadUint16(&ec.KemID) {
return false, echConfig{}, errMalformedECHConfig
}
if !readUint16LengthPrefixed(&s, &ec.PublicKey) {
return false, echConfig{}, errMalformedECHConfig
}
var cipherSuites cryptobyte.String
if !s.ReadUint16LengthPrefixed(&cipherSuites) {
return false, echConfig{}, errMalformedECHConfig
}
for !cipherSuites.Empty() {
var c echCipher
if !cipherSuites.ReadUint16(&c.KDFID) {
return false, echConfig{}, errMalformedECHConfig
}
if !cipherSuites.ReadUint16(&c.AEADID) {
return false, echConfig{}, errMalformedECHConfig
}
ec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c)
}
if !s.ReadUint8(&ec.MaxNameLength) {
return false, echConfig{}, errMalformedECHConfig
}
var publicName cryptobyte.String
if !s.ReadUint8LengthPrefixed(&publicName) {
return false, echConfig{}, errMalformedECHConfig
}
ec.PublicName = publicName
var extensions cryptobyte.String
if !s.ReadUint16LengthPrefixed(&extensions) {
return false, echConfig{}, errMalformedECHConfig
}
for !extensions.Empty() {
var e echExtension
if !extensions.ReadUint16(&e.Type) {
return false, echConfig{}, errMalformedECHConfig
}
if !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) {
return false, echConfig{}, errMalformedECHConfig
}
ec.Extensions = append(ec.Extensions, e)
}
return false, ec, nil
}
// parseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a
// slice of parsed ECHConfigs, in the same order they were parsed, or an error
// if the list is malformed.
func parseECHConfigList(data []byte) ([]echConfig, error) {
s := cryptobyte.String(data)
var length uint16
if !s.ReadUint16(&length) {
return nil, errMalformedECHConfig
}
if length != uint16(len(data)-2) {
return nil, errMalformedECHConfig
}
var configs []echConfig
for len(s) > 0 {
if len(s) < 4 {
return nil, errors.New("tls: malformed ECHConfig")
}
configLen := uint16(s[2])<<8 | uint16(s[3])
skip, ec, err := parseECHConfig(s)
if err != nil {
return nil, err
}
s = s[configLen+4:]
if !skip {
configs = append(configs, ec)
}
}
return configs, nil
}
func pickECHConfig(list []echConfig) *echConfig {
for _, ec := range list {
if _, ok := hpke.SupportedKEMs[ec.KemID]; !ok {
continue
}
var validSCS bool
for _, cs := range ec.SymmetricCipherSuite {
if _, ok := hpke.SupportedAEADs[cs.AEADID]; !ok {
continue
}
if _, ok := hpke.SupportedKDFs[cs.KDFID]; !ok {
continue
}
validSCS = true
break
}
if !validSCS {
continue
}
if !validDNSName(string(ec.PublicName)) {
continue
}
var unsupportedExt bool
for _, ext := range ec.Extensions {
// If high order bit is set to 1 the extension is mandatory.
// Since we don't support any extensions, if we see a mandatory
// bit, we skip the config.
if ext.Type&uint16(1<<15) != 0 {
unsupportedExt = true
}
}
if unsupportedExt {
continue
}
return &ec
}
return nil
}
func pickECHCipherSuite(suites []echCipher) (echCipher, error) {
for _, s := range suites {
// NOTE: all of the supported AEADs and KDFs are fine, rather than
// imposing some sort of preference here, we just pick the first valid
// suite.
if _, ok := hpke.SupportedAEADs[s.AEADID]; !ok {
continue
}
if _, ok := hpke.SupportedKDFs[s.KDFID]; !ok {
continue
}
return s, nil
}
return echCipher{}, errors.New("tls: no supported symmetric ciphersuites for ECH")
}
// [uTLS SECTION BEGIN]
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
return encodeInnerClientHelloReorderOuterExts(inner, maxNameLength, nil)
}
// [uTLS SECTION END]
// func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
func encodeInnerClientHelloReorderOuterExts(inner *clientHelloMsg, maxNameLength int, outerExts []uint16) ([]byte, error) { // uTLS
h, err := inner.marshalMsgReorderOuterExts(true, outerExts)
if err != nil {
return nil, err
}
h = h[4:] // strip four byte prefix
var paddingLen int
if inner.serverName != "" {
paddingLen = max(0, maxNameLength-len(inner.serverName))
} else {
paddingLen = maxNameLength + 9
}
paddingLen = 31 - ((len(h) + paddingLen - 1) % 32)
return append(h, make([]byte, paddingLen)...), nil
}
func skipUint8LengthPrefixed(s *cryptobyte.String) bool {
var skip uint8
if !s.ReadUint8(&skip) {
return false
}
return s.Skip(int(skip))
}
func skipUint16LengthPrefixed(s *cryptobyte.String) bool {
var skip uint16
if !s.ReadUint16(&skip) {
return false
}
return s.Skip(int(skip))
}
type rawExtension struct {
extType uint16
data []byte
}
func extractRawExtensions(hello *clientHelloMsg) ([]rawExtension, error) {
s := cryptobyte.String(hello.original)
if !s.Skip(4+2+32) || // header, version, random
!skipUint8LengthPrefixed(&s) || // session ID
!skipUint16LengthPrefixed(&s) || // cipher suites
!skipUint8LengthPrefixed(&s) { // compression methods
return nil, errors.New("tls: malformed outer client hello")
}
var rawExtensions []rawExtension
var extensions cryptobyte.String
if !s.ReadUint16LengthPrefixed(&extensions) {
return nil, errors.New("tls: malformed outer client hello")
}
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) ||
!extensions.ReadUint16LengthPrefixed(&extData) {
return nil, errors.New("tls: invalid inner client hello")
}
rawExtensions = append(rawExtensions, rawExtension{extension, extData})
}
return rawExtensions, nil
}
func decodeInnerClientHello(outer *clientHelloMsg, encoded []byte) (*clientHelloMsg, error) {
// Reconstructing the inner client hello from its encoded form is somewhat
// complicated. It is missing its header (message type and length), session
// ID, and the extensions may be compressed. Since we need to put the
// extensions back in the same order as they were in the raw outer hello,
// and since we don't store the raw extensions, or the order we parsed them
// in, we need to reparse the raw extensions from the outer hello in order
// to properly insert them into the inner hello. This _should_ result in raw
// bytes which match the hello as it was generated by the client.
innerReader := cryptobyte.String(encoded)
var versionAndRandom, sessionID, cipherSuites, compressionMethods []byte
var extensions cryptobyte.String
if !innerReader.ReadBytes(&versionAndRandom, 2+32) ||
!readUint8LengthPrefixed(&innerReader, &sessionID) ||
len(sessionID) != 0 ||
!readUint16LengthPrefixed(&innerReader, &cipherSuites) ||
!readUint8LengthPrefixed(&innerReader, &compressionMethods) ||
!innerReader.ReadUint16LengthPrefixed(&extensions) {
return nil, errors.New("tls: invalid inner client hello")
}
// The specification says we must verify that the trailing padding is all
// zeros. This is kind of weird for TLS messages, where we generally just
// throw away any trailing garbage.
for _, p := range innerReader {
if p != 0 {
return nil, errors.New("tls: invalid inner client hello")
}
}
rawOuterExts, err := extractRawExtensions(outer)
if err != nil {
return nil, err
}
recon := cryptobyte.NewBuilder(nil)
recon.AddUint8(typeClientHello)
recon.AddUint24LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(versionAndRandom)
recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(outer.sessionId)
})
recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(cipherSuites)
})
recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(compressionMethods)
})
recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) ||
!extensions.ReadUint16LengthPrefixed(&extData) {
recon.SetError(errors.New("tls: invalid inner client hello"))
return
}
if extension == extensionECHOuterExtensions {
if !extData.ReadUint8LengthPrefixed(&extData) {
recon.SetError(errors.New("tls: invalid inner client hello"))
return
}
var i int
for !extData.Empty() {
var extType uint16
if !extData.ReadUint16(&extType) {
recon.SetError(errors.New("tls: invalid inner client hello"))
return
}
if extType == extensionEncryptedClientHello {
recon.SetError(errors.New("tls: invalid outer extensions"))
return
}
for ; i <= len(rawOuterExts); i++ {
if i == len(rawOuterExts) {
recon.SetError(errors.New("tls: invalid outer extensions"))
return
}
if rawOuterExts[i].extType == extType {
break
}
}
recon.AddUint16(rawOuterExts[i].extType)
recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(rawOuterExts[i].data)
})
}
} else {
recon.AddUint16(extension)
recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) {
recon.AddBytes(extData)
})
}
}
})
})
reconBytes, err := recon.Bytes()
if err != nil {
return nil, err
}
inner := &clientHelloMsg{}
if !inner.unmarshal(reconBytes) {
return nil, errors.New("tls: invalid reconstructed inner client hello")
}
if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) {
return nil, errInvalidECHExt
}
if len(inner.supportedVersions) != 1 || (len(inner.supportedVersions) >= 1 && inner.supportedVersions[0] != VersionTLS13) {
return nil, errors.New("tls: client sent encrypted_client_hello extension and offered incompatible versions")
}
return inner, nil
}
func decryptECHPayload(context *hpke.Receipient, hello, payload []byte) ([]byte, error) {
outerAAD := bytes.Replace(hello[4:], payload, make([]byte, len(payload)), 1)
return context.Open(outerAAD, payload)
}
func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) {
var b cryptobyte.Builder
b.AddUint8(0) // outer
b.AddUint16(kdfID)
b.AddUint16(aeadID)
b.AddUint8(id)
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(encodedKey) })
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { b.AddBytes(payload) })
return b.Bytes()
}
func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echClientContext, useKey bool) error {
var encapKey []byte
if useKey {
encapKey = ech.encapsulatedKey
}
encodedInner, err := encodeInnerClientHello(inner, int(ech.config.MaxNameLength))
if err != nil {
return err
}
// NOTE: the tag lengths for all of the supported AEADs are the same (16
// bytes), so we have hardcoded it here. If we add support for another AEAD
// with a different tag length, we will need to change this.
encryptedLen := len(encodedInner) + 16 // AEAD tag length
outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
if err != nil {
return err
}
serializedOuter, err := outer.marshal()
if err != nil {
return err
}
serializedOuter = serializedOuter[4:] // strip the four byte prefix
encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
if err != nil {
return err
}
outer.encryptedClientHello, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
if err != nil {
return err
}
return nil
}
// validDNSName is a rather rudimentary check for the validity of a DNS name.
// This is used to check if the public_name in a ECHConfig is valid when we are
// picking a config. This can be somewhat lax because even if we pick a
// valid-looking name, the DNS layer will later reject it anyway.
func validDNSName(name string) bool {
if len(name) > 253 {
return false
}
labels := strings.Split(name, ".")
if len(labels) <= 1 {
return false
}
for _, l := range labels {
labelLen := len(l)
if labelLen == 0 {
return false
}
for i, r := range l {
if r == '-' && (i == 0 || i == labelLen-1) {
return false
}
if (r < '0' || r > '9') && (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && r != '-' {
return false
}
}
}
return true
}
// ECHRejectionError is the error type returned when ECH is rejected by a remote
// server. If the server offered a ECHConfigList to use for retries, the
// RetryConfigList field will contain this list.
//
// The client may treat an ECHRejectionError with an empty set of RetryConfigs
// as a secure signal from the server.
type ECHRejectionError struct {
RetryConfigList []byte
}
func (e *ECHRejectionError) Error() string {
return "tls: server rejected ECH"
}
var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension")
var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension")
type echExtType uint8
const (
innerECHExt echExtType = 1
outerECHExt echExtType = 0
)
func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8, encap []byte, payload []byte, err error) {
data := make([]byte, len(ext))
copy(data, ext)
s := cryptobyte.String(data)
var echInt uint8
if !s.ReadUint8(&echInt) {
err = errMalformedECHExt
return
}
echType = echExtType(echInt)
if echType == innerECHExt {
if !s.Empty() {
err = errMalformedECHExt
return
}
return echType, cs, 0, nil, nil, nil
}
if echType != outerECHExt {
err = errInvalidECHExt
return
}
if !s.ReadUint16(&cs.KDFID) {
err = errMalformedECHExt
return
}
if !s.ReadUint16(&cs.AEADID) {
err = errMalformedECHExt
return
}
if !s.ReadUint8(&configID) {
err = errMalformedECHExt
return
}
if !readUint16LengthPrefixed(&s, &encap) {
err = errMalformedECHExt
return
}
if !readUint16LengthPrefixed(&s, &payload) {
err = errMalformedECHExt
return
}
// NOTE: clone encap and payload so that mutating them does not mutate the
// raw extension bytes.
return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil
}
func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([]byte, error) {
builder := cryptobyte.NewBuilder(nil)
builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {
for _, c := range configs {
builder.AddBytes(c.Config)
}
})
return builder.Bytes()
}
func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *echServerContext, error) {
echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello)
if err != nil {
if errors.Is(err, errInvalidECHExt) {
c.sendAlert(alertIllegalParameter)
} else {
c.sendAlert(alertDecodeError)
}
return nil, nil, errInvalidECHExt
}
if echType == innerECHExt {
return outer, &echServerContext{inner: true}, nil
}
if len(c.config.EncryptedClientHelloKeys) == 0 {
return outer, nil, nil
}
for _, echKey := range c.config.EncryptedClientHelloKeys {
skip, config, err := parseECHConfig(echKey.Config)
if err != nil || skip {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", err)
}
if skip {
continue
}
echPriv, err := hpke.ParseHPKEPrivateKey(config.KemID, echKey.PrivateKey)
if err != nil {
c.sendAlert(alertInternalError)
return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", err)
}
info := append([]byte("tls ech\x00"), echKey.Config...)
hpkeContext, err := hpke.SetupReceipient(hpke.DHKEM_X25519_HKDF_SHA256, echCiphersuite.KDFID, echCiphersuite.AEADID, echPriv, info, encap)
if err != nil {
// attempt next trial decryption
continue
}
encodedInner, err := decryptECHPayload(hpkeContext, outer.original, payload)
if err != nil {
// attempt next trial decryption
continue
}
// NOTE: we do not enforce that the sent server_name matches the ECH
// configs PublicName, since this is not particularly important, and
// the client already had to know what it was in order to properly
// encrypt the payload. This is only a MAY in the spec, so we're not
// doing anything revolutionary.
echInner, err := decodeInnerClientHello(outer, encodedInner)
if err != nil {
c.sendAlert(alertIllegalParameter)
return nil, nil, errInvalidECHExt
}
c.echAccepted = true
return echInner, &echServerContext{
hpkeContext: hpkeContext,
configID: configID,
ciphersuite: echCiphersuite,
}, nil
}
return outer, nil, nil
}
func buildRetryConfigList(keys []EncryptedClientHelloKey) ([]byte, error) {
var atLeastOneRetryConfig bool
var retryBuilder cryptobyte.Builder
retryBuilder.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, c := range keys {
if !c.SendAsRetry {
continue
}
atLeastOneRetryConfig = true
b.AddBytes(c.Config)
}
})
if !atLeastOneRetryConfig {
return nil, nil
}
return retryBuilder.Bytes()
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,20 @@
package boring
import (
"crypto/cipher"
"errors"
)
const Enabled bool = false
func NewGCMTLS(_ cipher.Block) (cipher.AEAD, error) {
return nil, errors.New("boring not implemented")
}
func NewGCMTLS13(_ cipher.Block) (cipher.AEAD, error) {
return nil, errors.New("boring not implemented")
}
func Unreachable() {
// do nothing
}
@@ -0,0 +1,149 @@
// Copyright 2024 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 file.
// Package byteorder provides functions for decoding and encoding
// little and big endian integer types from/to byte slices.
package byteorder
func LEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[0]) | uint16(b[1])<<8
}
func LEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
}
func LEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v),
byte(v>>8),
)
}
func LEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24
}
func LEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
}
func LEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
)
}
func LEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 |
uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56
}
func LEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v)
b[1] = byte(v >> 8)
b[2] = byte(v >> 16)
b[3] = byte(v >> 24)
b[4] = byte(v >> 32)
b[5] = byte(v >> 40)
b[6] = byte(v >> 48)
b[7] = byte(v >> 56)
}
func LEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v),
byte(v>>8),
byte(v>>16),
byte(v>>24),
byte(v>>32),
byte(v>>40),
byte(v>>48),
byte(v>>56),
)
}
func BEUint16(b []byte) uint16 {
_ = b[1] // bounds check hint to compiler; see golang.org/issue/14808
return uint16(b[1]) | uint16(b[0])<<8
}
func BEPutUint16(b []byte, v uint16) {
_ = b[1] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func BEAppendUint16(b []byte, v uint16) []byte {
return append(b,
byte(v>>8),
byte(v),
)
}
func BEUint32(b []byte) uint32 {
_ = b[3] // bounds check hint to compiler; see golang.org/issue/14808
return uint32(b[3]) | uint32(b[2])<<8 | uint32(b[1])<<16 | uint32(b[0])<<24
}
func BEPutUint32(b []byte, v uint32) {
_ = b[3] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func BEAppendUint32(b []byte, v uint32) []byte {
return append(b,
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}
func BEUint64(b []byte) uint64 {
_ = b[7] // bounds check hint to compiler; see golang.org/issue/14808
return uint64(b[7]) | uint64(b[6])<<8 | uint64(b[5])<<16 | uint64(b[4])<<24 |
uint64(b[3])<<32 | uint64(b[2])<<40 | uint64(b[1])<<48 | uint64(b[0])<<56
}
func BEPutUint64(b []byte, v uint64) {
_ = b[7] // early bounds check to guarantee safety of writes below
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}
func BEAppendUint64(b []byte, v uint64) []byte {
return append(b,
byte(v>>56),
byte(v>>48),
byte(v>>40),
byte(v>>32),
byte(v>>24),
byte(v>>16),
byte(v>>8),
byte(v),
)
}
@@ -0,0 +1,5 @@
package fips140tls
func Required() bool {
return false
}
@@ -0,0 +1,23 @@
package helper
import (
"errors"
"golang.org/x/crypto/cryptobyte"
)
// Uint8to16 converts a slice of uint8 to a slice of uint16.
// e.g. []uint8{0x00, 0x01, 0x00, 0x02} -> []uint16{0x0001, 0x0002}
func Uint8to16(in []uint8) ([]uint16, error) {
s := cryptobyte.String(in)
var out []uint16
for !s.Empty() {
var v uint16
if s.ReadUint16(&v) {
out = append(out, v)
} else {
return nil, errors.New("ReadUint16 failed")
}
}
return out, nil
}
+24
View File
@@ -0,0 +1,24 @@
package hkdf
import (
"crypto/hkdf"
"hash"
)
func Extract[H hash.Hash](h func() H, secret, salt []byte) []byte {
res, err := hkdf.Extract(h, secret, salt)
if err != nil {
panic(err)
}
return res
}
func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLength int) []byte {
res, err := hkdf.Expand(h, pseudorandomKey, info, keyLength)
if err != nil {
panic(err)
}
return res
}
+333
View File
@@ -0,0 +1,333 @@
// Copyright 2024 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 file.
package hpke
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/ecdh"
"crypto/rand"
"errors"
"math/bits"
"github.com/refraction-networking/utls/internal/byteorder"
"github.com/refraction-networking/utls/internal/hkdf"
"golang.org/x/crypto/chacha20poly1305"
)
// testingOnlyGenerateKey is only used during testing, to provide
// a fixed test key to use when checking the RFC 9180 vectors.
var testingOnlyGenerateKey func() (*ecdh.PrivateKey, error)
type hkdfKDF struct {
hash crypto.Hash
}
func (kdf *hkdfKDF) LabeledExtract(sid []byte, salt []byte, label string, inputKey []byte) []byte {
labeledIKM := make([]byte, 0, 7+len(sid)+len(label)+len(inputKey))
labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
labeledIKM = append(labeledIKM, sid...)
labeledIKM = append(labeledIKM, label...)
labeledIKM = append(labeledIKM, inputKey...)
return hkdf.Extract(kdf.hash.New, labeledIKM, salt)
}
func (kdf *hkdfKDF) LabeledExpand(suiteID []byte, randomKey []byte, label string, info []byte, length uint16) []byte {
labeledInfo := make([]byte, 0, 2+7+len(suiteID)+len(label)+len(info))
labeledInfo = byteorder.BEAppendUint16(labeledInfo, length)
labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
labeledInfo = append(labeledInfo, suiteID...)
labeledInfo = append(labeledInfo, label...)
labeledInfo = append(labeledInfo, info...)
return hkdf.Expand(kdf.hash.New, randomKey, string(labeledInfo), int(length))
}
// dhKEM implements the KEM specified in RFC 9180, Section 4.1.
type dhKEM struct {
dh ecdh.Curve
kdf hkdfKDF
suiteID []byte
nSecret uint16
}
type KemID uint16
const DHKEM_X25519_HKDF_SHA256 = 0x0020
var SupportedKEMs = map[uint16]struct {
curve ecdh.Curve
hash crypto.Hash
nSecret uint16
}{
// RFC 9180 Section 7.1
DHKEM_X25519_HKDF_SHA256: {ecdh.X25519(), crypto.SHA256, 32},
}
func newDHKem(kemID uint16) (*dhKEM, error) {
suite, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported suite ID")
}
return &dhKEM{
dh: suite.curve,
kdf: hkdfKDF{suite.hash},
suiteID: byteorder.BEAppendUint16([]byte("KEM"), kemID),
nSecret: suite.nSecret,
}, nil
}
func (dh *dhKEM) ExtractAndExpand(dhKey, kemContext []byte) []byte {
eaePRK := dh.kdf.LabeledExtract(dh.suiteID[:], nil, "eae_prk", dhKey)
return dh.kdf.LabeledExpand(dh.suiteID[:], eaePRK, "shared_secret", kemContext, dh.nSecret)
}
func (dh *dhKEM) Encap(pubRecipient *ecdh.PublicKey) (sharedSecret []byte, encapPub []byte, err error) {
var privEph *ecdh.PrivateKey
if testingOnlyGenerateKey != nil {
privEph, err = testingOnlyGenerateKey()
} else {
privEph, err = dh.dh.GenerateKey(rand.Reader)
}
if err != nil {
return nil, nil, err
}
dhVal, err := privEph.ECDH(pubRecipient)
if err != nil {
return nil, nil, err
}
encPubEph := privEph.PublicKey().Bytes()
encPubRecip := pubRecipient.Bytes()
kemContext := append(encPubEph, encPubRecip...)
return dh.ExtractAndExpand(dhVal, kemContext), encPubEph, nil
}
func (dh *dhKEM) Decap(encPubEph []byte, secRecipient *ecdh.PrivateKey) ([]byte, error) {
pubEph, err := dh.dh.NewPublicKey(encPubEph)
if err != nil {
return nil, err
}
dhVal, err := secRecipient.ECDH(pubEph)
if err != nil {
return nil, err
}
kemContext := append(encPubEph, secRecipient.PublicKey().Bytes()...)
return dh.ExtractAndExpand(dhVal, kemContext), nil
}
type context struct {
aead cipher.AEAD
sharedSecret []byte
suiteID []byte
key []byte
baseNonce []byte
exporterSecret []byte
seqNum uint128
}
type Sender struct {
*context
}
type Receipient struct {
*context
}
var aesGCMNew = func(key []byte) (cipher.AEAD, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
return cipher.NewGCM(block)
}
type AEADID uint16
const (
AEAD_AES_128_GCM = 0x0001
AEAD_AES_256_GCM = 0x0002
AEAD_ChaCha20Poly1305 = 0x0003
)
var SupportedAEADs = map[uint16]struct {
keySize int
nonceSize int
aead func([]byte) (cipher.AEAD, error)
}{
// RFC 9180, Section 7.3
AEAD_AES_128_GCM: {keySize: 16, nonceSize: 12, aead: aesGCMNew},
AEAD_AES_256_GCM: {keySize: 32, nonceSize: 12, aead: aesGCMNew},
AEAD_ChaCha20Poly1305: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New},
}
type KDFID uint16
const KDF_HKDF_SHA256 = 0x0001
var SupportedKDFs = map[uint16]func() *hkdfKDF{
// RFC 9180, Section 7.2
KDF_HKDF_SHA256: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} },
}
func newContext(sharedSecret []byte, kemID, kdfID, aeadID uint16, info []byte) (*context, error) {
sid := suiteID(kemID, kdfID, aeadID)
kdfInit, ok := SupportedKDFs[kdfID]
if !ok {
return nil, errors.New("unsupported KDF id")
}
kdf := kdfInit()
aeadInfo, ok := SupportedAEADs[aeadID]
if !ok {
return nil, errors.New("unsupported AEAD id")
}
pskIDHash := kdf.LabeledExtract(sid, nil, "psk_id_hash", nil)
infoHash := kdf.LabeledExtract(sid, nil, "info_hash", info)
ksContext := append([]byte{0}, pskIDHash...)
ksContext = append(ksContext, infoHash...)
secret := kdf.LabeledExtract(sid, sharedSecret, "secret", nil)
key := kdf.LabeledExpand(sid, secret, "key", ksContext, uint16(aeadInfo.keySize) /* Nk - key size for AEAD */)
baseNonce := kdf.LabeledExpand(sid, secret, "base_nonce", ksContext, uint16(aeadInfo.nonceSize) /* Nn - nonce size for AEAD */)
exporterSecret := kdf.LabeledExpand(sid, secret, "exp", ksContext, uint16(kdf.hash.Size()) /* Nh - hash output size of the kdf*/)
aead, err := aeadInfo.aead(key)
if err != nil {
return nil, err
}
return &context{
aead: aead,
sharedSecret: sharedSecret,
suiteID: sid,
key: key,
baseNonce: baseNonce,
exporterSecret: exporterSecret,
}, nil
}
func SetupSender(kemID, kdfID, aeadID uint16, pub *ecdh.PublicKey, info []byte) ([]byte, *Sender, error) {
kem, err := newDHKem(kemID)
if err != nil {
return nil, nil, err
}
sharedSecret, encapsulatedKey, err := kem.Encap(pub)
if err != nil {
return nil, nil, err
}
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
if err != nil {
return nil, nil, err
}
return encapsulatedKey, &Sender{context}, nil
}
func SetupReceipient(kemID, kdfID, aeadID uint16, priv *ecdh.PrivateKey, info, encPubEph []byte) (*Receipient, error) {
kem, err := newDHKem(kemID)
if err != nil {
return nil, err
}
sharedSecret, err := kem.Decap(encPubEph, priv)
if err != nil {
return nil, err
}
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
if err != nil {
return nil, err
}
return &Receipient{context}, nil
}
func (ctx *context) nextNonce() []byte {
nonce := ctx.seqNum.bytes()[16-ctx.aead.NonceSize():]
for i := range ctx.baseNonce {
nonce[i] ^= ctx.baseNonce[i]
}
return nonce
}
func (ctx *context) incrementNonce() {
// Message limit is, according to the RFC, 2^95+1, which
// is somewhat confusing, but we do as we're told.
if ctx.seqNum.bitLen() >= (ctx.aead.NonceSize()*8)-1 {
panic("message limit reached")
}
ctx.seqNum = ctx.seqNum.addOne()
}
func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) {
ciphertext := s.aead.Seal(nil, s.nextNonce(), plaintext, aad)
s.incrementNonce()
return ciphertext, nil
}
func (r *Receipient) Open(aad, ciphertext []byte) ([]byte, error) {
plaintext, err := r.aead.Open(nil, r.nextNonce(), ciphertext, aad)
if err != nil {
return nil, err
}
r.incrementNonce()
return plaintext, nil
}
func suiteID(kemID, kdfID, aeadID uint16) []byte {
suiteID := make([]byte, 0, 4+2+2+2)
suiteID = append(suiteID, []byte("HPKE")...)
suiteID = byteorder.BEAppendUint16(suiteID, kemID)
suiteID = byteorder.BEAppendUint16(suiteID, kdfID)
suiteID = byteorder.BEAppendUint16(suiteID, aeadID)
return suiteID
}
func ParseHPKEPublicKey(kemID uint16, bytes []byte) (*ecdh.PublicKey, error) {
kemInfo, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported KEM id")
}
return kemInfo.curve.NewPublicKey(bytes)
}
func ParseHPKEPrivateKey(kemID uint16, bytes []byte) (*ecdh.PrivateKey, error) {
kemInfo, ok := SupportedKEMs[kemID]
if !ok {
return nil, errors.New("unsupported KEM id")
}
return kemInfo.curve.NewPrivateKey(bytes)
}
type uint128 struct {
hi, lo uint64
}
func (u uint128) addOne() uint128 {
lo, carry := bits.Add64(u.lo, 1, 0)
return uint128{u.hi + carry, lo}
}
func (u uint128) bitLen() int {
return bits.Len64(u.hi) + bits.Len64(u.lo)
}
func (u uint128) bytes() []byte {
b := make([]byte, 16)
byteorder.BEPutUint64(b[0:], u.hi)
byteorder.BEPutUint64(b[8:], u.lo)
return b
}
@@ -0,0 +1,157 @@
// Copyright 2024 The quic-go Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file of
// the quic-go repository.
package protocol
import (
"fmt"
"time"
)
// The PacketType is the Long Header Type
type PacketType uint8
const (
// PacketTypeInitial is the packet type of an Initial packet
PacketTypeInitial PacketType = 1 + iota
// PacketTypeRetry is the packet type of a Retry packet
PacketTypeRetry
// PacketTypeHandshake is the packet type of a Handshake packet
PacketTypeHandshake
// PacketType0RTT is the packet type of a 0-RTT packet
PacketType0RTT
)
func (t PacketType) String() string {
switch t {
case PacketTypeInitial:
return "Initial"
case PacketTypeRetry:
return "Retry"
case PacketTypeHandshake:
return "Handshake"
case PacketType0RTT:
return "0-RTT Protected"
default:
return fmt.Sprintf("unknown packet type: %d", t)
}
}
type ECN uint8
const (
ECNUnsupported ECN = iota
ECNNon // 00
ECT1 // 01
ECT0 // 10
ECNCE // 11
)
func ParseECNHeaderBits(bits byte) ECN {
switch bits {
case 0:
return ECNNon
case 0b00000010:
return ECT0
case 0b00000001:
return ECT1
case 0b00000011:
return ECNCE
default:
panic("invalid ECN bits")
}
}
func (e ECN) ToHeaderBits() byte {
//nolint:exhaustive // There are only 4 values.
switch e {
case ECNNon:
return 0
case ECT0:
return 0b00000010
case ECT1:
return 0b00000001
case ECNCE:
return 0b00000011
default:
panic("ECN unsupported")
}
}
func (e ECN) String() string {
switch e {
case ECNUnsupported:
return "ECN unsupported"
case ECNNon:
return "Not-ECT"
case ECT1:
return "ECT(1)"
case ECT0:
return "ECT(0)"
case ECNCE:
return "CE"
default:
return fmt.Sprintf("invalid ECN value: %d", e)
}
}
// A ByteCount in QUIC
type ByteCount int64
// MaxByteCount is the maximum value of a ByteCount
const MaxByteCount = ByteCount(1<<62 - 1)
// InvalidByteCount is an invalid byte count
const InvalidByteCount ByteCount = -1
// A StatelessResetToken is a stateless reset token.
type StatelessResetToken [16]byte
// MaxPacketBufferSize maximum packet size of any QUIC packet, based on
// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes.
// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452.
const MaxPacketBufferSize = 1452
// MaxLargePacketBufferSize is used when using GSO
const MaxLargePacketBufferSize = 20 * 1024
// MinInitialPacketSize is the minimum size an Initial packet is required to have.
const MinInitialPacketSize = 1200
// MinUnknownVersionPacketSize is the minimum size a packet with an unknown version
// needs to have in order to trigger a Version Negotiation packet.
const MinUnknownVersionPacketSize = MinInitialPacketSize
// MinStatelessResetSize is the minimum size of a stateless reset packet that we send
const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */
// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet.
const MinConnectionIDLenInitial = 8
// DefaultAckDelayExponent is the default ack delay exponent
const DefaultAckDelayExponent = 3
// DefaultActiveConnectionIDLimit is the default active connection ID limit
const DefaultActiveConnectionIDLimit = 2
// MaxAckDelayExponent is the maximum ack delay exponent
const MaxAckDelayExponent = 20
// DefaultMaxAckDelay is the default max_ack_delay
const DefaultMaxAckDelay = 25 * time.Millisecond
// MaxMaxAckDelay is the maximum max_ack_delay
const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond
// MaxConnIDLen is the maximum length of the connection ID
const MaxConnIDLen = 20
// InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using
// AEAD_AES_128_GCM or AEAD_AES_265_GCM.
const InvalidPacketLimitAES = 1 << 52
// InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305.
const InvalidPacketLimitChaCha = 1 << 36
@@ -0,0 +1,146 @@
// Copyright 2024 The quic-go Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file of
// the quic-go repository.
package quicvarint
import (
"fmt"
"io"
"github.com/refraction-networking/utls/internal/quicvarint/protocol"
)
// taken from the QUIC draft
const (
// Min is the minimum value allowed for a QUIC varint.
Min = 0
// Max is the maximum allowed value for a QUIC varint (2^62-1).
Max = maxVarInt8
maxVarInt1 = 63
maxVarInt2 = 16383
maxVarInt4 = 1073741823
maxVarInt8 = 4611686018427387903
)
// Read reads a number in the QUIC varint format from r.
func Read(r io.ByteReader) (uint64, error) {
firstByte, err := r.ReadByte()
if err != nil {
return 0, err
}
// the first two bits of the first byte encode the length
len := 1 << ((firstByte & 0xc0) >> 6)
b1 := firstByte & (0xff - 0xc0)
if len == 1 {
return uint64(b1), nil
}
b2, err := r.ReadByte()
if err != nil {
return 0, err
}
if len == 2 {
return uint64(b2) + uint64(b1)<<8, nil
}
b3, err := r.ReadByte()
if err != nil {
return 0, err
}
b4, err := r.ReadByte()
if err != nil {
return 0, err
}
if len == 4 {
return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil
}
b5, err := r.ReadByte()
if err != nil {
return 0, err
}
b6, err := r.ReadByte()
if err != nil {
return 0, err
}
b7, err := r.ReadByte()
if err != nil {
return 0, err
}
b8, err := r.ReadByte()
if err != nil {
return 0, err
}
return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil
}
// Append appends i in the QUIC varint format.
func Append(b []byte, i uint64) []byte {
if i <= maxVarInt1 {
return append(b, uint8(i))
}
if i <= maxVarInt2 {
return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...)
}
if i <= maxVarInt4 {
return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...)
}
if i <= maxVarInt8 {
return append(b, []byte{
uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
}...)
}
panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
}
// AppendWithLen append i in the QUIC varint format with the desired length.
func AppendWithLen(b []byte, i uint64, length protocol.ByteCount) []byte {
if length != 1 && length != 2 && length != 4 && length != 8 {
panic("invalid varint length")
}
l := Len(i)
if l == length {
return Append(b, i)
}
if l > length {
panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length))
}
if length == 2 {
b = append(b, 0b01000000)
} else if length == 4 {
b = append(b, 0b10000000)
} else if length == 8 {
b = append(b, 0b11000000)
}
for j := protocol.ByteCount(1); j < length-l; j++ {
b = append(b, 0)
}
for j := protocol.ByteCount(0); j < l; j++ {
b = append(b, uint8(i>>(8*(l-1-j))))
}
return b
}
// Len determines the number of bytes that will be needed to write the number i.
func Len(i uint64) protocol.ByteCount {
if i <= maxVarInt1 {
return 1
}
if i <= maxVarInt2 {
return 2
}
if i <= maxVarInt4 {
return 4
}
if i <= maxVarInt8 {
return 8
}
// Don't use a fmt.Sprintf here to format the error message.
// The function would then exceed the inlining budget.
panic(struct {
message string
num uint64
}{"value doesn't fit into 62 bits: ", i})
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright 2024 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 file.
package tls12
import (
"crypto/hmac"
"hash"
)
// PRF implements the TLS 1.2 pseudo-random function, as defined in RFC 5246,
// Section 5 and allowed by SP 800-135, Revision 1, Section 4.2.2.
func PRF(hash func() hash.Hash, secret []byte, label string, seed []byte, keyLen int) []byte {
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
result := make([]byte, keyLen)
pHash(hash, result, secret, labelAndSeed)
return result
}
// pHash implements the P_hash function, as defined in RFC 5246, Section 5.
func pHash(hash func() hash.Hash, result, secret, seed []byte) {
h := hmac.New(hash, secret)
h.Write(seed)
a := h.Sum(nil)
for len(result) > 0 {
h.Reset()
h.Write(a)
h.Write(seed)
b := h.Sum(nil)
n := copy(result, b)
result = result[n:]
h.Reset()
h.Write(a)
a = h.Sum(nil)
}
}
const masterSecretLength = 48
const extendedMasterSecretLabel = "extended master secret"
// MasterSecret implements the TLS 1.2 extended master secret derivation, as
// defined in RFC 7627 and allowed by SP 800-135, Revision 1, Section 4.2.2.
func MasterSecret(hash func() hash.Hash, preMasterSecret, transcript []byte) []byte {
// [uTLS SECTION BEGIN]
// "The TLS 1.2 KDF is an approved KDF when the following conditions are
// satisfied: [...] (3) P_HASH uses either SHA-256, SHA-384 or SHA-512."
// h := hash()
// switch any(h).(type) {
// case *sha256.Digest:
// if h.Size() != 32 {
// fips140.RecordNonApproved()
// }
// case *sha512.Digest:
// if h.Size() != 46 && h.Size() != 64 {
// fips140.RecordNonApproved()
// }
// default:
// fips140.RecordNonApproved()
// }
// [uTLS SECTION END]
return PRF(hash, preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength)
}
+179
View File
@@ -0,0 +1,179 @@
// Copyright 2024 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 file.
// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446,
// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7.
package tls13
import (
fips140 "hash"
"github.com/refraction-networking/utls/internal/byteorder"
"github.com/refraction-networking/utls/internal/hkdf"
)
// We don't set the service indicator in this package but we delegate that to
// the underlying functions because the TLS 1.3 KDF does not have a standard of
// its own.
// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
func ExpandLabel[H fips140.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte {
if len("tls13 ")+len(label) > 255 || len(context) > 255 {
// It should be impossible for this to panic: labels are fixed strings,
// and context is either a fixed-length computed hash, or parsed from a
// field which has the same length limitation.
//
// Another reasonable approach might be to return a randomized slice if
// we encounter an error, which would break the connection, but avoid
// panicking. This would perhaps be safer but significantly more
// confusing to users.
panic("tls13: label or context too long")
}
hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context))
hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length))
hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label)))
hkdfLabel = append(hkdfLabel, "tls13 "...)
hkdfLabel = append(hkdfLabel, label...)
hkdfLabel = append(hkdfLabel, byte(len(context)))
hkdfLabel = append(hkdfLabel, context...)
return hkdf.Expand(hash, secret, string(hkdfLabel), length)
}
func extract[H fips140.Hash](hash func() H, newSecret, currentSecret []byte) []byte {
if newSecret == nil {
newSecret = make([]byte, hash().Size())
}
return hkdf.Extract(hash, newSecret, currentSecret)
}
func deriveSecret[H fips140.Hash](hash func() H, secret []byte, label string, transcript fips140.Hash) []byte {
if transcript == nil {
transcript = hash()
}
return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size())
}
const (
resumptionBinderLabel = "res binder"
clientEarlyTrafficLabel = "c e traffic"
clientHandshakeTrafficLabel = "c hs traffic"
serverHandshakeTrafficLabel = "s hs traffic"
clientApplicationTrafficLabel = "c ap traffic"
serverApplicationTrafficLabel = "s ap traffic"
earlyExporterLabel = "e exp master"
exporterLabel = "exp master"
resumptionLabel = "res master"
)
type EarlySecret struct {
secret []byte
hash func() fips140.Hash
}
func NewEarlySecret[H fips140.Hash](hash func() H, psk []byte) *EarlySecret {
return &EarlySecret{
secret: extract(hash, psk, nil),
hash: func() fips140.Hash { return hash() },
}
}
func (s *EarlySecret) ResumptionBinderKey() []byte {
return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil)
}
// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the
// early secret and the transcript up to the ClientHello.
func (s *EarlySecret) ClientEarlyTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript)
}
type HandshakeSecret struct {
secret []byte
hash func() fips140.Hash
}
func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret {
derived := deriveSecret(s.hash, s.secret, "derived", nil)
return &HandshakeSecret{
secret: extract(s.hash, sharedSecret, derived),
hash: s.hash,
}
}
// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from
// the handshake secret and the transcript up to the ServerHello.
func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript)
}
// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from
// the handshake secret and the transcript up to the ServerHello.
func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript)
}
type MasterSecret struct {
secret []byte
hash func() fips140.Hash
}
func (s *HandshakeSecret) MasterSecret() *MasterSecret {
derived := deriveSecret(s.hash, s.secret, "derived", nil)
return &MasterSecret{
secret: extract(s.hash, nil, derived),
hash: s.hash,
}
}
// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0
// from the master secret and the transcript up to the server Finished.
func (s *MasterSecret) ClientApplicationTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript)
}
// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0
// from the master secret and the transcript up to the server Finished.
func (s *MasterSecret) ServerApplicationTrafficSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript)
}
// ResumptionMasterSecret derives the resumption_master_secret from the master secret
// and the transcript up to the client Finished.
func (s *MasterSecret) ResumptionMasterSecret(transcript fips140.Hash) []byte {
return deriveSecret(s.hash, s.secret, resumptionLabel, transcript)
}
type ExporterMasterSecret struct {
secret []byte
hash func() fips140.Hash
}
// ExporterMasterSecret derives the exporter_master_secret from the master secret
// and the transcript up to the server Finished.
func (s *MasterSecret) ExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
return &ExporterMasterSecret{
secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript),
hash: s.hash,
}
}
// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret
// and the transcript up to the ClientHello.
func (s *EarlySecret) EarlyExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
return &ExporterMasterSecret{
secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript),
hash: s.hash,
}
}
func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte {
secret := deriveSecret(s.hash, s.secret, label, nil)
h := s.hash()
h.Write(context)
return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length)
}
func TestingOnlyExporterSecret(s *ExporterMasterSecret) []byte {
return s.secret
}
+31
View File
@@ -0,0 +1,31 @@
package tls13
import fips140 "hash"
func NewEarlySecretFromSecret[H fips140.Hash](hash func() H, secret []byte) *EarlySecret {
return &EarlySecret{
secret: secret,
hash: func() fips140.Hash { return hash() },
}
}
func (s *EarlySecret) Secret() []byte {
if s != nil {
return s.secret
}
return nil
}
func NewMasterSecretFromSecret[H fips140.Hash](hash func() H, secret []byte) *MasterSecret {
return &MasterSecret{
secret: secret,
hash: func() fips140.Hash { return hash() },
}
}
func (s *MasterSecret) Secret() []byte {
if s != nil {
return s.secret
}
return nil
}
+366
View File
@@ -0,0 +1,366 @@
// Copyright 2010 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 file.
package tls
import (
"crypto"
"crypto/ecdh"
"crypto/md5"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"errors"
"fmt"
"io"
)
// A keyAgreement implements the client and server side of a TLS 1.01.2 key
// agreement protocol by generating and processing key exchange messages.
type keyAgreement interface {
// On the server side, the first two methods are called in order.
// In the case that the key agreement protocol doesn't use a
// ServerKeyExchange message, generateServerKeyExchange can return nil,
// nil.
generateServerKeyExchange(*Config, *Certificate, *clientHelloMsg, *serverHelloMsg) (*serverKeyExchangeMsg, error)
processClientKeyExchange(*Config, *Certificate, *clientKeyExchangeMsg, uint16) ([]byte, error)
// On the client side, the next two methods are called in order.
// This method may not be called if the server doesn't send a
// ServerKeyExchange message.
processServerKeyExchange(*Config, *clientHelloMsg, *serverHelloMsg, *x509.Certificate, *serverKeyExchangeMsg) error
generateClientKeyExchange(*Config, *clientHelloMsg, *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error)
}
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
var errServerKeyExchange = errors.New("tls: invalid ServerKeyExchange message")
// rsaKeyAgreement implements the standard TLS key agreement where the client
// encrypts the pre-master secret to the server's public key.
type rsaKeyAgreement struct{}
func (ka rsaKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
return nil, nil
}
func (ka rsaKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) < 2 {
return nil, errClientKeyExchange
}
ciphertextLen := int(ckx.ciphertext[0])<<8 | int(ckx.ciphertext[1])
if ciphertextLen != len(ckx.ciphertext)-2 {
return nil, errClientKeyExchange
}
ciphertext := ckx.ciphertext[2:]
priv, ok := cert.PrivateKey.(crypto.Decrypter)
if !ok {
return nil, errors.New("tls: certificate private key does not implement crypto.Decrypter")
}
// Perform constant time RSA PKCS #1 v1.5 decryption
preMasterSecret, err := priv.Decrypt(config.rand(), ciphertext, &rsa.PKCS1v15DecryptOptions{SessionKeyLen: 48})
if err != nil {
return nil, err
}
// We don't check the version number in the premaster secret. For one,
// by checking it, we would leak information about the validity of the
// encrypted pre-master secret. Secondly, it provides only a small
// benefit against a downgrade attack and some implementations send the
// wrong version anyway. See the discussion at the end of section
// 7.4.7.1 of RFC 4346.
return preMasterSecret, nil
}
func (ka rsaKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
return errors.New("tls: unexpected ServerKeyExchange")
}
func (ka rsaKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
preMasterSecret := make([]byte, 48)
preMasterSecret[0] = byte(clientHello.vers >> 8)
preMasterSecret[1] = byte(clientHello.vers)
_, err := io.ReadFull(config.rand(), preMasterSecret[2:])
if err != nil {
return nil, nil, err
}
rsaKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, nil, errors.New("tls: server certificate contains incorrect key type for selected ciphersuite")
}
encrypted, err := rsa.EncryptPKCS1v15(config.rand(), rsaKey, preMasterSecret)
if err != nil {
return nil, nil, err
}
ckx := new(clientKeyExchangeMsg)
ckx.ciphertext = make([]byte, len(encrypted)+2)
ckx.ciphertext[0] = byte(len(encrypted) >> 8)
ckx.ciphertext[1] = byte(len(encrypted))
copy(ckx.ciphertext[2:], encrypted)
return preMasterSecret, ckx, nil
}
// sha1Hash calculates a SHA1 hash over the given byte slices.
func sha1Hash(slices [][]byte) []byte {
hsha1 := sha1.New()
for _, slice := range slices {
hsha1.Write(slice)
}
return hsha1.Sum(nil)
}
// md5SHA1Hash implements TLS 1.0's hybrid hash function which consists of the
// concatenation of an MD5 and SHA1 hash.
func md5SHA1Hash(slices [][]byte) []byte {
md5sha1 := make([]byte, md5.Size+sha1.Size)
hmd5 := md5.New()
for _, slice := range slices {
hmd5.Write(slice)
}
copy(md5sha1, hmd5.Sum(nil))
copy(md5sha1[md5.Size:], sha1Hash(slices))
return md5sha1
}
// hashForServerKeyExchange hashes the given slices and returns their digest
// using the given hash function (for TLS 1.2) or using a default based on
// the sigType (for earlier TLS versions). For Ed25519 signatures, which don't
// do pre-hashing, it returns the concatenation of the slices.
func hashForServerKeyExchange(sigType uint8, hashFunc crypto.Hash, version uint16, slices ...[]byte) []byte {
if sigType == signatureEd25519 {
var signed []byte
for _, slice := range slices {
signed = append(signed, slice...)
}
return signed
}
if version >= VersionTLS12 {
h := hashFunc.New()
for _, slice := range slices {
h.Write(slice)
}
digest := h.Sum(nil)
return digest
}
if sigType == signatureECDSA {
return sha1Hash(slices)
}
return md5SHA1Hash(slices)
}
// ecdheKeyAgreement implements a TLS key agreement where the server
// generates an ephemeral EC public/private key pair and signs it. The
// pre-master secret is then calculated using ECDH. The signature may
// be ECDSA, Ed25519 or RSA.
type ecdheKeyAgreement struct {
version uint16
isRSA bool
key *ecdh.PrivateKey
// ckx and preMasterSecret are generated in processServerKeyExchange
// and returned in generateClientKeyExchange.
ckx *clientKeyExchangeMsg
preMasterSecret []byte
}
func (ka *ecdheKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
var curveID CurveID
for _, c := range clientHello.supportedCurves {
if config.supportsCurve(ka.version, c) {
curveID = c
break
}
}
if curveID == 0 {
return nil, errors.New("tls: no supported elliptic curves offered")
}
if _, ok := curveForCurveID(curveID); !ok {
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
}
key, err := generateECDHEKey(config.rand(), curveID)
if err != nil {
return nil, err
}
ka.key = key
// See RFC 4492, Section 5.4.
ecdhePublic := key.PublicKey().Bytes()
serverECDHEParams := make([]byte, 1+2+1+len(ecdhePublic))
serverECDHEParams[0] = 3 // named curve
serverECDHEParams[1] = byte(curveID >> 8)
serverECDHEParams[2] = byte(curveID)
serverECDHEParams[3] = byte(len(ecdhePublic))
copy(serverECDHEParams[4:], ecdhePublic)
priv, ok := cert.PrivateKey.(crypto.Signer)
if !ok {
return nil, fmt.Errorf("tls: certificate private key of type %T does not implement crypto.Signer", cert.PrivateKey)
}
var signatureAlgorithm SignatureScheme
var sigType uint8
var sigHash crypto.Hash
if ka.version >= VersionTLS12 {
signatureAlgorithm, err = selectSignatureScheme(ka.version, cert, clientHello.supportedSignatureAlgorithms)
if err != nil {
return nil, err
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
if err != nil {
return nil, err
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(priv.Public())
if err != nil {
return nil, err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return nil, errors.New("tls: certificate cannot be used with the selected cipher suite")
}
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, hello.random, serverECDHEParams)
signOpts := crypto.SignerOpts(sigHash)
if sigType == signatureRSAPSS {
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
}
sig, err := priv.Sign(config.rand(), signed, signOpts)
if err != nil {
return nil, errors.New("tls: failed to sign ECDHE parameters: " + err.Error())
}
skx := new(serverKeyExchangeMsg)
sigAndHashLen := 0
if ka.version >= VersionTLS12 {
sigAndHashLen = 2
}
skx.key = make([]byte, len(serverECDHEParams)+sigAndHashLen+2+len(sig))
copy(skx.key, serverECDHEParams)
k := skx.key[len(serverECDHEParams):]
if ka.version >= VersionTLS12 {
k[0] = byte(signatureAlgorithm >> 8)
k[1] = byte(signatureAlgorithm)
k = k[2:]
}
k[0] = byte(len(sig) >> 8)
k[1] = byte(len(sig))
copy(k[2:], sig)
return skx, nil
}
func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
return nil, errClientKeyExchange
}
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
}
return preMasterSecret, nil
}
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
if len(skx.key) < 4 {
return errServerKeyExchange
}
if skx.key[0] != 3 { // named curve
return errors.New("tls: server selected unsupported curve")
}
curveID := CurveID(skx.key[1])<<8 | CurveID(skx.key[2])
publicLen := int(skx.key[3])
if publicLen+4 > len(skx.key) {
return errServerKeyExchange
}
serverECDHEParams := skx.key[:4+publicLen]
publicKey := serverECDHEParams[4:]
sig := skx.key[4+publicLen:]
if len(sig) < 2 {
return errServerKeyExchange
}
if _, ok := curveForCurveID(curveID); !ok {
return errors.New("tls: server selected unsupported curve")
}
key, err := generateECDHEKey(config.rand(), curveID)
if err != nil {
return err
}
ka.key = key
peerKey, err := key.Curve().NewPublicKey(publicKey)
if err != nil {
return errServerKeyExchange
}
ka.preMasterSecret, err = key.ECDH(peerKey)
if err != nil {
return errServerKeyExchange
}
ourPublicKey := key.PublicKey().Bytes()
ka.ckx = new(clientKeyExchangeMsg)
ka.ckx.ciphertext = make([]byte, 1+len(ourPublicKey))
ka.ckx.ciphertext[0] = byte(len(ourPublicKey))
copy(ka.ckx.ciphertext[1:], ourPublicKey)
var sigType uint8
var sigHash crypto.Hash
if ka.version >= VersionTLS12 {
signatureAlgorithm := SignatureScheme(sig[0])<<8 | SignatureScheme(sig[1])
sig = sig[2:]
if len(sig) < 2 {
return errServerKeyExchange
}
if !isSupportedSignatureAlgorithm(signatureAlgorithm, clientHello.supportedSignatureAlgorithms) {
return errors.New("tls: certificate used with invalid signature algorithm")
}
sigType, sigHash, err = typeAndHashFromSignatureScheme(signatureAlgorithm)
if err != nil {
return err
}
} else {
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(cert.PublicKey)
if err != nil {
return err
}
}
if (sigType == signaturePKCS1v15 || sigType == signatureRSAPSS) != ka.isRSA {
return errServerKeyExchange
}
sigLen := int(sig[0])<<8 | int(sig[1])
if sigLen+2 != len(sig) {
return errServerKeyExchange
}
sig = sig[2:]
signed := hashForServerKeyExchange(sigType, sigHash, ka.version, clientHello.random, serverHello.random, serverECDHEParams)
if err := verifyHandshakeSignature(sigType, cert.PublicKey, sigHash, signed, sig); err != nil {
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
}
return nil
}
func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
if ka.ckx == nil {
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
}
return ka.preMasterSecret, ka.ckx, nil
}
+101
View File
@@ -0,0 +1,101 @@
// Copyright 2018 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 file.
package tls
import (
"crypto/ecdh"
"crypto/hmac"
"crypto/mlkem"
"errors"
"hash"
"io"
"github.com/refraction-networking/utls/internal/tls13"
)
// This file contains the functions necessary to compute the TLS 1.3 key
// schedule. See RFC 8446, Section 7.
// nextTrafficSecret generates the next traffic secret, given the current one,
// according to RFC 8446, Section 7.2.
func (c *cipherSuiteTLS13) nextTrafficSecret(trafficSecret []byte) []byte {
return tls13.ExpandLabel(c.hash.New, trafficSecret, "traffic upd", nil, c.hash.Size())
}
// trafficKey generates traffic keys according to RFC 8446, Section 7.3.
func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) {
key = tls13.ExpandLabel(c.hash.New, trafficSecret, "key", nil, c.keyLen)
iv = tls13.ExpandLabel(c.hash.New, trafficSecret, "iv", nil, aeadNonceLength)
return
}
// finishedHash generates the Finished verify_data or PskBinderEntry according
// to RFC 8446, Section 4.4.4. See sections 4.4 and 4.2.11.2 for the baseKey
// selection.
func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) []byte {
finishedKey := tls13.ExpandLabel(c.hash.New, baseKey, "finished", nil, c.hash.Size())
verifyData := hmac.New(c.hash.New, finishedKey)
verifyData.Write(transcript.Sum(nil))
return verifyData.Sum(nil)
}
// exportKeyingMaterial implements RFC5705 exporters for TLS 1.3 according to
// RFC 8446, Section 7.5.
func (c *cipherSuiteTLS13) exportKeyingMaterial(s *tls13.MasterSecret, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
expMasterSecret := s.ExporterMasterSecret(transcript)
return func(label string, context []byte, length int) ([]byte, error) {
return expMasterSecret.Exporter(label, context, length), nil
}
}
type keySharePrivateKeys struct {
curveID CurveID
ecdhe *ecdh.PrivateKey
mlkem *mlkem.DecapsulationKey768
mlkemEcdhe *ecdh.PrivateKey // [uTLS] seperate ecdhe key for pq keyshare in line with Chrome, instead of reusing ecdhe key like stdlib
}
const x25519PublicKeySize = 32
// generateECDHEKey returns a PrivateKey that implements Diffie-Hellman
// according to RFC 8446, Section 4.2.8.2.
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")
}
return curve.GenerateKey(rand)
}
func curveForCurveID(id CurveID) (ecdh.Curve, bool) {
switch id {
case X25519:
return ecdh.X25519(), true
case CurveP256:
return ecdh.P256(), true
case CurveP384:
return ecdh.P384(), true
case CurveP521:
return ecdh.P521(), true
default:
return nil, false
}
}
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
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

+297
View File
@@ -0,0 +1,297 @@
// Copyright 2009 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 file.
package tls
import (
"crypto"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"hash"
"github.com/refraction-networking/utls/internal/tls12"
)
type prfFunc func(secret []byte, label string, seed []byte, keyLen int) []byte
// Split a premaster secret in two as specified in RFC 4346, Section 5.
func splitPreMasterSecret(secret []byte) (s1, s2 []byte) {
s1 = secret[0 : (len(secret)+1)/2]
s2 = secret[len(secret)/2:]
return
}
// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
func pHash(result, secret, seed []byte, hash func() hash.Hash) {
h := hmac.New(hash, secret)
h.Write(seed)
a := h.Sum(nil)
j := 0
for j < len(result) {
h.Reset()
h.Write(a)
h.Write(seed)
b := h.Sum(nil)
copy(result[j:], b)
j += len(b)
h.Reset()
h.Write(a)
a = h.Sum(nil)
}
}
// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
func prf10(secret []byte, label string, seed []byte, keyLen int) []byte {
result := make([]byte, keyLen)
hashSHA1 := sha1.New
hashMD5 := md5.New
labelAndSeed := make([]byte, len(label)+len(seed))
copy(labelAndSeed, label)
copy(labelAndSeed[len(label):], seed)
s1, s2 := splitPreMasterSecret(secret)
pHash(result, s1, labelAndSeed, hashMD5)
result2 := make([]byte, len(result))
pHash(result2, s2, labelAndSeed, hashSHA1)
for i, b := range result2 {
result[i] ^= b
}
return result
}
// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
func prf12(hashFunc func() hash.Hash) prfFunc {
return func(secret []byte, label string, seed []byte, keyLen int) []byte {
return tls12.PRF(hashFunc, secret, label, seed, keyLen)
}
}
const (
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
finishedVerifyLength = 12 // Length of verify_data in a Finished message.
)
const masterSecretLabel = "master secret"
const extendedMasterSecretLabel = "extended master secret"
const keyExpansionLabel = "key expansion"
const clientFinishedLabel = "client finished"
const serverFinishedLabel = "server finished"
func prfAndHashForVersion(version uint16, suite *cipherSuite) (prfFunc, crypto.Hash) {
switch version {
case VersionTLS10, VersionTLS11:
return prf10, crypto.Hash(0)
case VersionTLS12:
if suite.flags&suiteSHA384 != 0 {
return prf12(sha512.New384), crypto.SHA384
}
return prf12(sha256.New), crypto.SHA256
default:
panic("unknown version")
}
}
func prfForVersion(version uint16, suite *cipherSuite) prfFunc {
prf, _ := prfAndHashForVersion(version, suite)
return prf
}
// masterFromPreMasterSecret generates the master secret from the pre-master
// secret. See RFC 5246, Section 8.1.
func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, clientRandom, serverRandom []byte) []byte {
seed := make([]byte, 0, len(clientRandom)+len(serverRandom))
seed = append(seed, clientRandom...)
seed = append(seed, serverRandom...)
return prfForVersion(version, suite)(preMasterSecret, masterSecretLabel, seed, masterSecretLength)
}
// extMasterFromPreMasterSecret generates the extended master secret from the
// pre-master secret. See RFC 7627.
func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte {
prf, hash := prfAndHashForVersion(version, suite)
if version == VersionTLS12 {
// Use the FIPS 140-3 module only for TLS 1.2 with EMS, which is the
// only TLS 1.0-1.2 approved mode per IG D.Q.
return tls12.MasterSecret(hash.New, preMasterSecret, transcript)
}
return prf(preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength)
}
// keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, Section 6.3.
func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte, macLen, keyLen, ivLen int) (clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV []byte) {
seed := make([]byte, 0, len(serverRandom)+len(clientRandom))
seed = append(seed, serverRandom...)
seed = append(seed, clientRandom...)
n := 2*macLen + 2*keyLen + 2*ivLen
keyMaterial := prfForVersion(version, suite)(masterSecret, keyExpansionLabel, seed, n)
clientMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:]
serverMAC = keyMaterial[:macLen]
keyMaterial = keyMaterial[macLen:]
clientKey = keyMaterial[:keyLen]
keyMaterial = keyMaterial[keyLen:]
serverKey = keyMaterial[:keyLen]
keyMaterial = keyMaterial[keyLen:]
clientIV = keyMaterial[:ivLen]
keyMaterial = keyMaterial[ivLen:]
serverIV = keyMaterial[:ivLen]
return
}
func newFinishedHash(version uint16, cipherSuite *cipherSuite) finishedHash {
var buffer []byte
if version >= VersionTLS12 {
buffer = []byte{}
}
prf, hash := prfAndHashForVersion(version, cipherSuite)
if hash != 0 {
return finishedHash{hash.New(), hash.New(), nil, nil, buffer, version, prf}
}
return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), buffer, version, prf}
}
// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
client hash.Hash
server hash.Hash
// Prior to TLS 1.2, an additional MD5 hash is required.
clientMD5 hash.Hash
serverMD5 hash.Hash
// In TLS 1.2, a full buffer is sadly required.
buffer []byte
version uint16
prf prfFunc
}
func (h *finishedHash) Write(msg []byte) (n int, err error) {
h.client.Write(msg)
h.server.Write(msg)
if h.version < VersionTLS12 {
h.clientMD5.Write(msg)
h.serverMD5.Write(msg)
}
if h.buffer != nil {
h.buffer = append(h.buffer, msg...)
}
return len(msg), nil
}
func (h finishedHash) Sum() []byte {
if h.version >= VersionTLS12 {
return h.client.Sum(nil)
}
out := make([]byte, 0, md5.Size+sha1.Size)
out = h.clientMD5.Sum(out)
return h.client.Sum(out)
}
// clientSum returns the contents of the verify_data member of a client's
// Finished message.
func (h finishedHash) clientSum(masterSecret []byte) []byte {
return h.prf(masterSecret, clientFinishedLabel, h.Sum(), finishedVerifyLength)
}
// serverSum returns the contents of the verify_data member of a server's
// Finished message.
func (h finishedHash) serverSum(masterSecret []byte) []byte {
return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength)
}
// 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) []byte {
if (h.version >= VersionTLS12 || sigType == signatureEd25519) && h.buffer == nil {
panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
}
if sigType == signatureEd25519 {
return h.buffer
}
if h.version >= VersionTLS12 {
hash := hashAlg.New()
hash.Write(h.buffer)
return hash.Sum(nil)
}
if sigType == signatureECDSA {
return h.server.Sum(nil)
}
return h.Sum()
}
// discardHandshakeBuffer is called when there is no more need to
// buffer the entirety of the handshake messages.
func (h *finishedHash) discardHandshakeBuffer() {
h.buffer = nil
}
// noEKMBecauseRenegotiation is used as a value of
// ConnectionState.ekm when renegotiation is enabled and thus
// we wish to fail all key-material export requests.
func noEKMBecauseRenegotiation(label string, context []byte, length int) ([]byte, error) {
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
}
// noEKMBecauseNoEMS is used as a value of ConnectionState.ekm when Extended
// Master Secret is not negotiated and thus we wish to fail all key-material
// export requests.
func noEKMBecauseNoEMS(label string, context []byte, length int) ([]byte, error) {
return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when neither TLS 1.3 nor Extended Master Secret are negotiated; override with GODEBUG=tlsunsafeekm=1")
}
// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clientRandom, serverRandom []byte) func(string, []byte, int) ([]byte, error) {
return func(label string, context []byte, length int) ([]byte, error) {
switch label {
case "client finished", "server finished", "master secret", "key expansion":
// These values are reserved and may not be used.
return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", label)
}
seedLen := len(serverRandom) + len(clientRandom)
if context != nil {
seedLen += 2 + len(context)
}
seed := make([]byte, 0, seedLen)
seed = append(seed, clientRandom...)
seed = append(seed, serverRandom...)
if context != nil {
if len(context) >= 1<<16 {
return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
}
seed = append(seed, byte(len(context)>>8), byte(len(context)))
seed = append(seed, context...)
}
return prfForVersion(version, suite)(masterSecret, label, seed, length), nil
}
}
+500
View File
@@ -0,0 +1,500 @@
// Copyright 2023 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 file.
package tls
import (
"context"
"errors"
"fmt"
)
// QUICEncryptionLevel represents a QUIC encryption level used to transmit
// handshake messages.
type QUICEncryptionLevel int
const (
QUICEncryptionLevelInitial = QUICEncryptionLevel(iota)
QUICEncryptionLevelEarly
QUICEncryptionLevelHandshake
QUICEncryptionLevelApplication
)
func (l QUICEncryptionLevel) String() string {
switch l {
case QUICEncryptionLevelInitial:
return "Initial"
case QUICEncryptionLevelEarly:
return "Early"
case QUICEncryptionLevelHandshake:
return "Handshake"
case QUICEncryptionLevelApplication:
return "Application"
default:
return fmt.Sprintf("QUICEncryptionLevel(%v)", int(l))
}
}
// A QUICConn represents a connection which uses a QUIC implementation as the underlying
// transport as described in RFC 9001.
//
// Methods of QUICConn are not safe for concurrent use.
type QUICConn struct {
conn *Conn
sessionTicketSent bool
}
// A QUICConfig configures a [QUICConn].
type QUICConfig struct {
TLSConfig *Config
// EnableSessionEvents may be set to true to enable the
// [QUICStoreSession] and [QUICResumeSession] events for client connections.
// When this event is enabled, sessions are not automatically
// stored in the client session cache.
// The application should use [QUICConn.StoreSession] to store sessions.
EnableSessionEvents bool
}
// A QUICEventKind is a type of operation on a QUIC connection.
type QUICEventKind int
const (
// QUICNoEvent indicates that there are no events available.
QUICNoEvent QUICEventKind = iota
// QUICSetReadSecret and QUICSetWriteSecret provide the read and write
// secrets for a given encryption level.
// QUICEvent.Level, QUICEvent.Data, and QUICEvent.Suite are set.
//
// Secrets for the Initial encryption level are derived from the initial
// destination connection ID, and are not provided by the QUICConn.
QUICSetReadSecret
QUICSetWriteSecret
// QUICWriteData provides data to send to the peer in CRYPTO frames.
// QUICEvent.Data is set.
QUICWriteData
// QUICTransportParameters provides the peer's QUIC transport parameters.
// QUICEvent.Data is set.
QUICTransportParameters
// QUICTransportParametersRequired indicates that the caller must provide
// QUIC transport parameters to send to the peer. The caller should set
// the transport parameters with QUICConn.SetTransportParameters and call
// QUICConn.NextEvent again.
//
// If transport parameters are set before calling QUICConn.Start, the
// connection will never generate a QUICTransportParametersRequired event.
QUICTransportParametersRequired
// QUICRejectedEarlyData indicates that the server rejected 0-RTT data even
// if we offered it. It's returned before QUICEncryptionLevelApplication
// keys are returned.
// This event only occurs on client connections.
QUICRejectedEarlyData
// QUICHandshakeDone indicates that the TLS handshake has completed.
QUICHandshakeDone
// QUICResumeSession indicates that a client is attempting to resume a previous session.
// [QUICEvent.SessionState] is set.
//
// For client connections, this event occurs when the session ticket is selected.
// For server connections, this event occurs when receiving the client's session ticket.
//
// The application may set [QUICEvent.SessionState.EarlyData] to false before the
// next call to [QUICConn.NextEvent] to decline 0-RTT even if the session supports it.
QUICResumeSession
// QUICStoreSession indicates that the server has provided state permitting
// the client to resume the session.
// [QUICEvent.SessionState] is set.
// The application should use [QUICConn.StoreSession] session to store the [SessionState].
// The application may modify the [SessionState] before storing it.
// This event only occurs on client connections.
QUICStoreSession
)
// A QUICEvent is an event occurring on a QUIC connection.
//
// The type of event is specified by the Kind field.
// The contents of the other fields are kind-specific.
type QUICEvent struct {
Kind QUICEventKind
// Set for QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
Level QUICEncryptionLevel
// Set for QUICTransportParameters, QUICSetReadSecret, QUICSetWriteSecret, and QUICWriteData.
// The contents are owned by crypto/tls, and are valid until the next NextEvent call.
Data []byte
// Set for QUICSetReadSecret and QUICSetWriteSecret.
Suite uint16
// Set for QUICResumeSession and QUICStoreSession.
SessionState *SessionState
}
type quicState struct {
events []QUICEvent
nextEvent int
// eventArr is a statically allocated event array, large enough to handle
// the usual maximum number of events resulting from a single call: transport
// parameters, Initial data, Early read secret, Handshake write and read
// secrets, Handshake data, Application write secret, Application data.
eventArr [8]QUICEvent
started bool
signalc chan struct{} // handshake data is available to be read
blockedc chan struct{} // handshake is waiting for data, closed when done
cancelc <-chan struct{} // handshake has been canceled
cancel context.CancelFunc
waitingForDrain bool
// readbuf is shared between HandleData and the handshake goroutine.
// HandshakeCryptoData passes ownership to the handshake goroutine by
// reading from signalc, and reclaims ownership by reading from blockedc.
readbuf []byte
transportParams []byte // to send to the peer
enableSessionEvents bool
}
// QUICClient returns a new TLS client side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICClient(config *QUICConfig) *QUICConn {
return newQUICConn(Client(nil, config.TLSConfig), config)
}
// QUICServer returns a new TLS server side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func QUICServer(config *QUICConfig) *QUICConn {
return newQUICConn(Server(nil, config.TLSConfig), config)
}
func newQUICConn(conn *Conn, config *QUICConfig) *QUICConn {
conn.quic = &quicState{
signalc: make(chan struct{}),
blockedc: make(chan struct{}),
enableSessionEvents: config.EnableSessionEvents,
}
conn.quic.events = conn.quic.eventArr[:0]
return &QUICConn{
conn: conn,
}
}
// Start starts the client or server handshake protocol.
// It may produce connection events, which may be read with [QUICConn.NextEvent].
//
// Start must be called at most once.
func (q *QUICConn) Start(ctx context.Context) error {
if q.conn.quic.started {
return quicError(errors.New("tls: Start called more than once"))
}
q.conn.quic.started = true
if q.conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3"))
}
go q.conn.HandshakeContext(ctx)
if _, ok := <-q.conn.quic.blockedc; !ok {
return q.conn.handshakeErr
}
return nil
}
// NextEvent returns the next event occurring on the connection.
// It returns an event with a Kind of [QUICNoEvent] when no events are available.
func (q *QUICConn) NextEvent() QUICEvent {
qs := q.conn.quic
if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
// Write over some of the previous event's data,
// to catch callers erroniously retaining it.
qs.events[last].Data[0] = 0
}
if qs.nextEvent >= len(qs.events) && qs.waitingForDrain {
qs.waitingForDrain = false
<-qs.signalc
<-qs.blockedc
}
if qs.nextEvent >= len(qs.events) {
qs.events = qs.events[:0]
qs.nextEvent = 0
return QUICEvent{Kind: QUICNoEvent}
}
e := qs.events[qs.nextEvent]
qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data
qs.nextEvent++
return e
}
// Close closes the connection and stops any in-progress handshake.
func (q *QUICConn) Close() error {
if q.conn.quic.cancel == nil {
return nil // never started
}
q.conn.quic.cancel()
for range q.conn.quic.blockedc {
// Wait for the handshake goroutine to return.
}
return q.conn.handshakeErr
}
// HandleData handles handshake bytes received from the peer.
// It may produce connection events, which may be read with [QUICConn.NextEvent].
func (q *QUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
c := q.conn
if c.in.level != level {
return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
}
c.quic.readbuf = data
<-c.quic.signalc
_, ok := <-c.quic.blockedc
if ok {
// The handshake goroutine is waiting for more data.
return nil
}
// The handshake goroutine has exited.
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
c.hand.Write(c.quic.readbuf)
c.quic.readbuf = nil
for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
b := q.conn.hand.Bytes()
n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
if n > maxHandshake {
q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)
break
}
if len(b) < 4+n {
return nil
}
if err := q.conn.handlePostHandshakeMessage(); err != nil {
q.conn.handshakeErr = err
}
}
if q.conn.handshakeErr != nil {
return quicError(q.conn.handshakeErr)
}
return nil
}
type QUICSessionTicketOptions struct {
// EarlyData specifies whether the ticket may be used for 0-RTT.
EarlyData bool
Extra [][]byte
}
// SendSessionTicket sends a session ticket to the client.
// It produces connection events, which may be read with [QUICConn.NextEvent].
// Currently, it can only be called once.
func (q *QUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error {
c := q.conn
if !c.isHandshakeComplete.Load() {
return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
}
if c.isClient {
return quicError(errors.New("tls: SendSessionTicket called on the client"))
}
if q.sessionTicketSent {
return quicError(errors.New("tls: SendSessionTicket called multiple times"))
}
q.sessionTicketSent = true
return quicError(c.sendSessionTicket(opts.EarlyData, opts.Extra))
}
// StoreSession stores a session previously received in a QUICStoreSession event
// in the ClientSessionCache.
// The application may process additional events or modify the SessionState
// before storing the session.
func (q *QUICConn) StoreSession(session *SessionState) error {
c := q.conn
if !c.isClient {
return quicError(errors.New("tls: StoreSessionTicket called on the server"))
}
cacheKey := c.clientSessionCacheKey()
if cacheKey == "" {
return nil
}
cs := &ClientSessionState{session: session}
c.config.ClientSessionCache.Put(cacheKey, cs)
return nil
}
// ConnectionState returns basic TLS details about the connection.
func (q *QUICConn) ConnectionState() ConnectionState {
return q.conn.ConnectionState()
}
// SetTransportParameters sets the transport parameters to send to the peer.
//
// Server connections may delay setting the transport parameters until after
// receiving the client's transport parameters. See [QUICTransportParametersRequired].
func (q *QUICConn) SetTransportParameters(params []byte) {
if params == nil {
params = []byte{}
}
q.conn.quic.transportParams = params
if q.conn.quic.started {
<-q.conn.quic.signalc
<-q.conn.quic.blockedc
}
}
// quicError ensures err is an AlertError.
// If err is not already, quicError wraps it with alertInternalError.
func quicError(err error) error {
if err == nil {
return nil
}
var ae AlertError
if errors.As(err, &ae) {
return err
}
var a alert
if !errors.As(err, &a) {
a = alertInternalError
}
// Return an error wrapping the original error and an AlertError.
// Truncate the text of the alert to 0 characters.
return fmt.Errorf("%w%.0w", err, AlertError(a))
}
func (c *Conn) quicReadHandshakeBytes(n int) error {
for c.hand.Len() < n {
if err := c.quicWaitForSignal(); err != nil {
return err
}
}
return nil
}
func (c *Conn) quicSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICSetReadSecret,
Level: level,
Suite: suite,
Data: secret,
})
}
func (c *Conn) quicSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICSetWriteSecret,
Level: level,
Suite: suite,
Data: secret,
})
}
func (c *Conn) quicWriteCryptoData(level QUICEncryptionLevel, data []byte) {
var last *QUICEvent
if len(c.quic.events) > 0 {
last = &c.quic.events[len(c.quic.events)-1]
}
if last == nil || last.Kind != QUICWriteData || last.Level != level {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICWriteData,
Level: level,
})
last = &c.quic.events[len(c.quic.events)-1]
}
last.Data = append(last.Data, data...)
}
func (c *Conn) quicResumeSession(session *SessionState) error {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICResumeSession,
SessionState: session,
})
c.quic.waitingForDrain = true
for c.quic.waitingForDrain {
if err := c.quicWaitForSignal(); err != nil {
return err
}
}
return nil
}
func (c *Conn) quicStoreSession(session *SessionState) {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICStoreSession,
SessionState: session,
})
}
func (c *Conn) quicSetTransportParameters(params []byte) {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICTransportParameters,
Data: params,
})
}
func (c *Conn) quicGetTransportParameters() ([]byte, error) {
if c.quic.transportParams == nil {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICTransportParametersRequired,
})
}
for c.quic.transportParams == nil {
if err := c.quicWaitForSignal(); err != nil {
return nil, err
}
}
return c.quic.transportParams, nil
}
func (c *Conn) quicHandshakeComplete() {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICHandshakeDone,
})
}
func (c *Conn) quicRejectedEarlyData() {
c.quic.events = append(c.quic.events, QUICEvent{
Kind: QUICRejectedEarlyData,
})
}
// quicWaitForSignal notifies the QUICConn that handshake progress is blocked,
// and waits for a signal that the handshake should proceed.
//
// The handshake may become blocked waiting for handshake bytes
// or for the user to provide transport parameters.
func (c *Conn) quicWaitForSignal() error {
// Drop the handshake mutex while blocked to allow the user
// to call ConnectionState before the handshake completes.
c.handshakeMutex.Unlock()
defer c.handshakeMutex.Lock()
// Send on blockedc to notify the QUICConn that the handshake is blocked.
// Exported methods of QUICConn wait for the handshake to become blocked
// before returning to the user.
select {
case c.quic.blockedc <- struct{}{}:
case <-c.quic.cancelc:
return c.sendAlertLocked(alertCloseNotify)
}
// The QUICConn reads from signalc to notify us that the handshake may
// be able to proceed. (The QUICConn reads, because we close signalc to
// indicate that the handshake has completed.)
select {
case c.quic.signalc <- struct{}{}:
c.hand.Write(c.quic.readbuf)
c.quic.readbuf = nil
case <-c.quic.cancelc:
return c.sendAlertLocked(alertCloseNotify)
}
return nil
}
+441
View File
@@ -0,0 +1,441 @@
// Copyright 2012 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 file.
package tls
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/subtle"
"crypto/x509"
"errors"
"io"
"golang.org/x/crypto/cryptobyte"
)
// A SessionState is a resumable session.
type SessionState struct {
// Encoded as a SessionState (in the language of RFC 8446, Section 3).
//
// enum { server(1), client(2) } SessionStateType;
//
// opaque Certificate<1..2^24-1>;
//
// Certificate CertificateChain<0..2^24-1>;
//
// opaque Extra<0..2^24-1>;
//
// struct {
// uint16 version;
// SessionStateType type;
// uint16 cipher_suite;
// uint64 created_at;
// opaque secret<1..2^8-1>;
// Extra extra<0..2^24-1>;
// uint8 ext_master_secret = { 0, 1 };
// uint8 early_data = { 0, 1 };
// CertificateEntry certificate_list<0..2^24-1>;
// CertificateChain verified_chains<0..2^24-1>; /* excluding leaf */
// select (SessionState.early_data) {
// case 0: Empty;
// case 1: opaque alpn<1..2^8-1>;
// };
// select (SessionState.type) {
// case server: Empty;
// case client: struct {
// select (SessionState.version) {
// case VersionTLS10..VersionTLS12: Empty;
// case VersionTLS13: struct {
// uint64 use_by;
// uint32 age_add;
// };
// };
// };
// };
// } SessionState;
//
// Extra is ignored by crypto/tls, but is encoded by [SessionState.Bytes]
// and parsed by [ParseSessionState].
//
// This allows [Config.UnwrapSession]/[Config.WrapSession] and
// [ClientSessionCache] implementations to store and retrieve additional
// data alongside this session.
//
// To allow different layers in a protocol stack to share this field,
// applications must only append to it, not replace it, and must use entries
// that can be recognized even if out of order (for example, by starting
// with an id and version prefix).
Extra [][]byte
// EarlyData indicates whether the ticket can be used for 0-RTT in a QUIC
// connection. The application may set this to false if it is true to
// decline to offer 0-RTT even if supported.
EarlyData bool
version uint16
isClient bool
cipherSuite uint16
// createdAt is the generation time of the secret on the sever (which for
// TLS 1.01.2 might be earlier than the current session) and the time at
// which the ticket was received on the client.
createdAt uint64 // seconds since UNIX epoch
secret []byte // master secret for TLS 1.2, or the PSK for TLS 1.3
extMasterSecret bool
peerCertificates []*x509.Certificate
activeCertHandles []*activeCert
ocspResponse []byte
scts [][]byte
verifiedChains [][]*x509.Certificate
alpnProtocol string // only set if EarlyData is true
// Client-side TLS 1.3-only fields.
useBy uint64 // seconds since UNIX epoch
ageAdd uint32
ticket []byte
}
// Bytes encodes the session, including any private fields, so that it can be
// parsed by [ParseSessionState]. The encoding contains secret values critical
// to the security of future and possibly past sessions.
//
// The specific encoding should be considered opaque and may change incompatibly
// between Go versions.
func (s *SessionState) Bytes() ([]byte, error) {
var b cryptobyte.Builder
b.AddUint16(s.version)
if s.isClient {
b.AddUint8(2) // client
} else {
b.AddUint8(1) // server
}
b.AddUint16(s.cipherSuite)
addUint64(&b, s.createdAt)
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(s.secret)
})
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
for _, extra := range s.Extra {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(extra)
})
}
})
if s.extMasterSecret {
b.AddUint8(1)
} else {
b.AddUint8(0)
}
if s.EarlyData {
b.AddUint8(1)
} else {
b.AddUint8(0)
}
marshalCertificate(&b, Certificate{
Certificate: certificatesToBytesSlice(s.peerCertificates),
OCSPStaple: s.ocspResponse,
SignedCertificateTimestamps: s.scts,
})
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
for _, chain := range s.verifiedChains {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
// We elide the first certificate because it's always the leaf.
if len(chain) == 0 {
b.SetError(errors.New("tls: internal error: empty verified chain"))
return
}
for _, cert := range chain[1:] {
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(cert.Raw)
})
}
})
}
})
if s.EarlyData {
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes([]byte(s.alpnProtocol))
})
}
if s.isClient {
if s.version >= VersionTLS13 {
addUint64(&b, s.useBy)
b.AddUint32(s.ageAdd)
}
}
return b.Bytes()
}
func certificatesToBytesSlice(certs []*x509.Certificate) [][]byte {
s := make([][]byte, 0, len(certs))
for _, c := range certs {
s = append(s, c.Raw)
}
return s
}
// ParseSessionState parses a [SessionState] encoded by [SessionState.Bytes].
func ParseSessionState(data []byte) (*SessionState, error) {
ss := &SessionState{}
s := cryptobyte.String(data)
var typ, extMasterSecret, earlyData uint8
var cert Certificate
var extra cryptobyte.String
if !s.ReadUint16(&ss.version) ||
!s.ReadUint8(&typ) ||
(typ != 1 && typ != 2) ||
!s.ReadUint16(&ss.cipherSuite) ||
!readUint64(&s, &ss.createdAt) ||
!readUint8LengthPrefixed(&s, &ss.secret) ||
!s.ReadUint24LengthPrefixed(&extra) ||
!s.ReadUint8(&extMasterSecret) ||
!s.ReadUint8(&earlyData) ||
len(ss.secret) == 0 ||
!unmarshalCertificate(&s, &cert) {
return nil, errors.New("tls: invalid session encoding")
}
for !extra.Empty() {
var e []byte
if !readUint24LengthPrefixed(&extra, &e) {
return nil, errors.New("tls: invalid session encoding")
}
ss.Extra = append(ss.Extra, e)
}
switch extMasterSecret {
case 0:
ss.extMasterSecret = false
case 1:
ss.extMasterSecret = true
default:
return nil, errors.New("tls: invalid session encoding")
}
switch earlyData {
case 0:
ss.EarlyData = false
case 1:
ss.EarlyData = true
default:
return nil, errors.New("tls: invalid session encoding")
}
for _, cert := range cert.Certificate {
c, err := globalCertCache.newCert(cert)
if err != nil {
return nil, err
}
ss.activeCertHandles = append(ss.activeCertHandles, c)
ss.peerCertificates = append(ss.peerCertificates, c.cert)
}
ss.ocspResponse = cert.OCSPStaple
ss.scts = cert.SignedCertificateTimestamps
var chainList cryptobyte.String
if !s.ReadUint24LengthPrefixed(&chainList) {
return nil, errors.New("tls: invalid session encoding")
}
for !chainList.Empty() {
var certList cryptobyte.String
if !chainList.ReadUint24LengthPrefixed(&certList) {
return nil, errors.New("tls: invalid session encoding")
}
var chain []*x509.Certificate
if len(ss.peerCertificates) == 0 {
return nil, errors.New("tls: invalid session encoding")
}
chain = append(chain, ss.peerCertificates[0])
for !certList.Empty() {
var cert []byte
if !readUint24LengthPrefixed(&certList, &cert) {
return nil, errors.New("tls: invalid session encoding")
}
c, err := globalCertCache.newCert(cert)
if err != nil {
return nil, err
}
ss.activeCertHandles = append(ss.activeCertHandles, c)
chain = append(chain, c.cert)
}
ss.verifiedChains = append(ss.verifiedChains, chain)
}
if ss.EarlyData {
var alpn []byte
if !readUint8LengthPrefixed(&s, &alpn) {
return nil, errors.New("tls: invalid session encoding")
}
ss.alpnProtocol = string(alpn)
}
if isClient := typ == 2; !isClient {
if !s.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return ss, nil
}
ss.isClient = true
if len(ss.peerCertificates) == 0 {
return nil, errors.New("tls: no server certificates in client session")
}
if ss.version < VersionTLS13 {
if !s.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return ss, nil
}
if !s.ReadUint64(&ss.useBy) || !s.ReadUint32(&ss.ageAdd) || !s.Empty() {
return nil, errors.New("tls: invalid session encoding")
}
return ss, nil
}
// sessionState returns a partially filled-out [SessionState] with information
// from the current connection.
func (c *Conn) sessionState() *SessionState {
return &SessionState{
version: c.vers,
cipherSuite: c.cipherSuite,
createdAt: uint64(c.config.time().Unix()),
alpnProtocol: c.clientProtocol,
peerCertificates: c.peerCertificates,
activeCertHandles: c.activeCertHandles,
ocspResponse: c.ocspResponse,
scts: c.scts,
isClient: c.isClient,
extMasterSecret: c.extMasterSecret,
verifiedChains: c.verifiedChains,
}
}
// EncryptTicket encrypts a ticket with the [Config]'s configured (or default)
// session ticket keys. It can be used as a [Config.WrapSession] implementation.
func (c *Config) EncryptTicket(cs ConnectionState, ss *SessionState) ([]byte, error) {
ticketKeys := c.ticketKeys(nil)
stateBytes, err := ss.Bytes()
if err != nil {
return nil, err
}
return c.encryptTicket(stateBytes, ticketKeys)
}
func (c *Config) encryptTicket(state []byte, ticketKeys []ticketKey) ([]byte, error) {
if len(ticketKeys) == 0 {
return nil, errors.New("tls: internal error: session ticket keys unavailable")
}
encrypted := make([]byte, aes.BlockSize+len(state)+sha256.Size)
iv := encrypted[:aes.BlockSize]
ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
authenticated := encrypted[:len(encrypted)-sha256.Size]
macBytes := encrypted[len(encrypted)-sha256.Size:]
if _, err := io.ReadFull(c.rand(), iv); err != nil {
return nil, err
}
key := ticketKeys[0]
block, err := aes.NewCipher(key.aesKey[:])
if err != nil {
return nil, errors.New("tls: failed to create cipher while encrypting ticket: " + err.Error())
}
cipher.NewCTR(block, iv).XORKeyStream(ciphertext, state)
mac := hmac.New(sha256.New, key.hmacKey[:])
mac.Write(authenticated)
mac.Sum(macBytes[:0])
return encrypted, nil
}
// DecryptTicket decrypts a ticket encrypted by [Config.EncryptTicket]. It can
// be used as a [Config.UnwrapSession] implementation.
//
// If the ticket can't be decrypted or parsed, DecryptTicket returns (nil, nil).
func (c *Config) DecryptTicket(identity []byte, cs ConnectionState) (*SessionState, error) {
ticketKeys := c.ticketKeys(nil)
stateBytes := c.decryptTicket(identity, ticketKeys)
if stateBytes == nil {
return nil, nil
}
s, err := ParseSessionState(stateBytes)
if err != nil {
return nil, nil // drop unparsable tickets on the floor
}
return s, nil
}
func (c *Config) decryptTicket(encrypted []byte, ticketKeys []ticketKey) []byte {
if len(encrypted) < aes.BlockSize+sha256.Size {
return nil
}
iv := encrypted[:aes.BlockSize]
ciphertext := encrypted[aes.BlockSize : len(encrypted)-sha256.Size]
authenticated := encrypted[:len(encrypted)-sha256.Size]
macBytes := encrypted[len(encrypted)-sha256.Size:]
for _, key := range ticketKeys {
mac := hmac.New(sha256.New, key.hmacKey[:])
mac.Write(authenticated)
expected := mac.Sum(nil)
if subtle.ConstantTimeCompare(macBytes, expected) != 1 {
continue
}
block, err := aes.NewCipher(key.aesKey[:])
if err != nil {
return nil
}
plaintext := make([]byte, len(ciphertext))
cipher.NewCTR(block, iv).XORKeyStream(plaintext, ciphertext)
return plaintext
}
return nil
}
// ClientSessionState contains the state needed by a client to
// resume a previous TLS session.
type ClientSessionState struct {
session *SessionState
}
// ResumptionState returns the session ticket sent by the server (also known as
// the session's identity) and the state necessary to resume this session.
//
// It can be called by [ClientSessionCache.Put] to serialize (with
// [SessionState.Bytes]) and store the session.
func (cs *ClientSessionState) ResumptionState() (ticket []byte, state *SessionState, err error) {
if cs == nil || cs.session == nil {
return nil, nil, nil
}
return cs.session.ticket, cs.session, nil
}
// NewResumptionState returns a state value that can be returned by
// [ClientSessionCache.Get] to resume a previous session.
//
// state needs to be returned by [ParseSessionState], and the ticket and session
// state must have been returned by [ClientSessionState.ResumptionState].
func NewResumptionState(ticket []byte, state *SessionState) (*ClientSessionState, error) {
state.ticket = ticket
return &ClientSessionState{
session: state,
}, nil
}
// // DecryptTicketWith decrypts an encrypted session ticket
// // using a TicketKeys (ie []TicketKey) struct
// //
// // usedOldKey will be true if the key used for decryption is
// // not the first in the []TicketKey slice
// //
// // [uTLS] changed to be made public and take a TicketKeys and use a fake conn receiver
// func DecryptTicketWith(encrypted []byte, tks TicketKeys) (plaintext []byte, usedOldKey bool) {
// // create fake conn
// c := &Conn{
// ticketKeys: tks.ToPrivate(),
// }
// return c.decryptTicket(encrypted)
// }
+373
View File
@@ -0,0 +1,373 @@
// Copyright 2009 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 file.
// Package tls partially implements TLS 1.2, as specified in RFC 5246,
// and TLS 1.3, as specified in RFC 8446.
package tls
// BUG(agl): The crypto/tls package only implements some countermeasures
// against Lucky13 attacks on CBC-mode encryption, and only on SHA1
// variants. See http://www.isg.rhul.ac.uk/tls/TLStiming.pdf and
// https://www.imperialviolet.org/2013/02/04/luckythirteen.html.
import (
"bytes"
"context"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"net"
"os"
"strings"
)
// Server returns a new TLS server side connection
// using conn as the underlying transport.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Server(conn net.Conn, config *Config) *Conn {
c := &Conn{
conn: conn,
config: config,
}
c.handshakeFn = c.serverHandshake
return c
}
// Client returns a new TLS client side connection
// using conn as the underlying transport.
// The config cannot be nil: users must set either ServerName or
// InsecureSkipVerify in the config.
func Client(conn net.Conn, config *Config) *Conn {
c := &Conn{
conn: conn,
config: config,
isClient: true,
}
c.handshakeFn = c.clientHandshake
return c
}
// A listener implements a network listener (net.Listener) for TLS connections.
type listener struct {
net.Listener
config *Config
}
// Accept waits for and returns the next incoming TLS connection.
// The returned connection is of type *Conn.
func (l *listener) Accept() (net.Conn, error) {
c, err := l.Listener.Accept()
if err != nil {
return nil, err
}
return Server(c, l.config), nil
}
// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with [Server].
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func NewListener(inner net.Listener, config *Config) net.Listener {
l := new(listener)
l.Listener = inner
l.config = config
return l
}
// Listen creates a TLS listener accepting connections on the
// given network address using net.Listen.
// The configuration config must be non-nil and must include
// at least one certificate or else set GetCertificate.
func Listen(network, laddr string, config *Config) (net.Listener, error) {
// If this condition changes, consider updating http.Server.ServeTLS too.
if config == nil || len(config.Certificates) == 0 &&
config.GetCertificate == nil && config.GetConfigForClient == nil {
return nil, errors.New("tls: neither Certificates, GetCertificate, nor GetConfigForClient set in Config")
}
l, err := net.Listen(network, laddr)
if err != nil {
return nil, err
}
return NewListener(l, config), nil
}
type timeoutError struct{}
func (timeoutError) Error() string { return "tls: DialWithDialer timed out" }
func (timeoutError) Timeout() bool { return true }
func (timeoutError) Temporary() bool { return true }
// DialWithDialer connects to the given network address using dialer.Dial and
// then initiates a TLS handshake, returning the resulting TLS connection. Any
// timeout or deadline given in the dialer apply to connection and TLS
// handshake as a whole.
//
// DialWithDialer interprets a nil configuration as equivalent to the zero
// configuration; see the documentation of [Config] for the defaults.
//
// DialWithDialer uses context.Background internally; to specify the context,
// use [Dialer.DialContext] with NetDialer set to the desired dialer.
func DialWithDialer(dialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
return dial(context.Background(), dialer, network, addr, config)
}
func dial(ctx context.Context, netDialer *net.Dialer, network, addr string, config *Config) (*Conn, error) {
if netDialer.Timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, netDialer.Timeout)
defer cancel()
}
if !netDialer.Deadline.IsZero() {
var cancel context.CancelFunc
ctx, cancel = context.WithDeadline(ctx, netDialer.Deadline)
defer cancel()
}
rawConn, err := netDialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
colonPos := strings.LastIndex(addr, ":")
if colonPos == -1 {
colonPos = len(addr)
}
hostname := addr[:colonPos]
if config == nil {
config = defaultConfig()
}
// If no ServerName is set, infer the ServerName
// from the hostname we're connecting to.
if config.ServerName == "" {
// Make a copy to avoid polluting argument or default.
c := config.Clone()
c.ServerName = hostname
config = c
}
conn := Client(rawConn, config)
if err := conn.HandshakeContext(ctx); err != nil {
rawConn.Close()
return nil, err
}
return conn, nil
}
// Dial connects to the given network address using net.Dial
// and then initiates a TLS handshake, returning the resulting
// TLS connection.
// Dial interprets a nil configuration as equivalent to
// the zero configuration; see the documentation of Config
// for the defaults.
func Dial(network, addr string, config *Config) (*Conn, error) {
return DialWithDialer(new(net.Dialer), network, addr, config)
}
// Dialer dials TLS connections given a configuration and a Dialer for the
// underlying connection.
type Dialer struct {
// NetDialer is the optional dialer to use for the TLS connections'
// underlying TCP connections.
// A nil NetDialer is equivalent to the net.Dialer zero value.
NetDialer *net.Dialer
// Config is the TLS configuration to use for new connections.
// A nil configuration is equivalent to the zero
// configuration; see the documentation of Config for the
// defaults.
Config *Config
}
// Dial connects to the given network address and initiates a TLS
// handshake, returning the resulting TLS connection.
//
// The returned [Conn], if any, will always be of type *[Conn].
//
// Dial uses context.Background internally; to specify the context,
// use [Dialer.DialContext].
func (d *Dialer) Dial(network, addr string) (net.Conn, error) {
return d.DialContext(context.Background(), network, addr)
}
func (d *Dialer) netDialer() *net.Dialer {
if d.NetDialer != nil {
return d.NetDialer
}
return new(net.Dialer)
}
// DialContext connects to the given network address and initiates a TLS
// handshake, returning the resulting TLS connection.
//
// The provided Context must be non-nil. If the context expires before
// the connection is complete, an error is returned. Once successfully
// connected, any expiration of the context will not affect the
// connection.
//
// The returned [Conn], if any, will always be of type *[Conn].
func (d *Dialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
c, err := dial(ctx, d.netDialer(), network, addr, d.Config)
if err != nil {
// Don't return c (a typed nil) in an interface.
return nil, err
}
return c, nil
}
// LoadX509KeyPair reads and parses a public/private key pair from a pair of
// files. The files must contain PEM encoded data. The certificate file may
// contain intermediate certificates following the leaf certificate to form a
// certificate chain. On successful return, Certificate.Leaf will be populated.
//
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func LoadX509KeyPair(certFile, keyFile string) (Certificate, error) {
certPEMBlock, err := os.ReadFile(certFile)
if err != nil {
return Certificate{}, err
}
keyPEMBlock, err := os.ReadFile(keyFile)
if err != nil {
return Certificate{}, err
}
return X509KeyPair(certPEMBlock, keyPEMBlock)
}
// var x509keypairleaf = godebug.New("x509keypairleaf") [uTLS]
// X509KeyPair parses a public/private key pair from a pair of
// PEM encoded data. On successful return, Certificate.Leaf will be populated.
//
// Before Go 1.23 Certificate.Leaf was left nil, and the parsed certificate was
// discarded. This behavior can be re-enabled by setting "x509keypairleaf=0"
// in the GODEBUG environment variable.
func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (Certificate, error) {
fail := func(err error) (Certificate, error) { return Certificate{}, err }
var cert Certificate
var skippedBlockTypes []string
for {
var certDERBlock *pem.Block
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
} else {
skippedBlockTypes = append(skippedBlockTypes, certDERBlock.Type)
}
}
if len(cert.Certificate) == 0 {
if len(skippedBlockTypes) == 0 {
return fail(errors.New("tls: failed to find any PEM data in certificate input"))
}
if len(skippedBlockTypes) == 1 && strings.HasSuffix(skippedBlockTypes[0], "PRIVATE KEY") {
return fail(errors.New("tls: failed to find certificate PEM data in certificate input, but did find a private key; PEM inputs may have been switched"))
}
return fail(fmt.Errorf("tls: failed to find \"CERTIFICATE\" PEM block in certificate input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
skippedBlockTypes = skippedBlockTypes[:0]
var keyDERBlock *pem.Block
for {
keyDERBlock, keyPEMBlock = pem.Decode(keyPEMBlock)
if keyDERBlock == nil {
if len(skippedBlockTypes) == 0 {
return fail(errors.New("tls: failed to find any PEM data in key input"))
}
if len(skippedBlockTypes) == 1 && skippedBlockTypes[0] == "CERTIFICATE" {
return fail(errors.New("tls: found a certificate rather than a key in the PEM for the private key"))
}
return fail(fmt.Errorf("tls: failed to find PEM block with type ending in \"PRIVATE KEY\" in key input after skipping PEM blocks of the following types: %v", skippedBlockTypes))
}
if keyDERBlock.Type == "PRIVATE KEY" || strings.HasSuffix(keyDERBlock.Type, " PRIVATE KEY") {
break
}
skippedBlockTypes = append(skippedBlockTypes, keyDERBlock.Type)
}
// We don't need to parse the public key for TLS, but we so do anyway
// to check that it looks sane and matches the private key.
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
if err != nil {
return fail(err)
}
// [uTLS section begins]
// if x509keypairleaf.Value() != "0" {
// cert.Leaf = x509Cert
// } else {
// x509keypairleaf.IncNonDefault()
// }
// [uTLS section ends]
cert.PrivateKey, err = parsePrivateKey(keyDERBlock.Bytes)
if err != nil {
return fail(err)
}
switch pub := x509Cert.PublicKey.(type) {
case *rsa.PublicKey:
priv, ok := cert.PrivateKey.(*rsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.N.Cmp(priv.N) != 0 {
return fail(errors.New("tls: private key does not match public key"))
}
case *ecdsa.PublicKey:
priv, ok := cert.PrivateKey.(*ecdsa.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if pub.X.Cmp(priv.X) != 0 || pub.Y.Cmp(priv.Y) != 0 {
return fail(errors.New("tls: private key does not match public key"))
}
case ed25519.PublicKey:
priv, ok := cert.PrivateKey.(ed25519.PrivateKey)
if !ok {
return fail(errors.New("tls: private key type does not match public key type"))
}
if !bytes.Equal(priv.Public().(ed25519.PublicKey), pub) {
return fail(errors.New("tls: private key does not match public key"))
}
default:
return fail(errors.New("tls: unknown public key algorithm"))
}
return cert, nil
}
// Attempt to parse the given private key DER block. OpenSSL 0.9.8 generates
// PKCS #1 private keys by default, while OpenSSL 1.0.0 generates PKCS #8 keys.
// OpenSSL ecparam generates SEC1 EC private keys for ECDSA. We try all three.
func parsePrivateKey(der []byte) (crypto.PrivateKey, error) {
if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
switch key := key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return key, nil
default:
return nil, errors.New("tls: found unknown private key type in PKCS#8 wrapping")
}
}
if key, err := x509.ParseECPrivateKey(der); err == nil {
return key, nil
}
return nil, errors.New("tls: failed to parse private key")
}
+39
View File
@@ -0,0 +1,39 @@
package tls
import (
"crypto/ecdh"
)
// This file contains all the alias functions, symbols, names, etc. that
// was once used in the old version of the library. This is to ensure
// backwards compatibility with the old version of the library.
// TLS Extensions
// UtlsExtendedMasterSecretExtension is an alias for ExtendedMasterSecretExtension.
//
// Deprecated: Use ExtendedMasterSecretExtension instead.
type UtlsExtendedMasterSecretExtension = ExtendedMasterSecretExtension
// Deprecated: Use KeySharePrivateKeys instead. This type is not used and will be removed in the future.
// KeySharesParameters serves as a in-memory storage for generated keypairs by UTLS when generating
// ClientHello. It is used to store both ecdhe and kem keypairs.
type KeySharesParameters struct{}
func NewKeySharesParameters() *KeySharesParameters { return &KeySharesParameters{} }
func (*KeySharesParameters) AddEcdheKeypair(curveID CurveID, ecdheKey *ecdh.PrivateKey, ecdhePubKey *ecdh.PublicKey) {
return
}
func (*KeySharesParameters) GetEcdheKey(curveID CurveID) (ecdheKey *ecdh.PrivateKey, ok bool) { return }
func (*KeySharesParameters) GetEcdhePubkey(curveID CurveID) (params *ecdh.PublicKey, ok bool) { return }
func (*KeySharesParameters) AddKemKeypair(curveID CurveID, kemKey any, kemPubKey any) {
return
}
func (ksp *KeySharesParameters) GetKemKey(curveID CurveID) (kemKey any, ok bool) { return }
func (ksp *KeySharesParameters) GetKemPubkey(curveID CurveID) (params any, ok bool) { return }
+184
View File
@@ -0,0 +1,184 @@
package tls
import (
"encoding/json"
"errors"
"fmt"
"os"
"github.com/refraction-networking/utls/dicttls"
)
var ErrUnknownExtension = errors.New("extension name is unknown to the dictionary")
type ClientHelloSpecJSONUnmarshaler struct {
CipherSuites *CipherSuitesJSONUnmarshaler `json:"cipher_suites"`
CompressionMethods *CompressionMethodsJSONUnmarshaler `json:"compression_methods"`
Extensions *TLSExtensionsJSONUnmarshaler `json:"extensions"`
TLSVersMin uint16 `json:"min_vers,omitempty"` // optional
TLSVersMax uint16 `json:"max_vers,omitempty"` // optional
}
func (chsju *ClientHelloSpecJSONUnmarshaler) ClientHelloSpec() ClientHelloSpec {
return ClientHelloSpec{
CipherSuites: chsju.CipherSuites.CipherSuites(),
CompressionMethods: chsju.CompressionMethods.CompressionMethods(),
Extensions: chsju.Extensions.Extensions(),
TLSVersMin: chsju.TLSVersMin,
TLSVersMax: chsju.TLSVersMax,
}
}
type CipherSuitesJSONUnmarshaler struct {
cipherSuites []uint16
}
func (c *CipherSuitesJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
var cipherSuiteNames []string
if err := json.Unmarshal(jsonStr, &cipherSuiteNames); err != nil {
return err
}
for _, name := range cipherSuiteNames {
if name == "GREASE" {
c.cipherSuites = append(c.cipherSuites, GREASE_PLACEHOLDER)
continue
}
if id, ok := dicttls.DictCipherSuiteNameIndexed[name]; ok {
c.cipherSuites = append(c.cipherSuites, id)
} else {
return fmt.Errorf("unknown cipher suite name: %s", name)
}
}
return nil
}
func (c *CipherSuitesJSONUnmarshaler) CipherSuites() []uint16 {
return c.cipherSuites
}
type CompressionMethodsJSONUnmarshaler struct {
compressionMethods []uint8
}
func (c *CompressionMethodsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
var compressionMethodNames []string
if err := json.Unmarshal(jsonStr, &compressionMethodNames); err != nil {
return err
}
for _, name := range compressionMethodNames {
if id, ok := dicttls.DictCompMethNameIndexed[name]; ok {
c.compressionMethods = append(c.compressionMethods, id)
} else {
return fmt.Errorf("unknown compression method name: %s", name)
}
}
return nil
}
func (c *CompressionMethodsJSONUnmarshaler) CompressionMethods() []uint8 {
return c.compressionMethods
}
type TLSExtensionsJSONUnmarshaler struct {
AllowUnknownExt bool // if set, unknown extensions will be added as GenericExtension, without recovering ext payload
UseRealPSK bool // if set, PSK extension will be real PSK extension, otherwise it will be fake PSK extension
extensions []TLSExtensionJSON
}
func (e *TLSExtensionsJSONUnmarshaler) UnmarshalJSON(jsonStr []byte) error {
var accepters []tlsExtensionJSONAccepter
if err := json.Unmarshal(jsonStr, &accepters); err != nil {
return err
}
var exts []TLSExtensionJSON = make([]TLSExtensionJSON, 0, len(accepters))
for _, accepter := range accepters {
if accepter.extNameOnly.Name == "GREASE" {
exts = append(exts, &UtlsGREASEExtension{})
continue
}
if extID, ok := dicttls.DictExtTypeNameIndexed[accepter.extNameOnly.Name]; !ok {
return fmt.Errorf("%w: %s", ErrUnknownExtension, accepter.extNameOnly.Name)
} else {
// get extension type from ID
var ext TLSExtension = ExtensionFromID(extID)
if ext == nil {
if e.AllowUnknownExt {
// fallback to generic extension, without recovering ext payload
ext = genericExtension(extID, accepter.extNameOnly.Name)
} else {
return fmt.Errorf("extension %s (%d) is not JSON compatible", accepter.extNameOnly.Name, extID)
}
}
switch extID {
case extensionPreSharedKey:
// PSK extension, need to see if we do real or fake PSK
if e.UseRealPSK {
ext = &UtlsPreSharedKeyExtension{}
} else {
ext = &FakePreSharedKeyExtension{}
}
}
if extJsonCompatible, ok := ext.(TLSExtensionJSON); ok {
exts = append(exts, extJsonCompatible)
} else {
return fmt.Errorf("extension %s (%d) is not JSON compatible", accepter.extNameOnly.Name, extID)
}
}
}
// unmashal extensions
for idx, ext := range exts {
// json.Unmarshal will call the UnmarshalJSON method of the extension
if err := json.Unmarshal(accepters[idx].origJsonInput, ext); err != nil {
return err
}
}
e.extensions = exts
return nil
}
func (e *TLSExtensionsJSONUnmarshaler) Extensions() []TLSExtension {
var exts []TLSExtension = make([]TLSExtension, 0, len(e.extensions))
for _, ext := range e.extensions {
exts = append(exts, ext)
}
return exts
}
func genericExtension(id uint16, name string) TLSExtension {
var warningMsg string = "WARNING: extension "
warningMsg += fmt.Sprintf("%d ", id)
if len(name) > 0 {
warningMsg += fmt.Sprintf("(%s) ", name)
}
warningMsg += "is falling back to generic extension"
warningMsg += "\n"
fmt.Fprint(os.Stderr, warningMsg)
// fallback to generic extension
return &GenericExtension{Id: id}
}
type tlsExtensionJSONAccepter struct {
extNameOnly struct {
Name string `json:"name"`
}
origJsonInput []byte
}
func (t *tlsExtensionJSONAccepter) UnmarshalJSON(jsonStr []byte) error {
t.origJsonInput = make([]byte, len(jsonStr))
copy(t.origJsonInput, jsonStr)
return json.Unmarshal(jsonStr, &t.extNameOnly)
}
+821
View File
@@ -0,0 +1,821 @@
// 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 (
"crypto/hmac"
"crypto/sha512"
"encoding/json"
"errors"
"fmt"
"hash"
"log"
"github.com/refraction-networking/utls/internal/helper"
"golang.org/x/crypto/cryptobyte"
)
// Naming convention:
// Unsupported things are prefixed with "Fake"
// Things, supported by utls, but not crypto/tls' are prefixed with "utls"
// Supported things, that have changed their ID are prefixed with "Old"
// Supported but disabled things are prefixed with "Disabled". We will _enable_ them.
// TLS handshake message types.
const (
utlsTypeEncryptedExtensions uint8 = 8 // implemention incomplete by crypto/tls
// https://datatracker.ietf.org/doc/html/rfc8879#section-7.2
utlsTypeCompressedCertificate uint8 = 25
)
// TLS
const (
extensionNextProtoNeg uint16 = 13172 // not IANA assigned. Removed by crypto/tls since Nov 2019
utlsExtensionPadding uint16 = 21
utlsExtensionCompressCertificate uint16 = 27 // https://datatracker.ietf.org/doc/html/rfc8879#section-7.1
utlsExtensionApplicationSettings uint16 = 17513 // not IANA assigned
utlsExtensionApplicationSettingsNew uint16 = 17613 // not IANA assigned
utlsFakeExtensionCustom uint16 = 1234 // not IANA assigned, for ALPS
utlsExtensionECH uint16 = 0xfe0d // draft-ietf-tls-esni-17
utlsExtensionECHOuterExtensions uint16 = 0xfd00 // draft-ietf-tls-esni-17
// extensions with 'fake' prefix break connection, if server echoes them back
fakeExtensionEncryptThenMAC uint16 = 22
fakeExtensionTokenBinding uint16 = 24
fakeExtensionDelegatedCredentials uint16 = 34
fakeExtensionPreSharedKey uint16 = 41
fakeOldExtensionChannelID uint16 = 30031 // not IANA assigned
fakeExtensionChannelID uint16 = 30032 // not IANA assigned
)
const (
OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc13)
OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc14)
DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = uint16(0xc024)
DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = uint16(0xc028)
DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256 = uint16(0x003d)
FAKE_OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc15) // we can try to craft these ciphersuites
FAKE_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = uint16(0x009e) // from existing pieces, if needed
FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = uint16(0x0033)
FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = uint16(0x0039)
FAKE_TLS_RSA_WITH_RC4_128_MD5 = uint16(0x0004)
FAKE_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = uint16(0x009f)
FAKE_TLS_DHE_DSS_WITH_AES_128_CBC_SHA = uint16(0x0032)
FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = uint16(0x006b)
FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = uint16(0x0067)
FAKE_TLS_EMPTY_RENEGOTIATION_INFO_SCSV = uint16(0x00ff)
// https://docs.microsoft.com/en-us/dotnet/api/system.net.security.tlsciphersuite?view=netcore-3.1
FAKE_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = uint16(0xc008)
)
const (
CurveSECP256R1 CurveID = 0x0017
CurveSECP384R1 CurveID = 0x0018
CurveSECP521R1 CurveID = 0x0019
CurveX25519 CurveID = 0x001d
FakeCurveFFDHE2048 CurveID = 0x0100
FakeCurveFFDHE3072 CurveID = 0x0101
FakeCurveFFDHE4096 CurveID = 0x0102
FakeCurveFFDHE6144 CurveID = 0x0103
FakeCurveFFDHE8192 CurveID = 0x0104
)
const (
X25519Kyber768Draft00 CurveID = 0x6399
FakeCurveX25519Kyber512Draft00 CurveID = 0xfe30
FakeCurveX25519Kyber768Draft00Old CurveID = 0xfe31
FakeCurveP256Kyber768Draft00 CurveID = 0xfe32
X25519Kyber512Draft00 CurveID = FakeCurveX25519Kyber512Draft00
X25519Kyber768Draft00Old CurveID = FakeCurveX25519Kyber768Draft00Old
P256Kyber768Draft00 CurveID = FakeCurveP256Kyber768Draft00
)
// Other things
const (
fakeRecordSizeLimit uint16 = 0x001c
)
// newest signatures
var (
FakePKCS1WithSHA224 SignatureScheme = 0x0301
FakeECDSAWithSHA224 SignatureScheme = 0x0303
FakeSHA1WithDSA SignatureScheme = 0x0202
FakeSHA256WithDSA SignatureScheme = 0x0402
// fakeEd25519 = SignatureAndHash{0x08, 0x07}
// fakeEd448 = SignatureAndHash{0x08, 0x08}
)
// fake curves(groups)
var (
FakeFFDHE2048 = uint16(0x0100)
FakeFFDHE3072 = uint16(0x0101)
)
// https://tools.ietf.org/html/draft-ietf-tls-certificate-compression-04
type CertCompressionAlgo uint16
const (
CertCompressionZlib CertCompressionAlgo = 0x0001
CertCompressionBrotli CertCompressionAlgo = 0x0002
CertCompressionZstd CertCompressionAlgo = 0x0003
)
const (
PskModePlain uint8 = pskModePlain
PskModeDHE uint8 = pskModeDHE
)
type ClientHelloID struct {
Client string
// Version specifies version of a mimicked clients (e.g. browsers).
// Not used in randomized, custom handshake, and default Go.
Version string
// Seed is only used for randomized fingerprints to seed PRNG.
// Must not be modified once set.
Seed *PRNGSeed
// Weights are only used for randomized fingerprints in func
// generateRandomizedSpec(). Must not be modified once set.
Weights *Weights
}
func (p *ClientHelloID) Str() string {
return fmt.Sprintf("%s-%s", p.Client, p.Version)
}
func (p *ClientHelloID) IsSet() bool {
return (p.Client == "") && (p.Version == "")
}
const (
// clients
helloGolang = "Golang"
helloRandomized = "Randomized"
helloRandomizedALPN = "Randomized-ALPN"
helloRandomizedNoALPN = "Randomized-NoALPN"
helloCustom = "Custom"
helloFirefox = "Firefox"
helloChrome = "Chrome"
helloIOS = "iOS"
helloAndroid = "Android"
helloEdge = "Edge"
helloSafari = "Safari"
hello360 = "360Browser"
helloQQ = "QQBrowser"
// versions
helloAutoVers = "0"
)
type ClientHelloSpec struct {
CipherSuites []uint16 // nil => default
CompressionMethods []uint8 // nil => no compression
Extensions []TLSExtension // nil => no extensions
TLSVersMin uint16 // [1.0-1.3] default: parse from .Extensions, if SupportedVersions ext is not present => 1.0
TLSVersMax uint16 // [1.2-1.3] default: parse from .Extensions, if SupportedVersions ext is not present => 1.2
// GreaseStyle: currently only random
// sessionID may or may not depend on ticket; nil => random
GetSessionID func(ticket []byte) [32]byte
// TLSFingerprintLink string // ?? link to tlsfingerprint.io for informational purposes
}
// ReadCipherSuites is a helper function to construct a list of cipher suites from
// a []byte into []uint16.
//
// example: []byte{0x13, 0x01, 0x13, 0x02, 0x13, 0x03} => []uint16{0x1301, 0x1302, 0x1303}
func (chs *ClientHelloSpec) ReadCipherSuites(b []byte) error {
cipherSuites := []uint16{}
s := cryptobyte.String(b)
for !s.Empty() {
var suite uint16
if !s.ReadUint16(&suite) {
return errors.New("unable to read ciphersuite")
}
cipherSuites = append(cipherSuites, unGREASEUint16(suite))
}
chs.CipherSuites = cipherSuites
return nil
}
// ReadCompressionMethods is a helper function to construct a list of compression
// methods from a []byte into []uint8.
func (chs *ClientHelloSpec) ReadCompressionMethods(compressionMethods []byte) error {
chs.CompressionMethods = compressionMethods
return nil
}
// ReadTLSExtensions is a helper function to construct a list of TLS extensions from
// a byte slice into []TLSExtension.
func (chs *ClientHelloSpec) ReadTLSExtensions(b []byte, allowBluntMimicry bool, realPSK bool) error {
extensions := cryptobyte.String(b)
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) {
return fmt.Errorf("unable to read extension ID")
}
if !extensions.ReadUint16LengthPrefixed(&extData) {
return fmt.Errorf("unable to read data for extension %x", extension)
}
ext := ExtensionFromID(extension)
extWriter, ok := ext.(TLSExtensionWriter)
if ext != nil && ok { // known extension and implements TLSExtensionWriter properly
switch extension {
case extensionPreSharedKey:
// PSK extension, need to see if we do real or fake PSK
if realPSK {
extWriter = &UtlsPreSharedKeyExtension{}
} else {
extWriter = &FakePreSharedKeyExtension{}
}
case extensionSupportedVersions:
chs.TLSVersMin = 0
chs.TLSVersMax = 0
}
if _, err := extWriter.Write(extData); err != nil {
return err
}
chs.Extensions = append(chs.Extensions, extWriter)
} else {
if allowBluntMimicry {
chs.Extensions = append(chs.Extensions, &GenericExtension{extension, extData})
} else {
return fmt.Errorf("unsupported extension %d", extension)
}
}
}
return nil
}
func (chs *ClientHelloSpec) AlwaysAddPadding() {
alreadyHasPadding := false
for idx, ext := range chs.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
alreadyHasPadding = true
break
}
if _, ok := ext.(PreSharedKeyExtension); ok {
alreadyHasPadding = true // PSK must be last, so we can't append padding after it
// instead we will insert padding before PSK
chs.Extensions = append(chs.Extensions[:idx], append([]TLSExtension{&UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle}}, chs.Extensions[idx:]...)...)
break
}
}
if !alreadyHasPadding {
chs.Extensions = append(chs.Extensions, &UtlsPaddingExtension{GetPaddingLen: BoringPaddingStyle})
}
}
// Import TLS ClientHello data from client.tlsfingerprint.io:8443
//
// data is a map of []byte with following keys:
// - cipher_suites: [10, 10, 19, 1, 19, 2, 19, 3, 192, 43, 192, 47, 192, 44, 192, 48, 204, 169, 204, 168, 192, 19, 192, 20, 0, 156, 0, 157, 0, 47, 0, 53]
// - compression_methods: [0] => null
// - extensions: [10, 10, 255, 1, 0, 45, 0, 35, 0, 16, 68, 105, 0, 11, 0, 43, 0, 18, 0, 13, 0, 0, 0, 10, 0, 27, 0, 5, 0, 51, 0, 23, 10, 10, 0, 21]
// - pt_fmts (ec_point_formats): [1, 0] => len: 1, content: 0x00
// - sig_algs signature_algorithms): [0, 16, 4, 3, 8, 4, 4, 1, 5, 3, 8, 5, 5, 1, 8, 6, 6, 1] => len: 16, content: 0x0403, 0x0804, 0x0401, 0x0503, 0x0805, 0x0501, 0x0806, 0x0601
// - supported_versions: [10, 10, 3, 4, 3, 3] => 0x0a0a, 0x0304, 0x0303 (GREASE, TLS 1.3, TLS 1.2)
// - curves (named_groups, supported_groups): [0, 8, 10, 10, 0, 29, 0, 23, 0, 24] => len: 8, content: GREASE, 0x001d, 0x0017, 0x0018
// - alpn: [0, 12, 2, 104, 50, 8, 104, 116, 116, 112, 47, 49, 46, 49] => len: 12, content: h2, http/1.1
// - key_share: [10, 10, 0, 1, 0, 29, 0, 32] => {group: 0x0a0a, len:1}, {group: 0x001d, len:32}
// - psk_key_exchange_modes: [1] => psk_dhe_ke(0x01)
// - cert_compression_algs: [2, 0, 2] => brotli (0x0002)
// - record_size_limit: [0, 255] => 255
//
// TLSVersMin/TLSVersMax are set to 0 if supported_versions is present.
// To prevent conflict, they should be set manually if needed BEFORE calling this function.
func (chs *ClientHelloSpec) ImportTLSClientHello(data map[string][]byte) error {
var tlsExtensionTypes []uint16
var err error
if data["cipher_suites"] == nil {
return errors.New("cipher_suites is required")
}
chs.CipherSuites, err = helper.Uint8to16(data["cipher_suites"])
if err != nil {
return err
}
if data["compression_methods"] == nil {
return errors.New("compression_methods is required")
}
chs.CompressionMethods = data["compression_methods"]
if data["extensions"] == nil {
return errors.New("extensions is required")
}
tlsExtensionTypes, err = helper.Uint8to16(data["extensions"])
if err != nil {
return err
}
for _, extType := range tlsExtensionTypes {
extension := ExtensionFromID(extType)
extWriter, ok := extension.(TLSExtensionWriter)
if !ok {
return fmt.Errorf("unsupported extension %d", extType)
}
if extension == nil || !ok {
log.Printf("[Warning] Unsupported extension %d added as a &GenericExtension without Data", extType)
chs.Extensions = append(chs.Extensions, &GenericExtension{extType, []byte{}})
} else {
switch extType {
case extensionSupportedPoints:
if data["pt_fmts"] == nil {
return errors.New("pt_fmts is required")
}
_, err = extWriter.Write(data["pt_fmts"])
if err != nil {
return err
}
case extensionSignatureAlgorithms:
if data["sig_algs"] == nil {
return errors.New("sig_algs is required")
}
_, err = extWriter.Write(data["sig_algs"])
if err != nil {
return err
}
case extensionSupportedVersions:
chs.TLSVersMin = 0
chs.TLSVersMax = 0
if data["supported_versions"] == nil {
return errors.New("supported_versions is required")
}
// need to add uint8 length prefix
fixedData := make([]byte, len(data["supported_versions"])+1)
fixedData[0] = uint8(len(data["supported_versions"]) & 0xff)
copy(fixedData[1:], data["supported_versions"])
_, err = extWriter.Write(fixedData)
if err != nil {
return err
}
case extensionSupportedCurves:
if data["curves"] == nil {
return errors.New("curves is required")
}
_, err = extWriter.Write(data["curves"])
if err != nil {
return err
}
case extensionALPN:
if data["alpn"] == nil {
return errors.New("alpn is required")
}
_, err = extWriter.Write(data["alpn"])
if err != nil {
return err
}
case extensionKeyShare:
if data["key_share"] == nil {
return errors.New("key_share is required")
}
// need to add (zero) data per each key share, [10, 10, 0, 1] => [10, 10, 0, 1, 0]
fixedData := make([]byte, 0)
for i := 0; i < len(data["key_share"]); i += 4 {
fixedData = append(fixedData, data["key_share"][i:i+4]...)
for j := 0; j < int(data["key_share"][i+3]); j++ {
fixedData = append(fixedData, 0)
}
}
// add uint16 length prefix
fixedData = append([]byte{uint8(len(fixedData) >> 8), uint8(len(fixedData) & 0xff)}, fixedData...)
_, err = extWriter.Write(fixedData)
if err != nil {
return err
}
case extensionPSKModes:
if data["psk_key_exchange_modes"] == nil {
return errors.New("psk_key_exchange_modes is required")
}
// need to add uint8 length prefix
fixedData := make([]byte, len(data["psk_key_exchange_modes"])+1)
fixedData[0] = uint8(len(data["psk_key_exchange_modes"]) & 0xff)
copy(fixedData[1:], data["psk_key_exchange_modes"])
_, err = extWriter.Write(fixedData)
if err != nil {
return err
}
case utlsExtensionCompressCertificate:
if data["cert_compression_algs"] == nil {
return errors.New("cert_compression_algs is required")
}
// need to add uint8 length prefix
fixedData := make([]byte, len(data["cert_compression_algs"])+1)
fixedData[0] = uint8(len(data["cert_compression_algs"]) & 0xff)
copy(fixedData[1:], data["cert_compression_algs"])
_, err = extWriter.Write(fixedData)
if err != nil {
return err
}
case fakeRecordSizeLimit:
if data["record_size_limit"] == nil {
return errors.New("record_size_limit is required")
}
_, err = extWriter.Write(data["record_size_limit"]) // uint16 as []byte
if err != nil {
return err
}
case utlsExtensionApplicationSettings:
// TODO: tlsfingerprint.io should record/provide application settings data
extWriter.(*ApplicationSettingsExtension).SupportedProtocols = []string{"h2"}
case utlsExtensionApplicationSettingsNew:
extWriter.(*ApplicationSettingsExtensionNew).SupportedProtocols = []string{"h2"}
case extensionPreSharedKey:
log.Printf("[Warning] PSK extension added without data")
default:
if !isGREASEUint16(extType) {
log.Printf("[Warning] extension %d added without data", extType)
} /*else {
log.Printf("[Warning] GREASE extension added but ID/Data discarded. They will be automatically re-GREASEd on ApplyPreset() call.")
}*/
}
chs.Extensions = append(chs.Extensions, extWriter)
}
}
return nil
}
// ImportTLSClientHelloFromJSON imports ClientHelloSpec from JSON data from client.tlsfingerprint.io format
//
// It calls ImportTLSClientHello internally after unmarshaling JSON data into map[string][]byte
func (chs *ClientHelloSpec) ImportTLSClientHelloFromJSON(jsonB []byte) error {
var data map[string][]byte
err := json.Unmarshal(jsonB, &data)
if err != nil {
return err
}
return chs.ImportTLSClientHello(data)
}
// FromRaw converts a ClientHello message in the form of raw bytes into a ClientHelloSpec.
//
// ctrlFlags: []bool{bluntMimicry, realPSK}
func (chs *ClientHelloSpec) FromRaw(raw []byte, ctrlFlags ...bool) error {
if chs == nil {
return errors.New("cannot unmarshal into nil ClientHelloSpec")
}
var bluntMimicry = false
var realPSK = false
if len(ctrlFlags) > 0 {
bluntMimicry = ctrlFlags[0]
}
if len(ctrlFlags) > 1 {
realPSK = ctrlFlags[1]
}
*chs = ClientHelloSpec{} // reset
s := cryptobyte.String(raw)
var contentType uint8
var recordVersion uint16
if !s.ReadUint8(&contentType) || // record type
!s.ReadUint16(&recordVersion) || !s.Skip(2) { // record version and length
return errors.New("unable to read record type, version, and length")
}
if recordType(contentType) != recordTypeHandshake {
return errors.New("record is not a handshake")
}
var handshakeVersion uint16
var handshakeType uint8
if !s.ReadUint8(&handshakeType) || !s.Skip(3) || // message type and 3 byte length
!s.ReadUint16(&handshakeVersion) || !s.Skip(32) { // 32 byte random
return errors.New("unable to read handshake message type, length, and random")
}
if handshakeType != typeClientHello {
return errors.New("handshake message is not a ClientHello")
}
chs.TLSVersMin = recordVersion
chs.TLSVersMax = handshakeVersion
var ignoredSessionID cryptobyte.String
if !s.ReadUint8LengthPrefixed(&ignoredSessionID) {
return errors.New("unable to read session id")
}
// CipherSuites
var cipherSuitesBytes cryptobyte.String
if !s.ReadUint16LengthPrefixed(&cipherSuitesBytes) {
return errors.New("unable to read ciphersuites")
}
if err := chs.ReadCipherSuites(cipherSuitesBytes); err != nil {
return err
}
// CompressionMethods
var compressionMethods cryptobyte.String
if !s.ReadUint8LengthPrefixed(&compressionMethods) {
return errors.New("unable to read compression methods")
}
if err := chs.ReadCompressionMethods(compressionMethods); err != nil {
return err
}
if s.Empty() {
// Extensions are optional
return nil
}
var extensions cryptobyte.String
if !s.ReadUint16LengthPrefixed(&extensions) {
return errors.New("unable to read extensions data")
}
if err := chs.ReadTLSExtensions(extensions, bluntMimicry, realPSK); err != nil {
return err
}
// if extension list includes padding, we update the padding-to-len according to
// the raw ClientHello length
for _, ext := range chs.Extensions {
if _, ok := ext.(*UtlsPaddingExtension); ok {
ext.(*UtlsPaddingExtension).GetPaddingLen = AlwaysPadToLen(len(raw) - 5)
break
}
}
return nil
}
// UnmarshalJSON unmarshals a ClientHello message in the form of JSON into a ClientHelloSpec.
func (chs *ClientHelloSpec) UnmarshalJSON(jsonB []byte) error {
var chsju ClientHelloSpecJSONUnmarshaler
if err := json.Unmarshal(jsonB, &chsju); err != nil {
return err
}
*chs = chsju.ClientHelloSpec()
return nil
}
var (
// HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL
// overwrite your changes to Hello(Config, Session are fine).
// You might want to call BuildHandshakeState() before applying any changes.
// UConn.Extensions will be completely ignored.
HelloGolang = ClientHelloID{helloGolang, helloAutoVers, nil, nil}
// HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with
// TLSExtensions manually or use ApplyPreset function
HelloCustom = ClientHelloID{helloCustom, helloAutoVers, nil, nil}
// HelloRandomized* randomly adds/reorders extensions, ciphersuites, etc.
HelloRandomized = ClientHelloID{helloRandomized, helloAutoVers, nil, nil}
HelloRandomizedALPN = ClientHelloID{helloRandomizedALPN, helloAutoVers, nil, nil}
HelloRandomizedNoALPN = ClientHelloID{helloRandomizedNoALPN, helloAutoVers, nil, nil}
// The rest will will parrot given browser.
HelloFirefox_Auto = HelloFirefox_120
HelloFirefox_55 = ClientHelloID{helloFirefox, "55", nil, nil}
HelloFirefox_56 = ClientHelloID{helloFirefox, "56", nil, nil}
HelloFirefox_63 = ClientHelloID{helloFirefox, "63", nil, nil}
HelloFirefox_65 = ClientHelloID{helloFirefox, "65", nil, nil}
HelloFirefox_99 = ClientHelloID{helloFirefox, "99", nil, nil}
HelloFirefox_102 = ClientHelloID{helloFirefox, "102", nil, nil}
HelloFirefox_105 = ClientHelloID{helloFirefox, "105", nil, nil}
HelloFirefox_120 = ClientHelloID{helloFirefox, "120", nil, nil}
HelloChrome_Auto = HelloChrome_133
HelloChrome_58 = ClientHelloID{helloChrome, "58", nil, nil}
HelloChrome_62 = ClientHelloID{helloChrome, "62", nil, nil}
HelloChrome_70 = ClientHelloID{helloChrome, "70", nil, nil}
HelloChrome_72 = ClientHelloID{helloChrome, "72", nil, nil}
HelloChrome_83 = ClientHelloID{helloChrome, "83", nil, nil}
HelloChrome_87 = ClientHelloID{helloChrome, "87", nil, nil}
HelloChrome_96 = ClientHelloID{helloChrome, "96", nil, nil}
HelloChrome_100 = ClientHelloID{helloChrome, "100", nil, nil}
HelloChrome_102 = ClientHelloID{helloChrome, "102", nil, nil}
HelloChrome_106_Shuffle = ClientHelloID{helloChrome, "106", nil, nil} // TLS Extension shuffler enabled starting from 106
// Chrome w/ PSK: Chrome start sending this ClientHello after doing TLS 1.3 handshake with the same server.
// Beta: PSK extension added. However, uTLS doesn't ship with full PSK support.
// Use at your own discretion.
HelloChrome_100_PSK = ClientHelloID{helloChrome, "100_PSK", nil, nil}
HelloChrome_112_PSK_Shuf = ClientHelloID{helloChrome, "112_PSK", nil, nil}
HelloChrome_114_Padding_PSK_Shuf = ClientHelloID{helloChrome, "114_PSK", nil, nil}
// Chrome w/ Post-Quantum Key Agreement
// Beta: PQ extension added. However, uTLS doesn't ship with full PQ support. Use at your own discretion.
HelloChrome_115_PQ = ClientHelloID{helloChrome, "115_PQ", nil, nil}
HelloChrome_115_PQ_PSK = ClientHelloID{helloChrome, "115_PQ_PSK", nil, nil}
// Chrome ECH
HelloChrome_120 = ClientHelloID{helloChrome, "120", nil, nil}
// Chrome w/ Post-Quantum Key Agreement and Encrypted ClientHello
HelloChrome_120_PQ = ClientHelloID{helloChrome, "120_PQ", nil, nil}
// Chrome w/ ML-KEM curve
HelloChrome_131 = ClientHelloID{helloChrome, "131", nil, nil}
// Chrome w/ New ALPS codepoint
HelloChrome_133 = ClientHelloID{helloChrome, "133", nil, nil}
HelloIOS_Auto = HelloIOS_14
HelloIOS_11_1 = ClientHelloID{helloIOS, "111", nil, nil} // legacy "111" means 11.1
HelloIOS_12_1 = ClientHelloID{helloIOS, "12.1", nil, nil}
HelloIOS_13 = ClientHelloID{helloIOS, "13", nil, nil}
HelloIOS_14 = ClientHelloID{helloIOS, "14", nil, nil}
HelloAndroid_11_OkHttp = ClientHelloID{helloAndroid, "11", nil, nil}
HelloEdge_Auto = HelloEdge_85 // HelloEdge_106 seems to be incompatible with this library
HelloEdge_85 = ClientHelloID{helloEdge, "85", nil, nil}
HelloEdge_106 = ClientHelloID{helloEdge, "106", nil, nil}
HelloSafari_Auto = HelloSafari_16_0
HelloSafari_16_0 = ClientHelloID{helloSafari, "16.0", nil, nil}
Hello360_Auto = Hello360_7_5 // Hello360_11_0 seems to be incompatible with this library
Hello360_7_5 = ClientHelloID{hello360, "7.5", nil, nil}
Hello360_11_0 = ClientHelloID{hello360, "11.0", nil, nil}
HelloQQ_Auto = HelloQQ_11_1
HelloQQ_11_1 = ClientHelloID{helloQQ, "11.1", nil, nil}
)
type Weights struct {
Extensions_Append_ALPN float64
TLSVersMax_Set_VersionTLS13 float64
CipherSuites_Remove_RandomCiphers float64
SigAndHashAlgos_Append_ECDSAWithSHA1 float64
SigAndHashAlgos_Append_ECDSAWithP521AndSHA512 float64
SigAndHashAlgos_Append_PSSWithSHA256 float64
SigAndHashAlgos_Append_PSSWithSHA384_PSSWithSHA512 float64
CurveIDs_Append_X25519 float64
CurveIDs_Append_CurveP521 float64
Extensions_Append_Padding float64
Extensions_Append_Status float64
Extensions_Append_SCT float64
Extensions_Append_Reneg float64
Extensions_Append_EMS float64
FirstKeyShare_Set_CurveP256 float64
KeyShare_Append_RandomGroups float64
Extensions_Append_ALPS float64
}
// Do not modify them directly as they may being used. If you
// want to use your custom weights, please make a copy first.
var DefaultWeights = Weights{
Extensions_Append_ALPN: 0.7,
TLSVersMax_Set_VersionTLS13: 0.4,
CipherSuites_Remove_RandomCiphers: 0.4,
SigAndHashAlgos_Append_ECDSAWithSHA1: 0.63,
SigAndHashAlgos_Append_ECDSAWithP521AndSHA512: 0.59,
SigAndHashAlgos_Append_PSSWithSHA256: 0.51,
SigAndHashAlgos_Append_PSSWithSHA384_PSSWithSHA512: 0.9,
CurveIDs_Append_X25519: 0.71,
CurveIDs_Append_CurveP521: 0.46,
Extensions_Append_Padding: 0.62,
Extensions_Append_Status: 0.74,
Extensions_Append_SCT: 0.46,
Extensions_Append_Reneg: 0.75,
Extensions_Append_EMS: 0.77,
FirstKeyShare_Set_CurveP256: 0.00, // legacy setting
KeyShare_Append_RandomGroups: 0.50,
Extensions_Append_ALPS: 0.33,
}
// based on spec's GreaseStyle, GREASE_PLACEHOLDER may be replaced by another GREASE value
// https://tools.ietf.org/html/draft-ietf-tls-grease-01
const GREASE_PLACEHOLDER = 0x0a0a
func isGREASEUint16(v uint16) bool {
// First byte is same as second byte
// and lowest nibble is 0xa
return ((v >> 8) == v&0xff) && v&0xf == 0xa
}
func unGREASEUint16(v uint16) uint16 {
if isGREASEUint16(v) {
return GREASE_PLACEHOLDER
} else {
return v
}
}
// utlsMacSHA384 returns a SHA-384 based MAC. These are only supported in TLS 1.2
// so the given version is ignored.
func utlsMacSHA384(key []byte) hash.Hash {
return hmac.New(sha512.New384, key)
}
var utlsSupportedCipherSuites []*cipherSuite
func init() {
utlsSupportedCipherSuites = append(cipherSuites, []*cipherSuite{
{OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheRSAKA,
suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
{OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdheECDSAKA,
suiteECDHE | suiteECSign | suiteTLS12, nil, nil, aeadChaCha20Poly1305},
}...)
}
// EnableWeakCiphers allows utls connections to continue in some cases, when weak cipher was chosen.
// This provides better compatibility with servers on the web, but weakens security. Feel free
// to use this option if you establish additional secure connection inside of utls connection.
// This option does not change the shape of parrots (i.e. same ciphers will be offered either way).
// Must be called before establishing any connections.
func EnableWeakCiphers() {
utlsSupportedCipherSuites = append(cipherSuites, []*cipherSuite{
{DISABLED_TLS_RSA_WITH_AES_256_CBC_SHA256, 32, 32, 16, rsaKA,
suiteTLS12, cipherAES, macSHA256, nil},
{DISABLED_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheECDSAKA,
suiteECDHE | suiteECSign | suiteTLS12 | suiteSHA384, cipherAES, utlsMacSHA384, nil},
{DISABLED_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdheRSAKA,
suiteECDHE | suiteTLS12 | suiteSHA384, cipherAES, utlsMacSHA384, nil},
}...)
}
func mapSlice[T any, U any](slice []T, transform func(T) U) []U {
newSlice := make([]U, 0, len(slice))
for _, t := range slice {
newSlice = append(newSlice, transform(t))
}
return newSlice
}
func panicOnNil(caller string, params ...any) {
for i, p := range params {
if p == nil {
panic(fmt.Sprintf("tls: %s failed: the [%d] parameter is nil", caller, i))
}
}
}
func anyTrue[T any](slice []T, predicate func(i int, t *T) bool) bool {
for i := 0; i < len(slice); i++ {
if predicate(i, &slice[i]) {
return true
}
}
return false
}
func allTrue[T any](slice []T, predicate func(i int, t *T) bool) bool {
for i := 0; i < len(slice); i++ {
if !predicate(i, &slice[i]) {
return false
}
}
return true
}
func uAssert(condition bool, msg string) {
if !condition {
panic(msg)
}
}
func sliceEq[T comparable](sliceA []T, sliceB []T) bool {
if len(sliceA) != len(sliceB) {
return false
}
for i := 0; i < len(sliceA); i++ {
if sliceA[i] != sliceB[i] {
return false
}
}
return true
}
type Initializable interface {
// IsInitialized returns a boolean indicating whether the extension has been initialized.
// If false is returned, utls will initialize the extension.
IsInitialized() bool
}
+984
View File
@@ -0,0 +1,984 @@
// 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 (
"bufio"
"bytes"
"context"
"crypto/cipher"
"encoding/binary"
"errors"
"fmt"
"hash"
"net"
"slices"
"strconv"
"golang.org/x/crypto/cryptobyte"
)
type ClientHelloBuildStatus int
const NotBuilt ClientHelloBuildStatus = 0
const BuildByUtls ClientHelloBuildStatus = 1
const BuildByGoTLS ClientHelloBuildStatus = 2
type UConn struct {
*Conn
Extensions []TLSExtension
ClientHelloID ClientHelloID
sessionController *sessionController
clientHelloBuildStatus ClientHelloBuildStatus
clientHelloSpec *ClientHelloSpec
HandshakeState PubClientHandshakeState
greaseSeed [ssl_grease_last_index]uint16
omitSNIExtension bool
// skipResumptionOnNilExtension is copied from `Config.PreferSkipResumptionOnNilExtension`.
//
// By default, if ClientHelloSpec is predefined or utls-generated (as opposed to HelloCustom), this flag will be updated to true.
skipResumptionOnNilExtension bool
// certCompressionAlgs represents the set of advertised certificate compression
// algorithms, as specified in the ClientHello. This is only relevant client-side, for the
// server certificate. All other forms of certificate compression are unsupported.
certCompressionAlgs []CertCompressionAlgo
// ech extension is a shortcut to the ECH extension in the Extensions slice if there is one.
ech ECHExtension
// echCtx is the echContex returned by makeClientHello()
echCtx *echClientContext
}
// UClient returns a new uTLS client, with behavior depending on clientHelloID.
// Config CAN be nil, but make sure to eventually specify ServerName.
func UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID) *UConn {
if config == nil {
config = &Config{}
}
tlsConn := Conn{conn: conn, config: config, isClient: true}
handshakeState := PubClientHandshakeState{C: &tlsConn, Hello: &PubClientHelloMsg{}}
uconn := UConn{Conn: &tlsConn, ClientHelloID: clientHelloID, HandshakeState: handshakeState}
uconn.HandshakeState.uconn = &uconn
uconn.handshakeFn = uconn.clientHandshake
uconn.sessionController = newSessionController(&uconn)
uconn.utls.sessionController = uconn.sessionController
uconn.skipResumptionOnNilExtension = config.PreferSkipResumptionOnNilExtension || clientHelloID.Client != helloCustom
return &uconn
}
// BuildHandshakeState behavior varies based on ClientHelloID and
// whether it was already called before.
// If HelloGolang:
//
// [only once] make default ClientHello and overwrite existing state
//
// If any other mimicking ClientHelloID is used:
//
// [only once] make ClientHello based on ID and overwrite existing state
// [each call] apply uconn.Extensions config to internal crypto/tls structures
// [each call] marshal ClientHello.
//
// BuildHandshakeState is automatically called before uTLS performs handshake,
// and should only be called explicitly to inspect/change fields of
// default/mimicked ClientHello.
// With the excpetion of session ticket and psk extensions, which cannot be changed
// after calling BuildHandshakeState, all other fields can be modified.
func (uconn *UConn) BuildHandshakeState() error {
return uconn.buildHandshakeState(true)
}
// BuildHandshakeStateWithoutSession is the same as BuildHandshakeState, but does not
// set the session. This is only useful when you want to inspect the ClientHello before
// setting the session manually through SetSessionTicketExtension or SetPSKExtension.
// BuildHandshakeState is automatically called before uTLS performs handshake.
func (uconn *UConn) BuildHandshakeStateWithoutSession() error {
return uconn.buildHandshakeState(false)
}
func (uconn *UConn) buildHandshakeState(loadSession bool) error {
if uconn.ClientHelloID == HelloGolang {
if uconn.clientHelloBuildStatus == BuildByGoTLS {
return nil
}
uAssert(uconn.clientHelloBuildStatus == NotBuilt, "BuildHandshakeState failed: invalid call, client hello has already been built by utls")
// use default Golang ClientHello.
hello, keySharePrivate, ech, err := uconn.makeClientHello()
if err != nil {
return err
}
uconn.HandshakeState.Hello = hello.getPublicPtr()
uconn.HandshakeState.State13.KeyShareKeys = keySharePrivate.ToPublic()
uconn.HandshakeState.C = uconn.Conn
uconn.echCtx = ech
uconn.clientHelloBuildStatus = BuildByGoTLS
} else {
uAssert(uconn.clientHelloBuildStatus == BuildByUtls || uconn.clientHelloBuildStatus == NotBuilt, "BuildHandshakeState failed: invalid call, client hello has already been built by go-tls")
if uconn.clientHelloBuildStatus == NotBuilt {
err := uconn.applyPresetByID(uconn.ClientHelloID)
if err != nil {
return err
}
if uconn.omitSNIExtension {
uconn.removeSNIExtension()
}
}
err := uconn.ApplyConfig()
if err != nil {
return err
}
if loadSession {
err = uconn.uLoadSession()
if err != nil {
return err
}
}
err = uconn.MarshalClientHello()
if err != nil {
return err
}
if loadSession {
uconn.uApplyPatch()
uconn.sessionController.finalCheck()
uconn.clientHelloBuildStatus = BuildByUtls
}
}
return nil
}
func (uconn *UConn) uLoadSession() error {
if cfg := uconn.config; cfg.SessionTicketsDisabled || cfg.ClientSessionCache == nil {
return nil
}
switch uconn.sessionController.shouldLoadSession() {
case shouldReturn:
case shouldSetTicket:
uconn.sessionController.setSessionTicketToUConn()
case shouldSetPsk:
uconn.sessionController.setPskToUConn()
case shouldLoad:
hello := uconn.HandshakeState.Hello.getPrivatePtr()
uconn.sessionController.utlsAboutToLoadSession()
session, earlySecret, binderKey, err := uconn.loadSession(hello)
if session == nil || err != nil {
return err
}
if session.version == VersionTLS12 {
// We use the session ticket extension for tls 1.2 session resumption
uconn.sessionController.initSessionTicketExt(session, hello.sessionTicket)
uconn.sessionController.setSessionTicketToUConn()
} else {
uconn.sessionController.initPskExt(session, earlySecret, binderKey, hello.pskIdentities)
}
}
return nil
}
func (uconn *UConn) uApplyPatch() {
helloLen := len(uconn.HandshakeState.Hello.Raw)
if uconn.sessionController.shouldUpdateBinders() {
uconn.sessionController.updateBinders()
uconn.sessionController.setPskToUConn()
}
uAssert(helloLen == len(uconn.HandshakeState.Hello.Raw), "tls: uApplyPatch Failed: the patch should never change the length of the marshaled clientHello")
}
func (uconn *UConn) DidTls12Resume() bool {
return uconn.didResume
}
// SetSessionState sets the session ticket, which may be preshared or fake.
// If session is nil, the body of session ticket extension will be unset,
// but the extension itself still MAY be present for mimicking purposes.
// Session tickets to be reused - use same cache on following connections.
//
// Deprecated: This method is deprecated in favor of SetSessionTicketExtension,
// as it only handles session override of TLS 1.2
func (uconn *UConn) SetSessionState(session *ClientSessionState) error {
sessionTicketExt := &SessionTicketExtension{Initialized: true}
if session != nil {
sessionTicketExt.Ticket = session.session.ticket
sessionTicketExt.Session = session.session
}
return uconn.SetSessionTicketExtension(sessionTicketExt)
}
// SetSessionTicket sets the session ticket extension.
// If extension is nil, this will be a no-op.
func (uconn *UConn) SetSessionTicketExtension(sessionTicketExt ISessionTicketExtension) error {
if uconn.config.SessionTicketsDisabled || uconn.config.ClientSessionCache == nil {
return fmt.Errorf("tls: SetSessionTicketExtension failed: session is disabled")
}
if sessionTicketExt == nil {
return nil
}
return uconn.sessionController.overrideSessionTicketExt(sessionTicketExt)
}
// SetPskExtension sets the psk extension for tls 1.3 resumption. This is a no-op if the psk is nil.
func (uconn *UConn) SetPskExtension(pskExt PreSharedKeyExtension) error {
if uconn.config.SessionTicketsDisabled || uconn.config.ClientSessionCache == nil {
return fmt.Errorf("tls: SetPskExtension failed: session is disabled")
}
if pskExt == nil {
return nil
}
uconn.HandshakeState.Hello.TicketSupported = true
return uconn.sessionController.overridePskExt(pskExt)
}
// If you want session tickets to be reused - use same cache on following connections
func (uconn *UConn) SetSessionCache(cache ClientSessionCache) {
uconn.config.ClientSessionCache = cache
uconn.HandshakeState.Hello.TicketSupported = true
}
// SetClientRandom sets client random explicitly.
// BuildHandshakeFirst() must be called before SetClientRandom.
// r must to be 32 bytes long.
func (uconn *UConn) SetClientRandom(r []byte) error {
if len(r) != 32 {
return errors.New("Incorrect client random length! Expected: 32, got: " + strconv.Itoa(len(r)))
} else {
uconn.HandshakeState.Hello.Random = make([]byte, 32)
copy(uconn.HandshakeState.Hello.Random, r)
return nil
}
}
func (uconn *UConn) SetSNI(sni string) {
hname := hostnameInSNI(sni)
uconn.config.ServerName = hname
for _, ext := range uconn.Extensions {
sniExt, ok := ext.(*SNIExtension)
if ok {
sniExt.ServerName = hname
}
}
}
// RemoveSNIExtension removes SNI from the list of extensions sent in ClientHello
// It returns an error when used with HelloGolang ClientHelloID
func (uconn *UConn) RemoveSNIExtension() error {
if uconn.ClientHelloID == HelloGolang {
return fmt.Errorf("cannot call RemoveSNIExtension on a UConn with a HelloGolang ClientHelloID")
}
uconn.omitSNIExtension = true
return nil
}
func (uconn *UConn) removeSNIExtension() {
filteredExts := make([]TLSExtension, 0, len(uconn.Extensions))
for _, e := range uconn.Extensions {
if _, ok := e.(*SNIExtension); !ok {
filteredExts = append(filteredExts, e)
}
}
uconn.Extensions = filteredExts
}
// Handshake runs the client handshake using given clientHandshakeState
// Requires hs.hello, and, optionally, hs.session to be set.
func (c *UConn) Handshake() error {
return c.HandshakeContext(context.Background())
}
// HandshakeContext runs the client or server handshake
// protocol if it has not yet been run.
//
// The provided Context must be non-nil. If the context is canceled before
// the handshake is complete, the handshake is interrupted and an error is returned.
// Once the handshake has completed, cancellation of the context will not affect the
// connection.
func (c *UConn) HandshakeContext(ctx context.Context) error {
// Delegate to unexported method for named return
// without confusing documented signature.
return c.handshakeContext(ctx)
}
func (c *UConn) 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.isHandshakeComplete.Load() {
return nil
}
handshakeCtx, cancel := context.WithCancel(ctx)
// Note: defer this before starting the "interrupter" goroutine
// so that we can tell the difference between the input being canceled and
// this cancellation. In the former case, we need to close the connection.
defer cancel()
// Start the "interrupter" goroutine, if this context might be canceled.
// (The background context cannot).
//
// The interrupter goroutine waits for the input context to be done and
// closes the connection if this happens before the function returns.
if c.quic != nil {
c.quic.cancelc = handshakeCtx.Done()
c.quic.cancel = cancel
} else if ctx.Done() != nil {
done := make(chan struct{})
interruptRes := make(chan error, 1)
defer func() {
close(done)
if ctxErr := <-interruptRes; ctxErr != nil {
// Return context error to user.
ret = ctxErr
}
}()
go func() {
select {
case <-handshakeCtx.Done():
// Close the connection, discarding the error
_ = c.conn.Close()
interruptRes <- handshakeCtx.Err()
case <-done:
interruptRes <- nil
}
}()
}
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if err := c.handshakeErr; err != nil {
return err
}
if c.isHandshakeComplete.Load() {
return nil
}
c.in.Lock()
defer c.in.Unlock()
// [uTLS section begins]
if c.isClient {
err := c.BuildHandshakeState()
if err != nil {
return err
}
}
// [uTLS section ends]
c.handshakeErr = c.handshakeFn(handshakeCtx)
if c.handshakeErr == nil {
c.handshakes++
} else {
// If an error occurred during the hadshake try to flush the
// alert that might be left in the buffer.
c.flush()
}
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.isHandshakeComplete.Load() {
panic("tls: internal error: handshake returned an error but is marked successful")
}
if c.quic != nil {
if c.handshakeErr == nil {
c.quicHandshakeComplete()
// Provide the 1-RTT read secret now that the handshake is complete.
// The QUIC layer MUST NOT decrypt 1-RTT packets prior to completing
// the handshake (RFC 9001, Section 5.7).
c.quicSetReadSecret(QUICEncryptionLevelApplication, c.cipherSuite, c.in.trafficSecret)
} else {
var a alert
c.out.Lock()
if !errors.As(c.out.err, &a) {
a = alertInternalError
}
c.out.Unlock()
// Return an error which wraps both the handshake error and
// any alert error we may have sent, or alertInternalError
// if we didn't send an alert.
// Truncate the text of the alert to 0 characters.
c.handshakeErr = fmt.Errorf("%w%.0w", c.handshakeErr, AlertError(a))
}
close(c.quic.blockedc)
close(c.quic.signalc)
}
return c.handshakeErr
}
// Copy-pasted from tls.Conn in its entirety. But c.Handshake() is now utls' one, not tls.
// Write writes data to the connection.
func (c *UConn) Write(b []byte) (int, error) {
// interlock with Close below
for {
x := c.activeCall.Load()
if x&1 != 0 {
return 0, net.ErrClosed
}
if c.activeCall.CompareAndSwap(x, x+2) {
defer c.activeCall.Add(-2)
break
}
}
if err := c.Handshake(); err != nil {
return 0, err
}
c.out.Lock()
defer c.out.Unlock()
if err := c.out.err; err != nil {
return 0, err
}
if !c.isHandshakeComplete.Load() {
return 0, alertInternalError
}
if c.closeNotifySent {
return 0, errShutdown
}
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext
// attack when using block mode ciphers due to predictable IVs.
// This can be prevented by splitting each Application Data
// record into two records, effectively randomizing the IV.
//
// https://www.openssl.org/~bodo/tls-cbc.txt
// https://bugzilla.mozilla.org/show_bug.cgi?id=665814
// https://www.imperialviolet.org/2012/01/15/beastfollowup.html
var m int
if len(b) > 1 && c.vers <= VersionTLS10 {
if _, ok := c.out.cipher.(cipher.BlockMode); ok {
n, err := c.writeRecordLocked(recordTypeApplicationData, b[:1])
if err != nil {
return n, c.out.setErrorLocked(err)
}
m, b = 1, b[1:]
}
}
n, err := c.writeRecordLocked(recordTypeApplicationData, b)
return n + m, c.out.setErrorLocked(err)
}
func (uconn *UConn) ApplyConfig() error {
for _, ext := range uconn.Extensions {
err := ext.writeToUConn(uconn)
if err != nil {
return err
}
}
return nil
}
func (uconn *UConn) extensionsList() []uint16 {
outerExts := []uint16{}
for _, ext := range uconn.Extensions {
buffer := cryptobyte.String(make([]byte, 2000))
ext.Read(buffer)
var extension uint16
buffer.ReadUint16(&extension)
outerExts = append(outerExts, extension)
}
return outerExts
}
func (uconn *UConn) computeAndUpdateOuterECHExtension(inner *clientHelloMsg, ech *echClientContext, useKey bool) error {
// This function is mostly copied from
// https://github.com/refraction-networking/utls/blob/e430876b1d82fdf582efc57f3992d448e7ab3d8a/ech.go#L408
var encapKey []byte
if useKey {
encapKey = ech.encapsulatedKey
}
encodedInner, err := encodeInnerClientHelloReorderOuterExts(inner, int(ech.config.MaxNameLength), uconn.extensionsList())
if err != nil {
return err
}
encryptedLen := len(encodedInner) + 16
outerECHExt, err := generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, make([]byte, encryptedLen))
if err != nil {
return err
}
echExtIdx := slices.IndexFunc(uconn.Extensions, func(ext TLSExtension) bool {
_, ok := ext.(EncryptedClientHelloExtension)
return ok
})
if echExtIdx < 0 {
return fmt.Errorf("extension satisfying EncryptedClientHelloExtension not present")
}
oldExt := uconn.Extensions[echExtIdx]
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
serializedOuter := uconn.HandshakeState.Hello.Raw
serializedOuter = serializedOuter[4:]
encryptedInner, err := ech.hpkeContext.Seal(serializedOuter, encodedInner)
if err != nil {
return err
}
outerECHExt, err = generateOuterECHExt(ech.config.ConfigID, ech.kdfID, ech.aeadID, encapKey, encryptedInner)
if err != nil {
return err
}
uconn.Extensions[echExtIdx] = &GenericExtension{
Id: extensionEncryptedClientHello,
Data: outerECHExt,
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
uconn.Extensions[echExtIdx] = oldExt
return nil
}
func (uconn *UConn) MarshalClientHello() error {
if len(uconn.config.EncryptedClientHelloConfigList) > 0 {
inner, _, ech, err := uconn.makeClientHello()
if err != nil {
return err
}
// copy compressed extensions to the ClientHelloInner
inner.keyShares = KeyShares(uconn.HandshakeState.Hello.KeyShares).ToPrivate()
inner.supportedSignatureAlgorithms = uconn.HandshakeState.Hello.SupportedSignatureAlgorithms
inner.sessionId = uconn.HandshakeState.Hello.SessionId
inner.supportedCurves = uconn.HandshakeState.Hello.SupportedCurves
ech.innerHello = inner
uconn.computeAndUpdateOuterECHExtension(inner, ech, true)
uconn.echCtx = ech
return nil
}
if err := uconn.MarshalClientHelloNoECH(); err != nil {
return err
}
return nil
}
// MarshalClientHelloNoECH marshals ClientHello as if there was no
// ECH extension present.
func (uconn *UConn) MarshalClientHelloNoECH() error {
hello := uconn.HandshakeState.Hello
headerLength := 2 + 32 + 1 + len(hello.SessionId) +
2 + len(hello.CipherSuites)*2 +
1 + len(hello.CompressionMethods)
extensionsLen := 0
var paddingExt *UtlsPaddingExtension // reference to padding extension, if present
for _, ext := range uconn.Extensions {
if pe, ok := ext.(*UtlsPaddingExtension); !ok {
// If not padding - just add length of extension to total length
extensionsLen += ext.Len()
} else {
// If padding - process it later
if paddingExt == nil {
paddingExt = pe
} else {
return errors.New("multiple padding extensions")
}
}
}
if paddingExt != nil {
// determine padding extension presence and length
paddingExt.Update(headerLength + 4 + extensionsLen + 2)
extensionsLen += paddingExt.Len()
}
helloLen := headerLength
if len(uconn.Extensions) > 0 {
helloLen += 2 + extensionsLen // 2 bytes for extensions' length
}
helloBuffer := bytes.Buffer{}
bufferedWriter := bufio.NewWriterSize(&helloBuffer, helloLen+4) // 1 byte for tls record type, 3 for length
// We use buffered Writer to avoid checking write errors after every Write(): whenever first error happens
// Write() will become noop, and error will be accessible via Flush(), which is called once in the end
binary.Write(bufferedWriter, binary.BigEndian, typeClientHello)
helloLenBytes := []byte{byte(helloLen >> 16), byte(helloLen >> 8), byte(helloLen)} // poor man's uint24
binary.Write(bufferedWriter, binary.BigEndian, helloLenBytes)
binary.Write(bufferedWriter, binary.BigEndian, hello.Vers)
binary.Write(bufferedWriter, binary.BigEndian, hello.Random)
binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.SessionId)))
binary.Write(bufferedWriter, binary.BigEndian, hello.SessionId)
binary.Write(bufferedWriter, binary.BigEndian, uint16(len(hello.CipherSuites)<<1))
for _, suite := range hello.CipherSuites {
binary.Write(bufferedWriter, binary.BigEndian, suite)
}
binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.CompressionMethods)))
binary.Write(bufferedWriter, binary.BigEndian, hello.CompressionMethods)
if len(uconn.Extensions) > 0 {
binary.Write(bufferedWriter, binary.BigEndian, uint16(extensionsLen))
for _, ext := range uconn.Extensions {
if _, err := bufferedWriter.ReadFrom(ext); err != nil {
return err
}
}
}
err := bufferedWriter.Flush()
if err != nil {
return err
}
if helloBuffer.Len() != 4+helloLen {
return errors.New("utls: unexpected ClientHello length. Expected: " + strconv.Itoa(4+helloLen) +
". Got: " + strconv.Itoa(helloBuffer.Len()))
}
hello.Raw = helloBuffer.Bytes()
return nil
}
// get current state of cipher and encrypt zeros to get keystream
func (uconn *UConn) GetOutKeystream(length int) ([]byte, error) {
zeros := make([]byte, length)
if outCipher, ok := uconn.out.cipher.(cipher.AEAD); ok {
// AEAD.Seal() does not mutate internal state, other ciphers might
return outCipher.Seal(nil, uconn.out.seq[:], zeros, nil), nil
}
return nil, errors.New("could not convert OutCipher to cipher.AEAD")
}
// SetTLSVers sets min and max TLS version in all appropriate places.
// Function will use first non-zero version parsed in following order:
// 1. Provided minTLSVers, maxTLSVers
// 2. specExtensions may have SupportedVersionsExtension
// 3. [default] min = TLS 1.0, max = TLS 1.2
//
// Error is only returned if things are in clearly undesirable state
// to help user fix them.
func (uconn *UConn) SetTLSVers(minTLSVers, maxTLSVers uint16, specExtensions []TLSExtension) error {
if minTLSVers == 0 && maxTLSVers == 0 {
// if version is not set explicitly in the ClientHelloSpec, check the SupportedVersions extension
supportedVersionsExtensionsPresent := 0
for _, e := range specExtensions {
switch ext := e.(type) {
case *SupportedVersionsExtension:
findVersionsInSupportedVersionsExtensions := func(versions []uint16) (uint16, uint16) {
// returns (minVers, maxVers)
minVers := uint16(0)
maxVers := uint16(0)
for _, vers := range versions {
if isGREASEUint16(vers) {
continue
}
if maxVers < vers || maxVers == 0 {
maxVers = vers
}
if minVers > vers || minVers == 0 {
minVers = vers
}
}
return minVers, maxVers
}
supportedVersionsExtensionsPresent += 1
minTLSVers, maxTLSVers = findVersionsInSupportedVersionsExtensions(ext.Versions)
if minTLSVers == 0 && maxTLSVers == 0 {
return fmt.Errorf("SupportedVersions extension has invalid Versions field")
} // else: proceed
}
}
switch supportedVersionsExtensionsPresent {
case 0:
// if mandatory for TLS 1.3 extension is not present, just default to 1.2
minTLSVers = VersionTLS10
maxTLSVers = VersionTLS12
case 1:
default:
return fmt.Errorf("uconn.Extensions contains %v separate SupportedVersions extensions",
supportedVersionsExtensionsPresent)
}
}
if minTLSVers < VersionTLS10 || minTLSVers > VersionTLS13 {
return fmt.Errorf("uTLS does not support 0x%X as min version", minTLSVers)
}
if maxTLSVers < VersionTLS10 || maxTLSVers > VersionTLS13 {
return fmt.Errorf("uTLS does not support 0x%X as max version", maxTLSVers)
}
uconn.HandshakeState.Hello.SupportedVersions = makeSupportedVersions(minTLSVers, maxTLSVers)
if uconn.config.EncryptedClientHelloConfigList == nil {
uconn.config.MinVersion = minTLSVers
uconn.config.MaxVersion = maxTLSVers
}
return nil
}
func (uconn *UConn) SetUnderlyingConn(c net.Conn) {
uconn.Conn.conn = c
}
func (uconn *UConn) GetUnderlyingConn() net.Conn {
return uconn.Conn.conn
}
// MakeConnWithCompleteHandshake allows to forge both server and client side TLS connections.
// Major Hack Alert.
func MakeConnWithCompleteHandshake(tcpConn net.Conn, version uint16, cipherSuite uint16, masterSecret []byte, clientRandom []byte, serverRandom []byte, isClient bool) *Conn {
tlsConn := &Conn{conn: tcpConn, config: &Config{}, isClient: isClient}
cs := cipherSuiteByID(cipherSuite)
if cs != nil {
// This is mostly borrowed from establishKeys()
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV :=
keysFromMasterSecret(version, cs, masterSecret, clientRandom, serverRandom,
cs.macLen, cs.keyLen, cs.ivLen)
var clientCipher, serverCipher interface{}
var clientHash, serverHash hash.Hash
if cs.cipher != nil {
clientCipher = cs.cipher(clientKey, clientIV, true /* for reading */)
clientHash = cs.mac(clientMAC)
serverCipher = cs.cipher(serverKey, serverIV, false /* not for reading */)
serverHash = cs.mac(serverMAC)
} else {
clientCipher = cs.aead(clientKey, clientIV)
serverCipher = cs.aead(serverKey, serverIV)
}
if isClient {
tlsConn.in.prepareCipherSpec(version, serverCipher, serverHash)
tlsConn.out.prepareCipherSpec(version, clientCipher, clientHash)
} else {
tlsConn.in.prepareCipherSpec(version, clientCipher, clientHash)
tlsConn.out.prepareCipherSpec(version, serverCipher, serverHash)
}
// skip the handshake states
tlsConn.isHandshakeComplete.Store(true)
tlsConn.cipherSuite = cipherSuite
tlsConn.haveVers = true
tlsConn.vers = version
// Update to the new cipher specs
// and consume the finished messages
tlsConn.in.changeCipherSpec()
tlsConn.out.changeCipherSpec()
tlsConn.in.incSeq()
tlsConn.out.incSeq()
return tlsConn
} else {
// TODO: Support TLS 1.3 Cipher Suites
return nil
}
}
func makeSupportedVersions(minVers, maxVers uint16) []uint16 {
a := make([]uint16, maxVers-minVers+1)
for i := range a {
a[i] = maxVers - uint16(i)
}
return a
}
// Extending (*Conn).readHandshake() to support more customized handshake messages.
func (c *Conn) utlsHandshakeMessageType(msgType byte) (handshakeMessage, error) {
switch msgType {
case utlsTypeCompressedCertificate:
return new(utlsCompressedCertificateMsg), nil
case utlsTypeEncryptedExtensions:
if c.isClient {
return new(encryptedExtensionsMsg), nil
} else {
return new(utlsClientEncryptedExtensionsMsg), nil
}
default:
return nil, c.in.setErrorLocked(c.sendAlert(alertUnexpectedMessage))
}
}
// Extending (*Conn).connectionStateLocked()
func (c *Conn) utlsConnectionStateLocked(state *ConnectionState) {
state.PeerApplicationSettings = c.utls.peerApplicationSettings
}
type utlsConnExtraFields struct {
// Application Settings (ALPS)
peerApplicationSettings []byte
localApplicationSettings []byte
applicationSettingsCodepoint uint16
sessionController *sessionController
}
// Read reads data from the connection.
//
// As Read calls [Conn.Handshake], in order to prevent indefinite blocking a deadline
// must be set for both Read and [Conn.Write] before Read is called when the handshake
// has not yet completed. See [Conn.SetDeadline], [Conn.SetReadDeadline], and
// [Conn.SetWriteDeadline].
func (c *UConn) Read(b []byte) (int, error) {
if err := c.Handshake(); err != nil {
return 0, err
}
if len(b) == 0 {
// Put this after Handshake, in case people were calling
// Read(nil) for the side effect of the Handshake.
return 0, nil
}
c.in.Lock()
defer c.in.Unlock()
for c.input.Len() == 0 {
if err := c.readRecord(); err != nil {
return 0, err
}
for c.hand.Len() > 0 {
if err := c.handlePostHandshakeMessage(); err != nil {
return 0, err
}
}
}
n, _ := c.input.Read(b)
// If a close-notify alert is waiting, read it so that we can return (n,
// EOF) instead of (n, nil), to signal to the HTTP response reading
// goroutine that the connection is now closed. This eliminates a race
// where the HTTP response reading goroutine would otherwise not observe
// the EOF until its next read, by which time a client goroutine might
// have already tried to reuse the HTTP connection for a new request.
// See https://golang.org/cl/76400046 and https://golang.org/issue/3514
if n != 0 && c.input.Len() == 0 && c.rawInput.Len() > 0 &&
recordType(c.rawInput.Bytes()[0]) == recordTypeAlert {
if err := c.readRecord(); err != nil {
return n, err // will be io.EOF on closeNotify
}
}
return n, nil
}
// handleRenegotiation processes a HelloRequest handshake message.
func (c *UConn) handleRenegotiation() error {
if c.vers == VersionTLS13 {
return errors.New("tls: internal error: unexpected renegotiation")
}
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
helloReq, ok := msg.(*helloRequestMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(helloReq, msg)
}
if !c.isClient {
return c.sendAlert(alertNoRenegotiation)
}
switch c.config.Renegotiation {
case RenegotiateNever:
return c.sendAlert(alertNoRenegotiation)
case RenegotiateOnceAsClient:
if c.handshakes > 1 {
return c.sendAlert(alertNoRenegotiation)
}
case RenegotiateFreelyAsClient:
// Ok.
default:
c.sendAlert(alertInternalError)
return errors.New("tls: unknown Renegotiation value")
}
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
c.isHandshakeComplete.Store(false)
// [uTLS section begins]
if err = c.BuildHandshakeState(); err != nil {
return err
}
// [uTLS section ends]
if c.handshakeErr = c.clientHandshake(context.Background()); c.handshakeErr == nil {
c.handshakes++
}
return c.handshakeErr
}
// handlePostHandshakeMessage processes a handshake message arrived after the
// handshake is complete. Up to TLS 1.2, it indicates the start of a renegotiation.
func (c *UConn) handlePostHandshakeMessage() error {
if c.vers != VersionTLS13 {
return c.handleRenegotiation()
}
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
c.retryCount++
if c.retryCount > maxUselessRecords {
c.sendAlert(alertUnexpectedMessage)
return c.in.setErrorLocked(errors.New("tls: too many non-advancing records"))
}
switch msg := msg.(type) {
case *newSessionTicketMsgTLS13:
return c.handleNewSessionTicket(msg)
case *keyUpdateMsg:
return c.handleKeyUpdate(msg)
}
// The QUIC layer is supposed to treat an unexpected post-handshake CertificateRequest
// as a QUIC-level PROTOCOL_VIOLATION error (RFC 9001, Section 4.4). Returning an
// unexpected_message alert here doesn't provide it with enough information to distinguish
// this condition from other unexpected messages. This is probably fine.
c.sendAlert(alertUnexpectedMessage)
return fmt.Errorf("tls: received unexpected handshake message of type %T", msg)
}
+306
View File
@@ -0,0 +1,306 @@
package tls
import (
"crypto/rand"
"errors"
"fmt"
"io"
"math/big"
"sync"
"github.com/refraction-networking/utls/dicttls"
"github.com/refraction-networking/utls/internal/hpke"
"golang.org/x/crypto/cryptobyte"
)
// Unstable API: This is a work in progress and may change in the future. Using
// it in your application may cause your application to break when updating to
// a new version of uTLS.
const (
OuterClientHello byte = 0x00
InnerClientHello byte = 0x01
)
type EncryptedClientHelloExtension interface {
// TLSExtension must be implemented by all EncryptedClientHelloExtension implementations.
TLSExtension
// MarshalClientHello is called by (*UConn).MarshalClientHello() when an ECH extension
// is present to allow the ECH extension to take control of the generation of the
// entire ClientHello message.
MarshalClientHello(*UConn) error
mustEmbedUnimplementedECHExtension()
}
type ECHExtension = EncryptedClientHelloExtension // alias
// type guard: GREASEEncryptedClientHelloExtension must implement EncryptedClientHelloExtension
var (
_ EncryptedClientHelloExtension = (*GREASEEncryptedClientHelloExtension)(nil)
_ EncryptedClientHelloExtension = (*UnimplementedECHExtension)(nil)
)
type GREASEEncryptedClientHelloExtension struct {
CandidateCipherSuites []HPKESymmetricCipherSuite
cipherSuite HPKESymmetricCipherSuite // randomly picked from CandidateCipherSuites or generated if empty
CandidateConfigIds []uint8
configId uint8 // randomly picked from CandidateConfigIds or generated if empty
EncapsulatedKey []byte // if empty, will generate random bytes
CandidatePayloadLens []uint16 // Pre-encryption. If 0, will pick 128(+16=144)
payload []byte // payload should be calculated ONCE and stored here, HRR will reuse this
initOnce sync.Once
UnimplementedECHExtension
}
type GREASEECHExtension = GREASEEncryptedClientHelloExtension // alias
// init initializes the GREASEEncryptedClientHelloExtension with random values if they are not set.
//
// Based on cloudflare/go's echGenerateGreaseExt()
func (g *GREASEEncryptedClientHelloExtension) init() error {
var initErr error
g.initOnce.Do(func() {
// Set the config_id field to a random byte.
//
// Note: must not reuse this extension unless for HRR. It is required
// to generate new random bytes for config_id for each new ClientHello,
// but reuse the same config_id for HRR.
if len(g.CandidateConfigIds) == 0 {
var b []byte = make([]byte, 1)
_, err := rand.Read(b[:])
if err != nil {
initErr = fmt.Errorf("error generating random byte for config_id: %w", err)
return
}
g.configId = b[0]
} else {
// randomly pick one from the list
rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidateConfigIds))))
if err != nil {
initErr = fmt.Errorf("error generating random index for config_id: %w", err)
return
}
g.configId = g.CandidateConfigIds[rndIndex.Int64()]
}
// Set the cipher_suite field to a supported HpkeSymmetricCipherSuite.
// The selection SHOULD vary to exercise all supported configurations,
// but MAY be held constant for successive connections to the same server
// in the same session.
if len(g.CandidateCipherSuites) == 0 {
g.cipherSuite = HPKESymmetricCipherSuite{uint16(defaultHpkeKdf), uint16(defaultHpkeAead)}
} else {
// randomly pick one from the list
rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidateCipherSuites))))
if err != nil {
initErr = fmt.Errorf("error generating random index for cipher_suite: %w", err)
return
}
g.cipherSuite = HPKESymmetricCipherSuite{
g.CandidateCipherSuites[rndIndex.Int64()].KdfId,
g.CandidateCipherSuites[rndIndex.Int64()].AeadId,
}
// aead = hpke.AEAD(g.cipherSuite.AeadId)
}
if len(g.EncapsulatedKey) == 0 {
kem := uint16(defaultHpkeKem)
echPK, err := hpke.ParseHPKEPublicKey(uint16(kem), dummyX25519PublicKey)
if err != nil {
initErr = fmt.Errorf("tls: grease ech: failed to parse dummy public key: %w", err)
return
}
suite := echCipher{
KDFID: defaultHpkeKdf,
AEADID: defaultHpkeAead,
}
g.EncapsulatedKey, _, err = hpke.SetupSender(kem, suite.KDFID, suite.AEADID, echPK, []byte{})
if err != nil {
initErr = fmt.Errorf("tls: grease ech: failed to setup encapsulated key: %w", err)
return
}
}
if len(g.payload) == 0 {
if len(g.CandidatePayloadLens) == 0 {
g.CandidatePayloadLens = []uint16{128}
}
// randomly pick one from the list
rndIndex, err := rand.Int(rand.Reader, big.NewInt(int64(len(g.CandidatePayloadLens))))
if err != nil {
initErr = fmt.Errorf("error generating random index for payload length: %w", err)
return
}
initErr = g.randomizePayload(g.CandidatePayloadLens[rndIndex.Int64()])
}
})
return initErr
}
func (g *GREASEEncryptedClientHelloExtension) randomizePayload(encodedHelloInnerLen uint16) error {
if len(g.payload) != 0 {
return errors.New("tls: grease ech: regenerating payload is forbidden")
}
g.payload = make([]byte, cipherLen(g.cipherSuite.AeadId, int(encodedHelloInnerLen)))
_, err := rand.Read(g.payload)
if err != nil {
return fmt.Errorf("tls: generating grease ech payload: %w", err)
}
return nil
}
// writeToUConn implements TLSExtension.
//
// For ECH extensions, writeToUConn simply points the ech field in UConn to the extension.
func (g *GREASEEncryptedClientHelloExtension) writeToUConn(uconn *UConn) error {
uconn.ech = g
return uconn.MarshalClientHelloNoECH()
}
// Len implements TLSExtension.
func (g *GREASEEncryptedClientHelloExtension) Len() int {
g.init()
return 2 + 2 + 1 /* ClientHello Type */ + 4 /* CipherSuite */ + 1 /* Config ID */ + 2 + len(g.EncapsulatedKey) + 2 + len(g.payload)
}
// Read implements TLSExtension.
func (g *GREASEEncryptedClientHelloExtension) Read(b []byte) (int, error) {
if len(b) < g.Len() {
return 0, io.ErrShortBuffer
}
b[0] = byte(utlsExtensionECH >> 8)
b[1] = byte(utlsExtensionECH & 0xFF)
b[2] = byte((g.Len() - 4) >> 8)
b[3] = byte((g.Len() - 4) & 0xFF)
b[4] = OuterClientHello
b[5] = byte(g.cipherSuite.KdfId >> 8)
b[6] = byte(g.cipherSuite.KdfId & 0xFF)
b[7] = byte(g.cipherSuite.AeadId >> 8)
b[8] = byte(g.cipherSuite.AeadId & 0xFF)
b[9] = g.configId
b[10] = byte(len(g.EncapsulatedKey) >> 8)
b[11] = byte(len(g.EncapsulatedKey) & 0xFF)
copy(b[12:], g.EncapsulatedKey)
b[12+len(g.EncapsulatedKey)] = byte(len(g.payload) >> 8)
b[12+len(g.EncapsulatedKey)+1] = byte(len(g.payload) & 0xFF)
copy(b[12+len(g.EncapsulatedKey)+2:], g.payload)
return g.Len(), io.EOF
}
// MarshalClientHello implements EncryptedClientHelloExtension.
func (*GREASEEncryptedClientHelloExtension) MarshalClientHello(*UConn) error {
return errors.New("tls: grease ech: MarshalClientHello() is not implemented, use (*UConn).MarshalClientHello() instead")
}
// Write implements TLSExtensionWriter.
func (g *GREASEEncryptedClientHelloExtension) Write(b []byte) (int, error) {
fullLen := len(b)
extData := cryptobyte.String(b)
// Check the extension type, it must be OuterClientHello otherwise we are not
// parsing the correct extension
var chType uint8 // 0: outer, 1: inner
var ignored cryptobyte.String
if !extData.ReadUint8(&chType) || chType != 0 {
return fullLen, errors.New("bad Client Hello type, expected 0, got " + fmt.Sprintf("%d", chType))
}
// Parse the cipher suite
if !extData.ReadUint16(&g.cipherSuite.KdfId) || !extData.ReadUint16(&g.cipherSuite.AeadId) {
return fullLen, errors.New("bad cipher suite")
}
if g.cipherSuite.KdfId != dicttls.HKDF_SHA256 &&
g.cipherSuite.KdfId != dicttls.HKDF_SHA384 &&
g.cipherSuite.KdfId != dicttls.HKDF_SHA512 {
return fullLen, errors.New("bad KDF ID: " + fmt.Sprintf("%d", g.cipherSuite.KdfId))
}
if g.cipherSuite.AeadId != dicttls.AEAD_AES_128_GCM &&
g.cipherSuite.AeadId != dicttls.AEAD_AES_256_GCM &&
g.cipherSuite.AeadId != dicttls.AEAD_CHACHA20_POLY1305 {
return fullLen, errors.New("bad AEAD ID: " + fmt.Sprintf("%d", g.cipherSuite.AeadId))
}
g.CandidateCipherSuites = []HPKESymmetricCipherSuite{g.cipherSuite}
// GREASE the ConfigId
if !extData.ReadUint8(&g.configId) {
return fullLen, errors.New("bad config ID")
}
// we don't write to CandidateConfigIds because we don't really want to reuse the same config_id
// GREASE the EncapsulatedKey
if !extData.ReadUint16LengthPrefixed(&ignored) {
return fullLen, errors.New("bad encapsulated key")
}
g.EncapsulatedKey = make([]byte, len(ignored))
n, err := rand.Read(g.EncapsulatedKey)
if err != nil {
return fullLen, fmt.Errorf("tls: generating grease ech encapsulated key: %w", err)
}
if n != len(g.EncapsulatedKey) {
return fullLen, fmt.Errorf("tls: generating grease ech encapsulated key: short read for %d bytes", len(ignored)-n)
}
// GREASE the payload
if !extData.ReadUint16LengthPrefixed(&ignored) {
return fullLen, errors.New("bad payload")
}
g.CandidatePayloadLens = []uint16{uint16(len(ignored) - cipherLen(g.cipherSuite.AeadId, 0))}
return fullLen, nil
}
// UnimplementedECHExtension is a placeholder for an ECH extension that is not implemented.
// All implementations of EncryptedClientHelloExtension should embed this struct to ensure
// forward compatibility.
type UnimplementedECHExtension struct{}
// writeToUConn implements TLSExtension.
func (*UnimplementedECHExtension) writeToUConn(_ *UConn) error {
return errors.New("tls: unimplemented ECHExtension")
}
// Len implements TLSExtension.
func (*UnimplementedECHExtension) Len() int {
return 0
}
// Read implements TLSExtension.
func (*UnimplementedECHExtension) Read(_ []byte) (int, error) {
return 0, errors.New("tls: unimplemented ECHExtension")
}
// MarshalClientHello implements EncryptedClientHelloExtension.
func (*UnimplementedECHExtension) MarshalClientHello(*UConn) error {
return errors.New("tls: unimplemented ECHExtension")
}
// mustEmbedUnimplementedECHExtension is a noop function but is required to
// ensure forward compatibility.
func (*UnimplementedECHExtension) mustEmbedUnimplementedECHExtension() {
panic("mustEmbedUnimplementedECHExtension() is not implemented")
}
// BoringGREASEECH returns a GREASE scheme BoringSSL uses by default.
func BoringGREASEECH() *GREASEEncryptedClientHelloExtension {
return &GREASEEncryptedClientHelloExtension{
CandidateCipherSuites: []HPKESymmetricCipherSuite{
{
KdfId: dicttls.HKDF_SHA256,
AeadId: dicttls.AEAD_AES_128_GCM,
},
},
CandidatePayloadLens: []uint16{128, 160, 192, 224}, // +16: 144, 176, 208, 240
}
}
+74
View File
@@ -0,0 +1,74 @@
// 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
// Fingerprinter is a struct largely for holding options for the FingerprintClientHello func
type Fingerprinter struct {
// AllowBluntMimicry will ensure that unknown extensions are
// passed along into the resulting ClientHelloSpec as-is
// WARNING: there could be numerous subtle issues with ClientHelloSpecs
// that are generated with this flag which could compromise security and/or mimicry
AllowBluntMimicry bool
// AlwaysAddPadding will always add a UtlsPaddingExtension with BoringPaddingStyle
// at the end of the extensions list if it isn't found in the fingerprinted hello.
// This could be useful in scenarios where the hello you are fingerprinting does not
// have any padding, but you suspect that other changes you make to the final hello
// (including things like different SNI lengths) would cause padding to be necessary
AlwaysAddPadding bool
RealPSKResumption bool // if set, PSK extension (if any) will be real PSK extension, otherwise it will be fake PSK extension
}
// FingerprintClientHello returns a ClientHelloSpec which is based on the
// ClientHello that is passed in as the data argument
//
// If the ClientHello passed in has extensions that are not recognized or cannot be handled
// it will return a non-nil error and a nil *ClientHelloSpec value
//
// The data should be the full tls record, including the record type/version/length header
// as well as the handshake type/length/version header
// https://tools.ietf.org/html/rfc5246#section-6.2
// https://tools.ietf.org/html/rfc5246#section-7.4
//
// It calls UnmarshalClientHello internally, and is kept for backwards compatibility
func (f *Fingerprinter) FingerprintClientHello(data []byte) (clientHelloSpec *ClientHelloSpec, err error) {
return f.RawClientHello(data)
}
// RawClientHello returns a ClientHelloSpec which is based on the
// ClientHello raw bytes that is passed in as the raw argument.
//
// It was renamed from FingerprintClientHello in v1.3.1 and earlier versions
// as a more precise name for the function
func (f *Fingerprinter) RawClientHello(raw []byte) (clientHelloSpec *ClientHelloSpec, err error) {
clientHelloSpec = &ClientHelloSpec{}
err = clientHelloSpec.FromRaw(raw, f.AllowBluntMimicry, f.RealPSKResumption)
if err != nil {
return nil, err
}
if f.AlwaysAddPadding {
clientHelloSpec.AlwaysAddPadding()
}
return clientHelloSpec, nil
}
// UnmarshalJSONClientHello returns a ClientHelloSpec which is based on the
// ClientHello JSON bytes that is passed in as the json argument.
func (f *Fingerprinter) UnmarshalJSONClientHello(json []byte) (clientHelloSpec *ClientHelloSpec, err error) {
clientHelloSpec = &ClientHelloSpec{}
err = clientHelloSpec.UnmarshalJSON(json)
if err != nil {
return nil, err
}
if f.AlwaysAddPadding {
clientHelloSpec.AlwaysAddPadding()
}
return clientHelloSpec, nil
}
+595
View File
@@ -0,0 +1,595 @@
// Copyright 2022 uTLS Authors. 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 (
"bytes"
"compress/zlib"
"context"
"errors"
"fmt"
"io"
"github.com/andybalholm/brotli"
"github.com/klauspost/compress/zstd"
"github.com/refraction-networking/utls/internal/fips140tls"
"github.com/refraction-networking/utls/internal/hpke"
"github.com/refraction-networking/utls/internal/tls13"
)
// This function is called by (*clientHandshakeStateTLS13).readServerCertificate()
// to retrieve the certificate out of a message read by (*Conn).readHandshake()
func (hs *clientHandshakeStateTLS13) utlsReadServerCertificate(msg any) (processedMsg any, err error) {
for _, ext := range hs.uconn.Extensions {
switch ext.(type) {
case *UtlsCompressCertExtension:
// Included Compressed Certificate extension
if len(hs.uconn.certCompressionAlgs) > 0 {
compressedCertMsg, ok := msg.(*utlsCompressedCertificateMsg)
if ok {
if err = transcriptMsg(compressedCertMsg, hs.transcript); err != nil {
return nil, err
}
msg, err = hs.decompressCert(*compressedCertMsg)
if err != nil {
return nil, fmt.Errorf("tls: failed to decompress certificate message: %w", err)
} else {
return msg, nil
}
}
}
default:
continue
}
}
return nil, nil
}
// called by (*clientHandshakeStateTLS13).utlsReadServerCertificate() when UtlsCompressCertExtension is used
func (hs *clientHandshakeStateTLS13) decompressCert(m utlsCompressedCertificateMsg) (*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 && !errors.Is(err, io.EOF) {
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
}
// to be called in (*clientHandshakeStateTLS13).handshake(),
// after hs.readServerFinished() and before hs.sendClientCertificate()
func (hs *clientHandshakeStateTLS13) serverFinishedReceived() error {
if err := hs.sendClientEncryptedExtensions(); err != nil {
return err
}
return nil
}
func (hs *clientHandshakeStateTLS13) sendClientEncryptedExtensions() error {
c := hs.c
clientEncryptedExtensions := new(utlsClientEncryptedExtensionsMsg)
if c.utls.applicationSettingsCodepoint != 0 {
clientEncryptedExtensions.applicationSettingsCodepoint = c.utls.applicationSettingsCodepoint
clientEncryptedExtensions.applicationSettings = c.utls.localApplicationSettings
if _, err := c.writeHandshakeRecord(clientEncryptedExtensions, hs.transcript); err != nil {
return err
}
}
return nil
}
func (hs *clientHandshakeStateTLS13) utlsReadServerParameters(encryptedExtensions *encryptedExtensionsMsg) error {
hs.c.utls.peerApplicationSettings = encryptedExtensions.utls.applicationSettings
hs.c.utls.applicationSettingsCodepoint = encryptedExtensions.utls.applicationSettingsCodepoint
if hs.c.utls.applicationSettingsCodepoint != 0 {
if hs.uconn.vers < VersionTLS13 {
return errors.New("tls: server sent application settings at invalid version")
}
if len(hs.uconn.clientProtocol) == 0 {
return errors.New("tls: server sent application settings without ALPN")
}
// Check if the ALPN selected by the server exists in the client's list.
if alps, ok := hs.uconn.config.ApplicationSettings[hs.serverHello.alpnProtocol]; ok {
hs.c.utls.localApplicationSettings = alps
} else {
// return errors.New("tls: server selected ALPN doesn't match a client ALPS")
return nil // ignore if client doesn't have ALPS in use.
// TODO: is this a issue or not?
}
}
return nil
}
func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) {
config := c.config
// [UTLS SECTION START]
if len(config.ServerName) == 0 && !config.InsecureSkipVerify && len(config.InsecureServerNameToVerify) == 0 {
return nil, nil, nil, errors.New("tls: at least one of ServerName, InsecureSkipVerify or InsecureServerNameToVerify must be specified in the tls.Config")
}
// [UTLS SECTION END]
nextProtosLength := 0
for _, proto := range config.NextProtos {
if l := len(proto); l == 0 || l > 255 {
return nil, nil, nil, errors.New("tls: invalid NextProtos value")
} else {
nextProtosLength += 1 + l
}
}
if nextProtosLength > 0xffff {
return nil, nil, nil, errors.New("tls: NextProtos values too large")
}
supportedVersions := config.supportedVersions(roleClient)
if len(supportedVersions) == 0 {
return nil, nil, nil, errors.New("tls: no supported versions satisfy MinVersion and MaxVersion")
}
maxVersion := config.maxSupportedVersion(roleClient)
hello := &clientHelloMsg{
vers: maxVersion,
compressionMethods: []uint8{compressionNone},
random: make([]byte, 32),
extendedMasterSecret: true,
ocspStapling: true,
scts: true,
serverName: hostnameInSNI(config.ServerName),
supportedCurves: config.curvePreferences(maxVersion),
supportedPoints: []uint8{pointFormatUncompressed},
secureRenegotiationSupported: true,
alpnProtocols: config.NextProtos,
supportedVersions: supportedVersions,
}
// The version at the beginning of the ClientHello was capped at TLS 1.2
// for compatibility reasons. The supported_versions extension is used
// to negotiate versions now. See RFC 8446, Section 4.2.1.
if hello.vers > VersionTLS12 {
hello.vers = VersionTLS12
}
if c.handshakes > 0 {
hello.secureRenegotiation = c.clientFinished[:]
}
preferenceOrder := cipherSuitesPreferenceOrder
if !hasAESGCMHardwareSupport {
preferenceOrder = cipherSuitesPreferenceOrderNoAES
}
configCipherSuites := config.cipherSuites()
hello.cipherSuites = make([]uint16, 0, len(configCipherSuites))
for _, suiteId := range preferenceOrder {
suite := mutualCipherSuite(configCipherSuites, suiteId)
if suite == nil {
continue
}
// Don't advertise TLS 1.2-only cipher suites unless
// we're attempting TLS 1.2.
if maxVersion < VersionTLS12 && suite.flags&suiteTLS12 != 0 {
continue
}
hello.cipherSuites = append(hello.cipherSuites, suiteId)
}
_, err := io.ReadFull(config.rand(), hello.random)
if err != nil {
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
// A random session ID is used to detect when the server accepted a ticket
// and is resuming a session (see RFC 5077). In TLS 1.3, it's always set as
// a compatibility measure (see RFC 8446, Section 4.1.2).
//
// The session ID is not set for QUIC connections (see RFC 9001, Section 8.4).
if c.quic == nil {
hello.sessionId = make([]byte, 32)
if _, err := io.ReadFull(config.rand(), hello.sessionId); err != nil {
return nil, nil, nil, errors.New("tls: short read from Rand: " + err.Error())
}
}
if maxVersion >= VersionTLS12 {
hello.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
}
if testingOnlyForceClientHelloSignatureAlgorithms != nil {
hello.supportedSignatureAlgorithms = testingOnlyForceClientHelloSignatureAlgorithms
}
var keyShareKeys *keySharePrivateKeys
if hello.supportedVersions[0] == VersionTLS13 {
// Reset the list of ciphers when the client only supports TLS 1.3.
if len(hello.supportedVersions) == 1 {
hello.cipherSuites = nil
}
if fips140tls.Required() {
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13FIPS...)
} else if hasAESGCMHardwareSupport {
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...)
} else {
hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...)
}
if len(hello.supportedCurves) == 0 {
return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE")
}
// curveID := hello.supportedCurves[0]
// keyShareKeys = &keySharePrivateKeys{curveID: curveID}
// // Note that if X25519MLKEM768 is supported, it will be first because
// // the preference order is fixed.
// if curveID == X25519MLKEM768 {
// keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519)
// if err != nil {
// return nil, nil, nil, err
// }
// seed := make([]byte, mlkem.SeedSize)
// if _, err := io.ReadFull(config.rand(), seed); err != nil {
// return nil, nil, nil, err
// }
// keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed)
// if err != nil {
// return nil, nil, nil, err
// }
// mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes()
// x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes()
// hello.keyShares = []keyShare{
// {group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)},
// }
// // If both X25519MLKEM768 and X25519 are supported, we send both key
// // shares (as a fallback) and we reuse the same X25519 ephemeral
// // key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2.
// if slices.Contains(hello.supportedCurves, X25519) {
// hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey})
// }
// } else {
// if _, ok := curveForCurveID(curveID); !ok {
// return nil, nil, nil, errors.New("tls: CurvePreferences includes unsupported curve")
// }
// keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), curveID)
// if err != nil {
// return nil, nil, nil, err
// }
// hello.keyShares = []keyShare{{group: curveID, data: keyShareKeys.ecdhe.PublicKey().Bytes()}}
// }
}
// [UTLS] We don't need this, since it is not ready yet
// if c.quic != nil {
// p, err := c.quicGetTransportParameters()
// if err != nil {
// return nil, nil, nil, err
// }
// if p == nil {
// p = []byte{}
// }
// hello.quicTransportParameters = p
// }
var ech *echClientContext
if c.config.EncryptedClientHelloConfigList != nil {
if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 {
return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
}
if c.config.MaxVersion != 0 && c.config.MaxVersion <= VersionTLS12 {
return nil, nil, nil, errors.New("tls: MaxVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated")
}
echConfigs, err := parseECHConfigList(c.config.EncryptedClientHelloConfigList)
if err != nil {
return nil, nil, nil, err
}
echConfig := pickECHConfig(echConfigs)
if echConfig == nil {
return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs")
}
ech = &echClientContext{config: echConfig}
hello.encryptedClientHello = []byte{1} // indicate inner hello
// We need to explicitly set these 1.2 fields to nil, as we do not
// marshal them when encoding the inner hello, otherwise transcripts
// will later mismatch.
hello.supportedPoints = nil
hello.ticketSupported = false
hello.secureRenegotiationSupported = false
hello.extendedMasterSecret = false
echPK, err := hpke.ParseHPKEPublicKey(ech.config.KemID, ech.config.PublicKey)
if err != nil {
return nil, nil, nil, err
}
suite, err := pickECHCipherSuite(ech.config.SymmetricCipherSuite)
if err != nil {
return nil, nil, nil, err
}
ech.kdfID, ech.aeadID = suite.KDFID, suite.AEADID
info := append([]byte("tls ech\x00"), ech.config.raw...)
ech.encapsulatedKey, ech.hpkeContext, err = hpke.SetupSender(ech.config.KemID, suite.KDFID, suite.AEADID, echPK, info)
if err != nil {
return nil, nil, nil, err
}
}
return hello, keyShareKeys, ech, nil
}
// clientHandshakeWithOneState checks that exactly one expected state is set (1.2 or 1.3)
// and performs client TLS handshake with that state
func (c *UConn) clientHandshake(ctx context.Context) (err error) {
// [uTLS section begins]
hello := c.HandshakeState.Hello.getPrivatePtr()
ech := c.echCtx
defer func() { c.HandshakeState.Hello = hello.getPublicPtr() }()
sessionIsLocked := c.utls.sessionController.isSessionLocked()
// after this point exactly 1 out of 2 HandshakeState pointers is non-nil,
// useTLS13 variable tells which pointer
// [uTLS section ends]
if c.config == nil {
c.config = defaultConfig()
}
// This may be a renegotiation handshake, in which case some fields
// need to be reset.
c.didResume = false
// [uTLS section begins]
// don't make new ClientHello, use hs.hello
// preserve the checks from beginning and end of makeClientHello()
if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify && len(c.config.InsecureServerNameToVerify) == 0 {
return errors.New("tls: at least one of ServerName, InsecureSkipVerify or InsecureServerNameToVerify must be specified in the tls.Config")
}
nextProtosLength := 0
for _, proto := range c.config.NextProtos {
if l := len(proto); l == 0 || l > 255 {
return errors.New("tls: invalid NextProtos value")
} else {
nextProtosLength += 1 + l
}
}
if nextProtosLength > 0xffff {
return errors.New("tls: NextProtos values too large")
}
if c.handshakes > 0 {
hello.secureRenegotiation = c.clientFinished[:]
}
var (
session *SessionState
earlySecret *tls13.EarlySecret
binderKey []byte
)
if !sessionIsLocked {
// [uTLS section ends]
session, earlySecret, binderKey, err = c.loadSession(hello)
// [uTLS section start]
} else {
session = c.HandshakeState.Session
if c.HandshakeState.State13.EarlySecret != nil && session != nil {
cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite)
earlySecret = tls13.NewEarlySecretFromSecret(cipherSuite.hash.New, c.HandshakeState.State13.EarlySecret)
}
binderKey = c.HandshakeState.State13.BinderKey
}
// [uTLS section ends]
if err != nil {
return err
}
if session != nil {
defer func() {
// If we got a handshake failure when resuming a session, throw away
// the session ticket. See RFC 5077, Section 3.2.
//
// RFC 8446 makes no mention of dropping tickets on failure, but it
// does require servers to abort on invalid binders, so we need to
// delete tickets to recover from a corrupted PSK.
if err != nil {
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
c.config.ClientSessionCache.Put(cacheKey, nil)
}
}
}()
}
if ech != nil && c.clientHelloBuildStatus != BuildByUtls {
// Split hello into inner and outer
ech.innerHello = hello.clone()
// Overwrite the server name in the outer hello with the public facing
// name.
hello.serverName = string(ech.config.PublicName)
// Generate a new random for the outer hello.
hello.random = make([]byte, 32)
_, err = io.ReadFull(c.config.rand(), hello.random)
if err != nil {
return errors.New("tls: short read from Rand: " + err.Error())
}
// NOTE: we don't do PSK GREASE, in line with boringssl, it's meant to
// work around _possibly_ broken middleboxes, but there is little-to-no
// evidence that this is actually a problem.
if err := computeAndUpdateOuterECHExtension(hello, ech.innerHello, ech, true); err != nil {
return err
}
}
c.serverName = hello.serverName
if _, err := c.writeHandshakeRecord(hello, nil); err != nil {
return err
}
if hello.earlyData {
suite := cipherSuiteTLS13ByID(session.cipherSuite)
transcript := suite.hash.New()
if err := transcriptMsg(hello, transcript); err != nil {
return err
}
earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript)
c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret)
}
// serverHelloMsg is not included in the transcript
msg, err := c.readHandshake(nil)
if err != nil {
return err
}
serverHello, ok := msg.(*serverHelloMsg)
if !ok {
c.sendAlert(alertUnexpectedMessage)
return unexpectedMessageError(serverHello, msg)
}
if err := c.pickTLSVersion(serverHello); err != nil {
return err
}
// If we are negotiating a protocol version that's lower than what we
// support, check for the server downgrade canaries.
// See RFC 8446, Section 4.1.3.
maxVers := c.config.maxSupportedVersion(roleClient)
tls12Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS12
tls11Downgrade := string(serverHello.random[24:]) == downgradeCanaryTLS11
if maxVers == VersionTLS13 && c.vers <= VersionTLS12 && (tls12Downgrade || tls11Downgrade) ||
maxVers == VersionTLS12 && c.vers <= VersionTLS11 && tls11Downgrade {
c.sendAlert(alertIllegalParameter)
return errors.New("tls: downgrade attempt detected, possibly due to a MitM attack or a broken middlebox")
}
// uTLS: do not create new handshakeState, use existing one
c.HandshakeState.ServerHello = serverHello.getPublicPtr()
if c.vers == VersionTLS13 {
hs13 := c.HandshakeState.toPrivate13()
hs13.serverHello = serverHello
hs13.hello = hello
hs13.echContext = ech
if c.HandshakeState.State13.EarlySecret != nil && session.cipherSuite != 0 {
hs13.earlySecret = tls13.NewEarlySecretFromSecret(cipherSuiteTLS13ByID(session.cipherSuite).hash.New, c.HandshakeState.State13.EarlySecret)
}
if c.HandshakeState.MasterSecret != nil && session.cipherSuite != 0 {
hs13.masterSecret = tls13.NewMasterSecretFromSecret(cipherSuiteTLS13ByID(session.cipherSuite).hash.New, c.HandshakeState.MasterSecret)
}
if !sessionIsLocked {
hs13.earlySecret = earlySecret
hs13.binderKey = binderKey
hs13.session = session
}
hs13.ctx = ctx
// In TLS 1.3, session tickets are delivered after the handshake.
err = hs13.handshake()
if handshakeState := hs13.toPublic13(); handshakeState != nil {
c.HandshakeState = *handshakeState
}
return err
}
hs12 := c.HandshakeState.toPrivate12()
hs12.serverHello = serverHello
hs12.hello = hello
hs12.ctx = ctx
hs12.session = session
err = hs12.handshake()
if handshakeState := hs12.toPublic12(); handshakeState != nil {
c.HandshakeState = *handshakeState
}
if err != nil {
return err
}
return nil
}
func (c *UConn) echTranscriptMsg(outer *clientHelloMsg, echCtx *echClientContext) (err error) {
// Recreate the inner ClientHello from its compressed form using server's decodeInnerClientHello function.
// See https://github.com/refraction-networking/utls/blob/e430876b1d82fdf582efc57f3992d448e7ab3d8a/ech.go#L276-L283
encodedInner, err := encodeInnerClientHelloReorderOuterExts(echCtx.innerHello, int(echCtx.config.MaxNameLength), c.extensionsList())
if err != nil {
return err
}
decodedInner, err := decodeInnerClientHello(outer, encodedInner)
if err != nil {
return err
}
if err := transcriptMsg(decodedInner, echCtx.innerTranscript); err != nil {
return err
}
return nil
}
+138
View File
@@ -0,0 +1,138 @@
// Copyright 2022 uTLS Authors. 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 (
"golang.org/x/crypto/cryptobyte"
)
// Only implemented client-side, for server certificates.
// Alternate certificate message formats (https://datatracker.ietf.org/doc/html/rfc7250) are not
// supported.
// https://datatracker.ietf.org/doc/html/rfc8879
type utlsCompressedCertificateMsg struct {
raw []byte
algorithm uint16
uncompressedLength uint32 // uint24
compressedCertificateMessage []byte
}
func (m *utlsCompressedCertificateMsg) marshal() ([]byte, error) {
if m.raw != nil {
return m.raw, nil
}
var b cryptobyte.Builder
b.AddUint8(utlsTypeCompressedCertificate)
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddUint16(m.algorithm)
b.AddUint24(m.uncompressedLength)
b.AddUint24LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(m.compressedCertificateMessage)
})
})
var err error
m.raw, err = b.Bytes()
return m.raw, err
}
func (m *utlsCompressedCertificateMsg) unmarshal(data []byte) bool {
*m = utlsCompressedCertificateMsg{raw: data}
s := cryptobyte.String(data)
if !s.Skip(4) || // message type and uint24 length field
!s.ReadUint16(&m.algorithm) ||
!s.ReadUint24(&m.uncompressedLength) ||
!readUint24LengthPrefixed(&s, &m.compressedCertificateMessage) {
return false
}
return true
}
type utlsEncryptedExtensionsMsgExtraFields struct {
applicationSettings []byte
applicationSettingsCodepoint uint16
customExtension []byte
}
func (m *encryptedExtensionsMsg) utlsUnmarshal(extension uint16, extData cryptobyte.String) bool {
switch extension {
case utlsExtensionApplicationSettings:
fallthrough
case utlsExtensionApplicationSettingsNew:
m.utls.applicationSettingsCodepoint = extension
m.utls.applicationSettings = []byte(extData)
}
return true // success/unknown extension
}
type utlsClientEncryptedExtensionsMsg struct {
raw []byte
applicationSettings []byte
applicationSettingsCodepoint uint16
customExtension []byte
}
func (m *utlsClientEncryptedExtensionsMsg) marshal() (x []byte, err error) {
if m.raw != nil {
return m.raw, nil
}
var builder cryptobyte.Builder
builder.AddUint8(typeEncryptedExtensions)
builder.AddUint24LengthPrefixed(func(body *cryptobyte.Builder) {
body.AddUint16LengthPrefixed(func(extensions *cryptobyte.Builder) {
if m.applicationSettingsCodepoint != 0 {
extensions.AddUint16(m.applicationSettingsCodepoint)
extensions.AddUint16LengthPrefixed(func(msg *cryptobyte.Builder) {
msg.AddBytes(m.applicationSettings)
})
}
if len(m.customExtension) > 0 {
extensions.AddUint16(utlsFakeExtensionCustom)
extensions.AddUint16LengthPrefixed(func(msg *cryptobyte.Builder) {
msg.AddBytes(m.customExtension)
})
}
})
})
m.raw, err = builder.Bytes()
return m.raw, err
}
func (m *utlsClientEncryptedExtensionsMsg) unmarshal(data []byte) bool {
*m = utlsClientEncryptedExtensionsMsg{raw: data}
s := cryptobyte.String(data)
var extensions cryptobyte.String
if !s.Skip(4) || // message type and uint24 length field
!s.ReadUint16LengthPrefixed(&extensions) || !s.Empty() {
return false
}
for !extensions.Empty() {
var extension uint16
var extData cryptobyte.String
if !extensions.ReadUint16(&extension) ||
!extensions.ReadUint16LengthPrefixed(&extData) {
return false
}
switch extension {
case utlsExtensionApplicationSettings:
fallthrough
case utlsExtensionApplicationSettingsNew:
m.applicationSettingsCodepoint = extension
m.applicationSettings = []byte(extData)
default:
// Unknown extensions are illegal in EncryptedExtensions.
return false
}
}
return true
}
+35
View File
@@ -0,0 +1,35 @@
package tls
import (
"github.com/refraction-networking/utls/internal/hpke"
)
type HPKERawPublicKey = []byte
type HPKE_KEM_ID = uint16 // RFC 9180
type HPKE_KDF_ID = uint16 // RFC 9180
type HPKE_AEAD_ID = uint16 // RFC 9180
type HPKESymmetricCipherSuite struct {
KdfId HPKE_KDF_ID
AeadId HPKE_AEAD_ID
}
const defaultHpkeKdf = hpke.KDF_HKDF_SHA256
const defaultHpkeKem = hpke.DHKEM_X25519_HKDF_SHA256
const defaultHpkeAead = hpke.AEAD_AES_128_GCM
var dummyX25519PublicKey = []byte{
143, 38, 37, 36, 12, 6, 229, 30, 140, 27, 167, 73, 26, 100, 203, 107, 216,
81, 163, 222, 52, 211, 54, 210, 46, 37, 78, 216, 157, 97, 241, 244,
}
// cipherLen returns the length of a ciphertext corresponding to a message of
// length mLen.
func cipherLen(a uint16, mLen int) int {
switch a {
case hpke.AEAD_AES_128_GCM, hpke.AEAD_AES_256_GCM, hpke.AEAD_ChaCha20Poly1305:
return mLen + 16
default:
panic("hpke: invalid AEAD identifier")
}
}
+30
View File
@@ -0,0 +1,30 @@
package tls
import (
"crypto/mlkem"
"golang.org/x/crypto/sha3"
)
// kyberDecapsulate implements decapsulation according to Kyber Round 3.
func kyberDecapsulate(dk *mlkem.DecapsulationKey768, c []byte) ([]byte, error) {
K, err := dk.Decapsulate(c)
if err != nil {
return nil, err
}
return kyberSharedSecret(c, K), nil
}
func kyberSharedSecret(c, K []byte) []byte {
// Package mlkem implements ML-KEM, which compared to Kyber removed a
// final hashing step. Compute SHAKE-256(K || SHA3-256(c), 32) to match Kyber.
// See https://words.filippo.io/mlkem768/#bonus-track-using-a-ml-kem-implementation-as-kyber-v3.
h := sha3.NewShake256()
h.Write(K)
ch := sha3.New256()
ch.Write(c)
h.Write(ch.Sum(nil))
out := make([]byte, 32)
h.Read(out)
return out
}
File diff suppressed because it is too large Load Diff
+486
View File
@@ -0,0 +1,486 @@
package tls
import (
"encoding/json"
"errors"
"io"
"golang.org/x/crypto/cryptobyte"
)
var ErrEmptyPsk = errors.New("tls: empty psk detected; remove the psk extension for this connection or set OmitEmptyPsk to true to conceal it in utls")
type PreSharedKeyCommon struct {
Identities []PskIdentity
Binders [][]byte
BinderKey []byte // this will be used to compute the binder when hello message is ready
EarlySecret []byte
Session *SessionState
}
// The lifecycle of a PreSharedKeyExtension:
//
// Creation Phase:
// - The extension is created.
//
// Write Phase:
//
// - [writeToUConn() called]:
//
// > - During this phase, it is important to note that implementations should not write any session data to the UConn (Underlying Connection) as the session is not yet loaded. The session context is not active at this point.
//
// Initialization Phase:
//
// - [IsInitialized() called]:
//
// If IsInitialized() returns true
//
// > - GetPreSharedKeyCommon() will be called subsequently and the PSK states in handshake/clientHello will be fully initialized.
//
// If IsInitialized() returns false:
//
// > - [conn.loadSession() called]:
//
// >> - Once the session is available:
//
// >>> - [InitializeByUtls() called]:
//
// >>>> - The InitializeByUtls() method is invoked to initialize the extension based on the loaded session data.
//
// >>>> - This step prepares the extension for further processing.
//
// Marshal Phase:
//
// - [Len() called], [Read() called]:
//
// > - Implementations should marshal the extension into bytes, using placeholder binders to maintain the correct length.
//
// Binders Preparation Phase:
//
// - [PatchBuiltHello(hello) called]:
//
// > - The client hello is already marshaled in the "hello.Raw" format.
//
// > - Implementations are expected to update the binders within the marshaled client hello.
//
// - [GetPreSharedKeyCommon() called]:
//
// > - Implementations should gather and provide the final pre-shared key (PSK) related data.
//
// > - This data will be incorporated into both the clientHello and HandshakeState, ensuring that the PSK-related information is properly set and ready for the handshake process.
type PreSharedKeyExtension interface {
// TLSExtension must be implemented by all PreSharedKeyExtension implementations.
TLSExtension
// If false is returned, utls will invoke `InitializeByUtls()` for the necessary initialization.
Initializable
SetOmitEmptyPsk(val bool)
// InitializeByUtls is invoked when IsInitialized() returns false.
// It initializes the extension using a real and valid TLS 1.3 session.
InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity)
// GetPreSharedKeyCommon retrieves the final PreSharedKey-related states as defined in PreSharedKeyCommon.
GetPreSharedKeyCommon() PreSharedKeyCommon
// PatchBuiltHello is called once the hello message is fully applied and marshaled.
// Its purpose is to update the binders of PSK (Pre-Shared Key) identities.
PatchBuiltHello(hello *PubClientHelloMsg) error
mustEmbedUnimplementedPreSharedKeyExtension() // this works like a type guard
}
type UnimplementedPreSharedKeyExtension struct{}
func (UnimplementedPreSharedKeyExtension) mustEmbedUnimplementedPreSharedKeyExtension() {}
func (*UnimplementedPreSharedKeyExtension) IsInitialized() bool {
panic("tls: IsInitialized is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity) {
panic("tls: Initialize is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) writeToUConn(*UConn) error {
panic("tls: writeToUConn is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) Len() int {
panic("tls: Len is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) Read([]byte) (int, error) {
panic("tls: Read is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) GetPreSharedKeyCommon() PreSharedKeyCommon {
panic("tls: GetPreSharedKeyCommon is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) error {
panic("tls: ReadWithRawHello is not implemented for the PreSharedKeyExtension")
}
func (*UnimplementedPreSharedKeyExtension) SetOmitEmptyPsk(val bool) {
panic("tls: SetOmitEmptyPsk is not implemented for the PreSharedKeyExtension")
}
// UtlsPreSharedKeyExtension is an extension used to set the PSK extension in the
// ClientHello.
type UtlsPreSharedKeyExtension struct {
UnimplementedPreSharedKeyExtension
PreSharedKeyCommon
cipherSuite *cipherSuiteTLS13
cachedLength *int
// Deprecated: Set OmitEmptyPsk in Config instead.
OmitEmptyPsk bool
}
func (e *UtlsPreSharedKeyExtension) IsInitialized() bool {
return e.Session != nil
}
func (e *UtlsPreSharedKeyExtension) InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity) {
e.Session = session
e.EarlySecret = earlySecret
e.BinderKey = binderKey
e.cipherSuite = cipherSuiteTLS13ByID(e.Session.cipherSuite)
e.Identities = identities
e.Binders = make([][]byte, 0, len(e.Identities))
for i := 0; i < len(e.Identities); i++ {
e.Binders = append(e.Binders, make([]byte, e.cipherSuite.hash.Size()))
}
}
func (e *UtlsPreSharedKeyExtension) writeToUConn(uc *UConn) error {
uc.HandshakeState.Hello.TicketSupported = true // This doesn't matter though, as utls doesn't care about this field. We write this for consistency.
return nil
}
func (e *UtlsPreSharedKeyExtension) GetPreSharedKeyCommon() PreSharedKeyCommon {
return e.PreSharedKeyCommon
}
func pskExtLen(identities []PskIdentity, binders [][]byte) int {
if len(identities) == 0 || len(binders) == 0 {
// If there isn't psk identities, we don't write this ticket to the client hello, and therefore the length should be 0.
return 0
}
length := 4 // extension type + extension length
length += 2 // identities length
for _, identity := range identities {
length += 2 + len(identity.Label) + 4 // identity length + identity + obfuscated ticket age
}
length += 2 // binders length
for _, binder := range binders {
length += len(binder) + 1
}
return length
}
func (e *UtlsPreSharedKeyExtension) Len() int {
if e.Session == nil {
return 0
}
if e.cachedLength != nil {
return *e.cachedLength
}
length := pskExtLen(e.Identities, e.Binders)
e.cachedLength = &length
return length
}
func readPskIntoBytes(b []byte, identities []PskIdentity, binders [][]byte) (int, error) {
extLen := pskExtLen(identities, binders)
if extLen == 0 {
return 0, io.EOF
}
if len(b) < extLen {
return 0, io.ErrShortBuffer
}
b[0] = byte(extensionPreSharedKey >> 8)
b[1] = byte(extensionPreSharedKey)
b[2] = byte((extLen - 4) >> 8)
b[3] = byte(extLen - 4)
// identities length
identitiesLength := 0
for _, identity := range identities {
identitiesLength += 2 + len(identity.Label) + 4 // identity length + identity + obfuscated ticket age
}
b[4] = byte(identitiesLength >> 8)
b[5] = byte(identitiesLength)
// identities
offset := 6
for _, identity := range identities {
b[offset] = byte(len(identity.Label) >> 8)
b[offset+1] = byte(len(identity.Label))
offset += 2
copy(b[offset:], identity.Label)
offset += len(identity.Label)
b[offset] = byte(identity.ObfuscatedTicketAge >> 24)
b[offset+1] = byte(identity.ObfuscatedTicketAge >> 16)
b[offset+2] = byte(identity.ObfuscatedTicketAge >> 8)
b[offset+3] = byte(identity.ObfuscatedTicketAge)
offset += 4
}
// binders length
bindersLength := 0
for _, binder := range binders {
// check if binder size is valid
bindersLength += len(binder) + 1 // binder length + binder
}
b[offset] = byte(bindersLength >> 8)
b[offset+1] = byte(bindersLength)
offset += 2
// binders
for _, binder := range binders {
b[offset] = byte(len(binder))
offset++
copy(b[offset:], binder)
offset += len(binder)
}
return extLen, io.EOF
}
func (e *UtlsPreSharedKeyExtension) SetOmitEmptyPsk(val bool) {
e.OmitEmptyPsk = val
}
func (e *UtlsPreSharedKeyExtension) Read(b []byte) (int, error) {
if !e.OmitEmptyPsk && e.Len() == 0 {
return 0, ErrEmptyPsk
}
return readPskIntoBytes(b, e.Identities, e.Binders)
}
func (e *UtlsPreSharedKeyExtension) PatchBuiltHello(hello *PubClientHelloMsg) error {
if e.Len() == 0 {
return nil
}
private := hello.getCachedPrivatePtr()
if private == nil {
private = hello.getPrivatePtr()
}
private.original = hello.Raw
private.pskBinders = e.Binders // set the placeholder to the private Hello
//--- mirror loadSession() begin ---//
transcript := e.cipherSuite.hash.New()
helloBytes, err := private.marshalWithoutBinders() // no marshal() will be actually called, as we have set the field `raw`
if err != nil {
return err
}
transcript.Write(helloBytes)
pskBinders := [][]byte{e.cipherSuite.finishedHash(e.BinderKey, transcript)}
if err := private.updateBinders(pskBinders); err != nil {
return err
}
// copied from handshake_messages.go in 1.22
lenWithoutBinders := len(helloBytes)
b := cryptobyte.NewFixedBuilder(private.original[:lenWithoutBinders])
b.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) {
for _, binder := range private.pskBinders {
b.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
b.AddBytes(binder)
})
}
})
if out, err := b.Bytes(); err != nil || len(out) != len(private.original) {
return errors.New("tls: internal error: failed to update binders")
}
//--- mirror loadSession() end ---//
e.Binders = pskBinders
// no need to care about other PSK related fields, they will be handled separately
return io.EOF
}
func (e *UtlsPreSharedKeyExtension) Write(b []byte) (int, error) {
return len(b), nil // ignore the data
}
func (e *UtlsPreSharedKeyExtension) UnmarshalJSON(_ []byte) error {
return nil // ignore the data
}
// FakePreSharedKeyExtension is an extension used to set the PSK extension in the
// ClientHello.
//
// It does not compute binders based on ClientHello, but uses the binders specified instead.
// We still need to learn more of the safety
// of hardcoding both Identities and Binders without recalculating the latter.
type FakePreSharedKeyExtension struct {
UnimplementedPreSharedKeyExtension
Identities []PskIdentity `json:"identities"`
Binders [][]byte `json:"binders"`
// Deprecated: Set OmitEmptyPsk in Config instead.
OmitEmptyPsk bool
}
func (e *FakePreSharedKeyExtension) IsInitialized() bool {
return e.Identities != nil && e.Binders != nil
}
func (e *FakePreSharedKeyExtension) InitializeByUtls(session *SessionState, earlySecret []byte, binderKey []byte, identities []PskIdentity) {
panic("InitializeByUtls failed: don't let utls initialize FakePreSharedKeyExtension; provide your own identities and binders or use UtlsPreSharedKeyExtension")
}
func (e *FakePreSharedKeyExtension) writeToUConn(uc *UConn) error {
if uc.config.ClientSessionCache == nil {
return nil // don't write the extension if there is no session cache
}
if session, ok := uc.config.ClientSessionCache.Get(uc.clientSessionCacheKey()); !ok || session == nil {
return nil // don't write the extension if there is no session cache available for this session
}
uc.HandshakeState.Hello.PskIdentities = e.Identities
uc.HandshakeState.Hello.PskBinders = e.Binders
return nil
}
func (e *FakePreSharedKeyExtension) Len() int {
return pskExtLen(e.Identities, e.Binders)
}
func (e *FakePreSharedKeyExtension) SetOmitEmptyPsk(val bool) {
e.OmitEmptyPsk = val
}
func (e *FakePreSharedKeyExtension) Read(b []byte) (int, error) {
if !e.OmitEmptyPsk && e.Len() == 0 {
return 0, ErrEmptyPsk
}
for _, b := range e.Binders {
if !(anyTrue(validHashLen, func(_ int, valid *int) bool {
return len(b) == *valid
})) {
return 0, errors.New("tls: FakePreSharedKeyExtension.Read failed: invalid binder size")
}
}
return readPskIntoBytes(b, e.Identities, e.Binders)
}
func (e *FakePreSharedKeyExtension) GetPreSharedKeyCommon() PreSharedKeyCommon {
return PreSharedKeyCommon{
Identities: e.Identities,
Binders: e.Binders,
}
}
var validHashLen = mapSlice(cipherSuitesTLS13, func(c *cipherSuiteTLS13) int {
return c.hash.Size()
})
func (*FakePreSharedKeyExtension) PatchBuiltHello(*PubClientHelloMsg) error {
return nil // no need to patch the hello since we don't need to update binders
}
func (e *FakePreSharedKeyExtension) Write(b []byte) (n int, err error) {
fullLen := len(b)
s := cryptobyte.String(b)
var identitiesLength uint16
if !s.ReadUint16(&identitiesLength) {
return 0, errors.New("tls: invalid PSK extension")
}
// identities
for identitiesLength > 0 {
var identityLength uint16
if !s.ReadUint16(&identityLength) {
return 0, errors.New("tls: invalid PSK extension")
}
identitiesLength -= 2
if identityLength > identitiesLength {
return 0, errors.New("tls: invalid PSK extension")
}
var identity []byte
if !s.ReadBytes(&identity, int(identityLength)) {
return 0, errors.New("tls: invalid PSK extension")
}
identitiesLength -= identityLength // identity
var obfuscatedTicketAge uint32
if !s.ReadUint32(&obfuscatedTicketAge) {
return 0, errors.New("tls: invalid PSK extension")
}
e.Identities = append(e.Identities, PskIdentity{
Label: identity,
ObfuscatedTicketAge: obfuscatedTicketAge,
})
identitiesLength -= 4 // obfuscated ticket age
}
var bindersLength uint16
if !s.ReadUint16(&bindersLength) {
return 0, errors.New("tls: invalid PSK extension")
}
// binders
for bindersLength > 0 {
var binderLength uint8
if !s.ReadUint8(&binderLength) {
return 0, errors.New("tls: invalid PSK extension")
}
bindersLength -= 1
if uint16(binderLength) > bindersLength {
return 0, errors.New("tls: invalid PSK extension")
}
var binder []byte
if !s.ReadBytes(&binder, int(binderLength)) {
return 0, errors.New("tls: invalid PSK extension")
}
e.Binders = append(e.Binders, binder)
bindersLength -= uint16(binderLength)
}
return fullLen, nil
}
func (e *FakePreSharedKeyExtension) UnmarshalJSON(data []byte) error {
var pskAccepter struct {
PskIdentities []PskIdentity `json:"identities"`
PskBinders [][]byte `json:"binders"`
}
if err := json.Unmarshal(data, &pskAccepter); err != nil {
return err
}
e.Identities = pskAccepter.PskIdentities
e.Binders = pskAccepter.PskBinders
return nil
}
// type guard
var (
_ PreSharedKeyExtension = (*UtlsPreSharedKeyExtension)(nil)
_ TLSExtensionJSON = (*UtlsPreSharedKeyExtension)(nil)
_ PreSharedKeyExtension = (*FakePreSharedKeyExtension)(nil)
_ TLSExtensionJSON = (*FakePreSharedKeyExtension)(nil)
_ TLSExtensionWriter = (*FakePreSharedKeyExtension)(nil)
)
// type ExternalPreSharedKeyExtension struct{} // TODO: wait for whoever cares about external PSK to implement it
+188
View File
@@ -0,0 +1,188 @@
/*
* Copyright (c) 2019, Psiphon Inc.
* All rights reserved.
*
* Released under utls licence:
* https://github.com/refraction-networking/utls/blob/master/LICENSE
*/
// This code is a pared down version of:
// https://github.com/Psiphon-Labs/psiphon-tunnel-core/blob/158caea562287284cc3fa5fcd1b3c97b1addf659/psiphon/common/prng/prng.go
package tls
import (
crypto_rand "crypto/rand"
"encoding/binary"
"io"
"math"
"math/rand"
"sync"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/sha3"
)
const (
PRNGSeedLength = 32
)
// PRNGSeed is a PRNG seed.
type PRNGSeed [PRNGSeedLength]byte
// NewPRNGSeed creates a new PRNG seed using crypto/rand.Read.
func NewPRNGSeed() (*PRNGSeed, error) {
seed := new(PRNGSeed)
_, err := crypto_rand.Read(seed[:])
if err != nil {
return nil, err
}
return seed, nil
}
// newSaltedPRNGSeed creates a new seed derived from an existing seed and a
// salt. A HKDF is applied to the seed and salt.
//
// newSaltedPRNGSeed is intended for use cases where a single seed needs to be
// used in distinct contexts to produce independent random streams.
func newSaltedPRNGSeed(seed *PRNGSeed, salt string) (*PRNGSeed, error) {
saltedSeed := new(PRNGSeed)
_, err := io.ReadFull(
hkdf.New(sha3.New256, seed[:], []byte(salt), nil), saltedSeed[:])
if err != nil {
return nil, err
}
return saltedSeed, nil
}
// prng is a seeded, unbiased PRNG based on SHAKE256. that is suitable for use
// cases such as obfuscation. Seeding is based on crypto/rand.Read.
//
// This PRNG is _not_ for security use cases including production cryptographic
// key generation.
//
// It is safe to make concurrent calls to a PRNG instance.
//
// PRNG conforms to io.Reader and math/rand.Source, with additional helper
// functions.
type prng struct {
rand *rand.Rand
randomStreamMutex sync.Mutex
randomStream sha3.ShakeHash
}
// newPRNG generates a seed and creates a PRNG with that seed.
func newPRNG() (*prng, error) {
seed, err := NewPRNGSeed()
if err != nil {
return nil, err
}
return newPRNGWithSeed(seed)
}
// newPRNGWithSeed initializes a new PRNG using an existing seed.
func newPRNGWithSeed(seed *PRNGSeed) (*prng, error) {
shake := sha3.NewShake256()
_, err := shake.Write(seed[:])
if err != nil {
return nil, err
}
p := &prng{
randomStream: shake,
}
p.rand = rand.New(p)
return p, nil
}
// newPRNGWithSaltedSeed initializes a new PRNG using a seed derived from an
// existing seed and a salt with NewSaltedSeed.
func newPRNGWithSaltedSeed(seed *PRNGSeed, salt string) (*prng, error) {
saltedSeed, err := newSaltedPRNGSeed(seed, salt)
if err != nil {
return nil, err
}
return newPRNGWithSeed(saltedSeed)
}
// Read reads random bytes from the PRNG stream into b. Read conforms to
// io.Reader and always returns len(p), nil.
func (p *prng) Read(b []byte) (int, error) {
p.randomStreamMutex.Lock()
defer p.randomStreamMutex.Unlock()
// ShakeHash.Read never returns an error:
// https://godoc.org/golang.org/x/crypto/sha3#ShakeHash
_, _ = io.ReadFull(p.randomStream, b)
return len(b), nil
}
// Int63 is equivalent to math/read.Int63.
func (p *prng) Int63() int64 {
i := p.Uint64()
return int64(i & (1<<63 - 1))
}
// Int63 is equivalent to math/read.Uint64.
func (p *prng) Uint64() uint64 {
var b [8]byte
p.Read(b[:])
return binary.BigEndian.Uint64(b[:])
}
// Seed must exist in order to use a PRNG as a math/rand.Source. This call is
// not supported and ignored.
func (p *prng) Seed(_ int64) {
}
// FlipWeightedCoin returns the result of a weighted
// random coin flip. If the weight is 0.5, the outcome
// is equally likely to be true or false. If the weight
// is 1.0, the outcome is always true, and if the
// weight is 0.0, the outcome is always false.
//
// Input weights > 1.0 are treated as 1.0.
func (p *prng) FlipWeightedCoin(weight float64) bool {
if weight > 1.0 {
weight = 1.0
}
f := float64(p.Int63()) / float64(math.MaxInt64)
return f > 1.0-weight
}
// Intn is equivalent to math/read.Intn, except it returns 0 if n <= 0
// instead of panicking.
func (p *prng) Intn(n int) int {
if n <= 0 {
return 0
}
return p.rand.Intn(n)
}
// Int63n is equivalent to math/read.Int63n, except it returns 0 if n <= 0
// instead of panicking.
func (p *prng) Int63n(n int64) int64 {
if n <= 0 {
return 0
}
return p.rand.Int63n(n)
}
// Intn is equivalent to math/read.Perm.
func (p *prng) Perm(n int) []int {
return p.rand.Perm(n)
}
// Range selects a random integer in [min, max].
// If min < 0, min is set to 0. If max < min, min is returned.
func (p *prng) Range(min, max int) int {
if min < 0 {
min = 0
}
if max < min {
return min
}
n := p.Intn(max - min + 1)
n += min
return n
}
+925
View File
@@ -0,0 +1,925 @@
// 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 (
"crypto"
"crypto/ecdh"
"crypto/mlkem"
"crypto/x509"
"hash"
"time"
)
// ClientHandshakeState includes both TLS 1.3-only and TLS 1.2-only states,
// only one of them will be used, depending on negotiated version.
//
// ClientHandshakeState will be converted into and from either
// - clientHandshakeState (TLS 1.2)
// - clientHandshakeStateTLS13 (TLS 1.3)
//
// uTLS will call .handshake() on one of these private internal states,
// to perform TLS handshake using standard crypto/tls implementation.
type PubClientHandshakeState struct {
C *Conn
ServerHello *PubServerHelloMsg
Hello *PubClientHelloMsg
MasterSecret []byte
Session *SessionState
State12 TLS12OnlyState
State13 TLS13OnlyState
uconn *UConn
}
// TLS 1.3 only
type TLS13OnlyState struct {
// Deprecated: Use KeyShareKeys instead. KeyShareKeys will take precedence if both are set.
// Support may be removed in the future.
EcdheKey *ecdh.PrivateKey
// Deprecated: Use KeyShareKeys instead. This variable is no longer used.
// Will be removed in the future.
KeySharesParams *KeySharesParameters
// Deprecated: Use KeyShareKeys instead. This variable is no longer used.
// Will be removed in the future.
KEMKey *KemPrivateKey
KeyShareKeys *KeySharePrivateKeys
Suite *PubCipherSuiteTLS13
EarlySecret []byte
BinderKey []byte
CertReq *CertificateRequestMsgTLS13
UsingPSK bool // don't set this field when building client hello
SentDummyCCS bool
Transcript hash.Hash
TrafficSecret []byte // client_application_traffic_secret_0
}
// TLS 1.2 and before only
type TLS12OnlyState struct {
FinishedHash FinishedHash
Suite PubCipherSuite
}
func (chs *TLS13OnlyState) private13KeyShareKeys() *keySharePrivateKeys {
if chs.KeyShareKeys != nil {
return chs.KeyShareKeys.ToPrivate()
}
if chs.EcdheKey != nil {
return &keySharePrivateKeys{
ecdhe: chs.EcdheKey,
}
}
return nil
}
// func kyberGoToCircl(kyberKey *mlkem768.DecapsulationKey, ecdhKey *ecdh.PrivateKey) (kem.PrivateKey, error) {
// return hybrid.Kyber768X25519().UnmarshalBinaryPrivateKey(append(ecdhKey.Bytes(), kyberKey.Bytes()...))
// }
func (chs *PubClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 {
if chs == nil {
return nil
} else {
return &clientHandshakeStateTLS13{
c: chs.C,
serverHello: chs.ServerHello.getPrivatePtr(),
hello: chs.Hello.getPrivatePtr(),
keyShareKeys: chs.State13.private13KeyShareKeys(),
session: chs.Session,
binderKey: chs.State13.BinderKey,
certReq: chs.State13.CertReq.toPrivate(),
usingPSK: chs.State13.UsingPSK,
sentDummyCCS: chs.State13.SentDummyCCS,
suite: chs.State13.Suite.toPrivate(),
transcript: chs.State13.Transcript,
trafficSecret: chs.State13.TrafficSecret,
uconn: chs.uconn,
}
}
}
func (chs13 *clientHandshakeStateTLS13) toPublic13() *PubClientHandshakeState {
if chs13 == nil {
return nil
} else {
tls13State := TLS13OnlyState{
KeyShareKeys: chs13.keyShareKeys.ToPublic(),
EarlySecret: chs13.earlySecret.Secret(),
BinderKey: chs13.binderKey,
CertReq: chs13.certReq.toPublic(),
UsingPSK: chs13.usingPSK,
SentDummyCCS: chs13.sentDummyCCS,
Suite: chs13.suite.toPublic(),
TrafficSecret: chs13.trafficSecret,
Transcript: chs13.transcript,
}
return &PubClientHandshakeState{
C: chs13.c,
ServerHello: chs13.serverHello.getPublicPtr(),
Hello: chs13.hello.getPublicPtr(),
Session: chs13.session,
MasterSecret: chs13.masterSecret.Secret(),
State13: tls13State,
uconn: chs13.uconn,
}
}
}
func (chs *PubClientHandshakeState) toPrivate12() *clientHandshakeState {
if chs == nil {
return nil
} else {
return &clientHandshakeState{
c: chs.C,
serverHello: chs.ServerHello.getPrivatePtr(),
hello: chs.Hello.getPrivatePtr(),
suite: chs.State12.Suite.getPrivatePtr(),
session: chs.Session,
masterSecret: chs.MasterSecret,
finishedHash: chs.State12.FinishedHash.getPrivateObj(),
uconn: chs.uconn,
}
}
}
func (chs12 *clientHandshakeState) toPublic12() *PubClientHandshakeState {
if chs12 == nil {
return nil
} else {
tls12State := TLS12OnlyState{
Suite: chs12.suite.getPublicObj(),
FinishedHash: chs12.finishedHash.getPublicObj(),
}
return &PubClientHandshakeState{
C: chs12.c,
ServerHello: chs12.serverHello.getPublicPtr(),
Hello: chs12.hello.getPublicPtr(),
Session: chs12.session,
MasterSecret: chs12.masterSecret,
State12: tls12State,
uconn: chs12.uconn,
}
}
}
// type EcdheParameters interface {
// ecdheParameters
// }
type CertificateRequestMsgTLS13 struct {
// Deprecated: crypto/tls no longer use this variable. This field won't be read or used by utls, but will still be populated.
// Support may be removed in the future.
Raw []byte
OcspStapling bool
Scts bool
SupportedSignatureAlgorithms []SignatureScheme
SupportedSignatureAlgorithmsCert []SignatureScheme
CertificateAuthorities [][]byte
}
func (crm *certificateRequestMsgTLS13) toPublic() *CertificateRequestMsgTLS13 {
if crm == nil {
return nil
} else {
rawBytes := []byte{}
if raw, err := crm.marshal(); err == nil {
rawBytes = raw
}
return &CertificateRequestMsgTLS13{
Raw: rawBytes,
OcspStapling: crm.ocspStapling,
Scts: crm.scts,
SupportedSignatureAlgorithms: crm.supportedSignatureAlgorithms,
SupportedSignatureAlgorithmsCert: crm.supportedSignatureAlgorithmsCert,
CertificateAuthorities: crm.certificateAuthorities,
}
}
}
func (crm *CertificateRequestMsgTLS13) toPrivate() *certificateRequestMsgTLS13 {
if crm == nil {
return nil
} else {
return &certificateRequestMsgTLS13{
ocspStapling: crm.OcspStapling,
scts: crm.Scts,
supportedSignatureAlgorithms: crm.SupportedSignatureAlgorithms,
supportedSignatureAlgorithmsCert: crm.SupportedSignatureAlgorithmsCert,
certificateAuthorities: crm.CertificateAuthorities,
}
}
}
type PubCipherSuiteTLS13 struct {
Id uint16
KeyLen int
Aead func(key, fixedNonce []byte) aead
Hash crypto.Hash
}
func (c *cipherSuiteTLS13) toPublic() *PubCipherSuiteTLS13 {
if c == nil {
return nil
} else {
return &PubCipherSuiteTLS13{
Id: c.id,
KeyLen: c.keyLen,
Aead: c.aead,
Hash: c.hash,
}
}
}
func (c *PubCipherSuiteTLS13) toPrivate() *cipherSuiteTLS13 {
if c == nil {
return nil
} else {
return &cipherSuiteTLS13{
id: c.Id,
keyLen: c.KeyLen,
aead: c.Aead,
hash: c.Hash,
}
}
}
type PubServerHelloMsg struct {
Raw []byte // renamed to serverHelloMsg.original in crypto/tls
Vers uint16
Random []byte
SessionId []byte
CipherSuite uint16
CompressionMethod uint8
NextProtoNeg bool
NextProtos []string
OcspStapling bool
Scts [][]byte
ExtendedMasterSecret bool
TicketSupported bool // used by go tls to determine whether to add the session ticket ext
SecureRenegotiation []byte
SecureRenegotiationSupported bool
AlpnProtocol string
// 1.3
SupportedVersion uint16
ServerShare KeyShare
SelectedIdentityPresent bool
SelectedIdentity uint16
Cookie []byte // HelloRetryRequest extension
SelectedGroup CurveID // HelloRetryRequest extension
}
func (shm *PubServerHelloMsg) getPrivatePtr() *serverHelloMsg {
if shm == nil {
return nil
} else {
return &serverHelloMsg{
original: shm.Raw,
vers: shm.Vers,
random: shm.Random,
sessionId: shm.SessionId,
cipherSuite: shm.CipherSuite,
compressionMethod: shm.CompressionMethod,
nextProtoNeg: shm.NextProtoNeg,
nextProtos: shm.NextProtos,
ocspStapling: shm.OcspStapling,
scts: shm.Scts,
extendedMasterSecret: shm.ExtendedMasterSecret,
ticketSupported: shm.TicketSupported,
secureRenegotiation: shm.SecureRenegotiation,
secureRenegotiationSupported: shm.SecureRenegotiationSupported,
alpnProtocol: shm.AlpnProtocol,
supportedVersion: shm.SupportedVersion,
serverShare: shm.ServerShare.ToPrivate(),
selectedIdentityPresent: shm.SelectedIdentityPresent,
selectedIdentity: shm.SelectedIdentity,
cookie: shm.Cookie,
selectedGroup: shm.SelectedGroup,
}
}
}
func (shm *serverHelloMsg) getPublicPtr() *PubServerHelloMsg {
if shm == nil {
return nil
} else {
return &PubServerHelloMsg{
Raw: shm.original,
Vers: shm.vers,
Random: shm.random,
SessionId: shm.sessionId,
CipherSuite: shm.cipherSuite,
CompressionMethod: shm.compressionMethod,
NextProtoNeg: shm.nextProtoNeg,
NextProtos: shm.nextProtos,
OcspStapling: shm.ocspStapling,
Scts: shm.scts,
ExtendedMasterSecret: shm.extendedMasterSecret,
TicketSupported: shm.ticketSupported,
SecureRenegotiation: shm.secureRenegotiation,
SecureRenegotiationSupported: shm.secureRenegotiationSupported,
AlpnProtocol: shm.alpnProtocol,
SupportedVersion: shm.supportedVersion,
ServerShare: shm.serverShare.ToPublic(),
SelectedIdentityPresent: shm.selectedIdentityPresent,
SelectedIdentity: shm.selectedIdentity,
Cookie: shm.cookie,
SelectedGroup: shm.selectedGroup,
}
}
}
type PubClientHelloMsg struct {
Raw []byte // renamed to clientHelloMsg.original in crypto/tls
Vers uint16
Random []byte
SessionId []byte
CipherSuites []uint16
CompressionMethods []uint8
NextProtoNeg bool
ServerName string
OcspStapling bool
Scts bool
Ems bool // [uTLS] actually implemented due to its prevalence
SupportedCurves []CurveID
SupportedPoints []uint8
TicketSupported bool
SessionTicket []uint8
SupportedSignatureAlgorithms []SignatureScheme
SecureRenegotiation []byte
SecureRenegotiationSupported bool
AlpnProtocols []string
// 1.3
SupportedSignatureAlgorithmsCert []SignatureScheme
SupportedVersions []uint16
Cookie []byte
KeyShares []KeyShare
EarlyData bool
PskModes []uint8
PskIdentities []PskIdentity
PskBinders [][]byte
QuicTransportParameters []byte
cachedPrivateHello *clientHelloMsg // todo: further optimize to reduce clientHelloMsg construction
encryptedClientHello []byte
}
func (chm *PubClientHelloMsg) getPrivatePtr() *clientHelloMsg {
if chm == nil {
return nil
} else {
private := &clientHelloMsg{
original: chm.Raw,
vers: chm.Vers,
random: chm.Random,
sessionId: chm.SessionId,
cipherSuites: chm.CipherSuites,
compressionMethods: chm.CompressionMethods,
serverName: chm.ServerName,
ocspStapling: chm.OcspStapling,
supportedCurves: chm.SupportedCurves,
supportedPoints: chm.SupportedPoints,
ticketSupported: chm.TicketSupported,
sessionTicket: chm.SessionTicket,
supportedSignatureAlgorithms: chm.SupportedSignatureAlgorithms,
supportedSignatureAlgorithmsCert: chm.SupportedSignatureAlgorithmsCert,
secureRenegotiationSupported: chm.SecureRenegotiationSupported,
secureRenegotiation: chm.SecureRenegotiation,
extendedMasterSecret: chm.Ems,
alpnProtocols: chm.AlpnProtocols,
scts: chm.Scts,
supportedVersions: chm.SupportedVersions,
cookie: chm.Cookie,
keyShares: KeyShares(chm.KeyShares).ToPrivate(),
earlyData: chm.EarlyData,
pskModes: chm.PskModes,
pskIdentities: PskIdentities(chm.PskIdentities).ToPrivate(),
pskBinders: chm.PskBinders,
quicTransportParameters: chm.QuicTransportParameters,
encryptedClientHello: chm.encryptedClientHello,
nextProtoNeg: chm.NextProtoNeg,
}
chm.cachedPrivateHello = private
return private
}
}
func (chm *PubClientHelloMsg) getCachedPrivatePtr() *clientHelloMsg {
if chm == nil {
return nil
} else {
return chm.cachedPrivateHello
}
}
func (chm *clientHelloMsg) getPublicPtr() *PubClientHelloMsg {
if chm == nil {
return nil
} else {
return &PubClientHelloMsg{
Raw: chm.original,
Vers: chm.vers,
Random: chm.random,
SessionId: chm.sessionId,
CipherSuites: chm.cipherSuites,
CompressionMethods: chm.compressionMethods,
NextProtoNeg: chm.nextProtoNeg,
ServerName: chm.serverName,
OcspStapling: chm.ocspStapling,
Scts: chm.scts,
Ems: chm.extendedMasterSecret,
SupportedCurves: chm.supportedCurves,
SupportedPoints: chm.supportedPoints,
TicketSupported: chm.ticketSupported,
SessionTicket: chm.sessionTicket,
SupportedSignatureAlgorithms: chm.supportedSignatureAlgorithms,
SecureRenegotiation: chm.secureRenegotiation,
SecureRenegotiationSupported: chm.secureRenegotiationSupported,
AlpnProtocols: chm.alpnProtocols,
SupportedSignatureAlgorithmsCert: chm.supportedSignatureAlgorithmsCert,
SupportedVersions: chm.supportedVersions,
Cookie: chm.cookie,
KeyShares: keyShares(chm.keyShares).ToPublic(),
EarlyData: chm.earlyData,
PskModes: chm.pskModes,
PskIdentities: pskIdentities(chm.pskIdentities).ToPublic(),
PskBinders: chm.pskBinders,
QuicTransportParameters: chm.quicTransportParameters,
cachedPrivateHello: chm,
encryptedClientHello: chm.encryptedClientHello,
}
}
}
// UnmarshalClientHello allows external code to parse raw client hellos.
// It returns nil on failure.
func UnmarshalClientHello(data []byte) *PubClientHelloMsg {
m := &clientHelloMsg{}
if m.unmarshal(data) {
return m.getPublicPtr()
}
return nil
}
// Marshal allows external code to convert a ClientHello object back into
// raw bytes.
func (chm *PubClientHelloMsg) Marshal() ([]byte, error) {
return chm.getPrivatePtr().marshal()
}
// A CipherSuite is a specific combination of key agreement, cipher and MAC
// function. All cipher suites currently assume RSA key agreement.
type PubCipherSuite struct {
Id uint16
// the lengths, in bytes, of the key material needed for each component.
KeyLen int
MacLen int
IvLen int
Ka func(version uint16) keyAgreement
// flags is a bitmask of the suite* values, above.
Flags int
Cipher func(key, iv []byte, isRead bool) interface{}
Mac func(macKey []byte) hash.Hash
Aead func(key, fixedNonce []byte) aead
}
func (cs *PubCipherSuite) getPrivatePtr() *cipherSuite {
if cs == nil {
return nil
} else {
return &cipherSuite{
id: cs.Id,
keyLen: cs.KeyLen,
macLen: cs.MacLen,
ivLen: cs.IvLen,
ka: cs.Ka,
flags: cs.Flags,
cipher: cs.Cipher,
mac: cs.Mac,
aead: cs.Aead,
}
}
}
func (cs *cipherSuite) getPublicObj() PubCipherSuite {
if cs == nil {
return PubCipherSuite{}
} else {
return PubCipherSuite{
Id: cs.id,
KeyLen: cs.keyLen,
MacLen: cs.macLen,
IvLen: cs.ivLen,
Ka: cs.ka,
Flags: cs.flags,
Cipher: cs.cipher,
Mac: cs.mac,
Aead: cs.aead,
}
}
}
// A FinishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type FinishedHash struct {
Client hash.Hash
Server hash.Hash
// Prior to TLS 1.2, an additional MD5 hash is required.
ClientMD5 hash.Hash
ServerMD5 hash.Hash
// In TLS 1.2, a full buffer is sadly required.
Buffer []byte
Version uint16
Prfv2 prfFunc
// Deprecated: Use Prfv2 instead. Prfv2 will be used if both are set.
Prf prfFuncOld
}
type prfFuncOld func(result, secret, label, seed []byte)
func prfFuncV1ToV2(v1 prfFuncOld) prfFunc {
return func(secret []byte, label string, seed []byte, keyLen int) []byte {
res := make([]byte, keyLen)
v1(res, secret, []byte(label), seed)
return res
}
}
func prfFuncV2ToV1(v2 prfFunc) prfFuncOld {
return func(result, secret, label, seed []byte) {
copy(result, v2(secret, string(label), seed, len(result)))
}
}
func (fh *FinishedHash) getPrivateObj() finishedHash {
if fh == nil {
return finishedHash{}
} else {
res := finishedHash{
client: fh.Client,
server: fh.Server,
clientMD5: fh.ClientMD5,
serverMD5: fh.ServerMD5,
buffer: fh.Buffer,
version: fh.Version,
}
if fh.Prfv2 != nil {
res.prf = fh.Prfv2
} else if fh.Prf != nil {
res.prf = prfFuncV1ToV2(fh.Prf)
}
return res
}
}
func (fh *finishedHash) getPublicObj() FinishedHash {
if fh == nil {
return FinishedHash{}
} else {
res := FinishedHash{
Client: fh.client,
Server: fh.server,
ClientMD5: fh.clientMD5,
ServerMD5: fh.serverMD5,
Buffer: fh.buffer,
Version: fh.version,
}
res.Prfv2 = fh.prf
res.Prf = prfFuncV2ToV1(fh.prf)
return res
}
}
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
type KeyShare struct {
Group CurveID `json:"group"`
Data []byte `json:"key_exchange,omitempty"` // optional
}
func (ks KeyShare) ToPrivate() keyShare {
return keyShare{group: ks.Group, data: ks.Data}
}
func (ks keyShare) ToPublic() KeyShare {
return KeyShare{Group: ks.group, Data: ks.data}
}
type KeyShares []KeyShare
type keyShares []keyShare
func (kss keyShares) ToPublic() []KeyShare {
var KSS []KeyShare
for _, ks := range kss {
KSS = append(KSS, ks.ToPublic())
}
return KSS
}
func (KSS KeyShares) ToPrivate() []keyShare {
var kss []keyShare
for _, KS := range KSS {
kss = append(kss, KS.ToPrivate())
}
return kss
}
// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
// session. See RFC 8446, Section 4.2.11.
type PskIdentity struct {
Label []byte `json:"identity"`
ObfuscatedTicketAge uint32 `json:"obfuscated_ticket_age"`
}
type PskIdentities []PskIdentity
type pskIdentities []pskIdentity
func (pss pskIdentities) ToPublic() []PskIdentity {
var PSS []PskIdentity
for _, ps := range pss {
PSS = append(PSS, PskIdentity{Label: ps.label, ObfuscatedTicketAge: ps.obfuscatedTicketAge})
}
return PSS
}
func (PSS PskIdentities) ToPrivate() []pskIdentity {
var pss []pskIdentity
for _, PS := range PSS {
pss = append(pss, pskIdentity{label: PS.Label, obfuscatedTicketAge: PS.ObfuscatedTicketAge})
}
return pss
}
// ClientSessionState is public, but all its fields are private. Let's add setters, getters and constructor
// ClientSessionState contains the state needed by clients to resume TLS sessions.
func MakeClientSessionState(
SessionTicket []uint8,
Vers uint16,
CipherSuite uint16,
MasterSecret []byte,
ServerCertificates []*x509.Certificate,
VerifiedChains [][]*x509.Certificate) *ClientSessionState {
// TODO: Add EMS to this constructor in uTLS v2
css := &ClientSessionState{
session: &SessionState{
version: Vers,
cipherSuite: CipherSuite,
secret: MasterSecret,
peerCertificates: ServerCertificates,
verifiedChains: VerifiedChains,
ticket: SessionTicket,
},
}
return css
}
// Encrypted ticket used for session resumption with server
func (css *ClientSessionState) SessionTicket() []uint8 {
return css.session.ticket
}
// SSL/TLS version negotiated for the session
func (css *ClientSessionState) Vers() uint16 {
return css.session.version
}
// Ciphersuite negotiated for the session
func (css *ClientSessionState) CipherSuite() uint16 {
return css.session.cipherSuite
}
// MasterSecret generated by client on a full handshake
func (css *ClientSessionState) MasterSecret() []byte {
return css.session.secret
}
func (css *ClientSessionState) EMS() bool {
return css.session.extMasterSecret
}
// Certificate chain presented by the server
func (css *ClientSessionState) ServerCertificates() []*x509.Certificate {
return css.session.peerCertificates
}
// Certificate chains we built for verification
func (css *ClientSessionState) VerifiedChains() [][]*x509.Certificate {
return css.session.verifiedChains
}
func (css *ClientSessionState) SetSessionTicket(SessionTicket []uint8) {
css.session.ticket = SessionTicket
}
func (css *ClientSessionState) SetVers(Vers uint16) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.version = Vers
}
func (css *ClientSessionState) SetCipherSuite(CipherSuite uint16) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.cipherSuite = CipherSuite
}
func (css *ClientSessionState) SetCreatedAt(createdAt uint64) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.createdAt = createdAt
}
func (css *ClientSessionState) SetMasterSecret(MasterSecret []byte) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.secret = MasterSecret
}
func (css *ClientSessionState) SetEMS(ems bool) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.extMasterSecret = ems
}
func (css *ClientSessionState) SetServerCertificates(ServerCertificates []*x509.Certificate) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.peerCertificates = ServerCertificates
}
func (css *ClientSessionState) SetVerifiedChains(VerifiedChains [][]*x509.Certificate) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.verifiedChains = VerifiedChains
}
func (css *ClientSessionState) SetUseBy(useBy uint64) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.useBy = useBy
}
func (css *ClientSessionState) SetAgeAdd(ageAdd uint32) {
if css.session == nil {
css.session = &SessionState{}
}
css.session.ageAdd = ageAdd
}
// TicketKey is the internal representation of a session ticket key.
type TicketKey struct {
AesKey [16]byte
HmacKey [16]byte
// created is the time at which this ticket key was created. See Config.ticketKeys.
Created time.Time
}
type TicketKeys []TicketKey
type ticketKeys []ticketKey
func TicketKeyFromBytes(b [32]byte) TicketKey {
// [uTLS]
// empty config is required
config := &Config{}
tk := config.ticketKeyFromBytes(b)
return tk.ToPublic()
}
func (tk ticketKey) ToPublic() TicketKey {
return TicketKey{
AesKey: tk.aesKey,
HmacKey: tk.hmacKey,
Created: tk.created,
}
}
func (TK TicketKey) ToPrivate() ticketKey {
return ticketKey{
aesKey: TK.AesKey,
hmacKey: TK.HmacKey,
created: TK.Created,
}
}
func (tks ticketKeys) ToPublic() []TicketKey {
var TKS []TicketKey
for _, ks := range tks {
TKS = append(TKS, ks.ToPublic())
}
return TKS
}
func (TKS TicketKeys) ToPrivate() []ticketKey {
var tks []ticketKey
for _, TK := range TKS {
tks = append(tks, TK.ToPrivate())
}
return tks
}
type kemPrivateKey struct {
secretKey any
curveID CurveID
}
// Deprecated: Use KeySharePrivateKeys instead. This type is no longer used.
// Will be removed in the future.
type KemPrivateKey struct {
SecretKey any
CurveID CurveID
}
func (kpk *KemPrivateKey) ToPrivate() *kemPrivateKey {
if kpk == nil {
return nil
} else {
return &kemPrivateKey{
secretKey: kpk.SecretKey,
curveID: kpk.CurveID,
}
}
}
func (kpk *kemPrivateKey) ToPublic() *KemPrivateKey {
if kpk == nil {
return nil
} else {
return &KemPrivateKey{
SecretKey: kpk.secretKey,
CurveID: kpk.curveID,
}
}
}
type KeySharePrivateKeys struct {
CurveID CurveID
Ecdhe *ecdh.PrivateKey
Mlkem *mlkem.DecapsulationKey768
MlkemEcdhe *ecdh.PrivateKey
}
func (ksp *KeySharePrivateKeys) ToPrivate() *keySharePrivateKeys {
if ksp == nil {
return nil
}
return &keySharePrivateKeys{
curveID: ksp.CurveID,
ecdhe: ksp.Ecdhe,
mlkem: ksp.Mlkem,
mlkemEcdhe: ksp.MlkemEcdhe,
}
}
func (ksp *keySharePrivateKeys) ToPublic() *KeySharePrivateKeys {
if ksp == nil {
return nil
}
return &KeySharePrivateKeys{
CurveID: ksp.curveID,
Ecdhe: ksp.ecdhe,
Mlkem: ksp.mlkem,
MlkemEcdhe: ksp.mlkemEcdhe,
}
}
+212
View File
@@ -0,0 +1,212 @@
// Copyright 2023 The uTLS Authors. 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 (
"context"
"errors"
"fmt"
)
// A UQUICConn represents a connection which uses a QUIC implementation as the underlying
// transport as described in RFC 9001.
//
// Methods of UQUICConn are not safe for concurrent use.
type UQUICConn struct {
conn *UConn
sessionTicketSent bool
}
// QUICClient returns a new TLS client side connection using QUICTransport as the
// underlying transport. The config cannot be nil.
//
// The config's MinVersion must be at least TLS 1.3.
func UQUICClient(config *QUICConfig, clientHelloID ClientHelloID) *UQUICConn {
return newUQUICConn(UClient(nil, config.TLSConfig, clientHelloID))
}
func newUQUICConn(uconn *UConn) *UQUICConn {
uconn.quic = &quicState{
signalc: make(chan struct{}),
blockedc: make(chan struct{}),
}
uconn.quic.events = uconn.quic.eventArr[:0]
return &UQUICConn{
conn: uconn,
}
}
// Start starts the client or server handshake protocol.
// It may produce connection events, which may be read with NextEvent.
//
// Start must be called at most once.
func (q *UQUICConn) Start(ctx context.Context) error {
if q.conn.quic.started {
return quicError(errors.New("tls: Start called more than once"))
}
q.conn.quic.started = true
if q.conn.config.MinVersion < VersionTLS13 {
return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.13"))
}
go q.conn.HandshakeContext(ctx)
if _, ok := <-q.conn.quic.blockedc; !ok {
return q.conn.handshakeErr
}
return nil
}
func (q *UQUICConn) ApplyPreset(p *ClientHelloSpec) error {
return q.conn.ApplyPreset(p)
}
// NextEvent returns the next event occurring on the connection.
// It returns an event with a Kind of QUICNoEvent when no events are available.
func (q *UQUICConn) NextEvent() QUICEvent {
qs := q.conn.quic
if last := qs.nextEvent - 1; last >= 0 && len(qs.events[last].Data) > 0 {
// Write over some of the previous event's data,
// to catch callers erroniously retaining it.
qs.events[last].Data[0] = 0
}
if qs.nextEvent >= len(qs.events) {
qs.events = qs.events[:0]
qs.nextEvent = 0
return QUICEvent{Kind: QUICNoEvent}
}
e := qs.events[qs.nextEvent]
qs.events[qs.nextEvent] = QUICEvent{} // zero out references to data
qs.nextEvent++
return e
}
// Close closes the connection and stops any in-progress handshake.
func (q *UQUICConn) Close() error {
if q.conn.quic.cancel == nil {
return nil // never started
}
q.conn.quic.cancel()
for range q.conn.quic.blockedc {
// Wait for the handshake goroutine to return.
}
return q.conn.handshakeErr
}
// HandleData handles handshake bytes received from the peer.
// It may produce connection events, which may be read with NextEvent.
func (q *UQUICConn) HandleData(level QUICEncryptionLevel, data []byte) error {
c := q.conn
if c.in.level != level {
return quicError(c.in.setErrorLocked(errors.New("tls: handshake data received at wrong level")))
}
c.quic.readbuf = data
<-c.quic.signalc
_, ok := <-c.quic.blockedc
if ok {
// The handshake goroutine is waiting for more data.
return nil
}
// The handshake goroutine has exited.
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
c.hand.Write(c.quic.readbuf)
c.quic.readbuf = nil
for q.conn.hand.Len() >= 4 && q.conn.handshakeErr == nil {
b := q.conn.hand.Bytes()
n := int(b[1])<<16 | int(b[2])<<8 | int(b[3])
if n > maxHandshake {
q.conn.handshakeErr = fmt.Errorf("tls: handshake message of length %d bytes exceeds maximum of %d bytes", n, maxHandshake)
break
}
if len(b) < 4+n {
return nil
}
if err := q.conn.handlePostHandshakeMessage(); err != nil {
q.conn.handshakeErr = err
}
}
if q.conn.handshakeErr != nil {
return quicError(q.conn.handshakeErr)
}
return nil
}
// SendSessionTicket sends a session ticket to the client.
// It produces connection events, which may be read with NextEvent.
// Currently, it can only be called once.
func (q *UQUICConn) SendSessionTicket(opts QUICSessionTicketOptions) error {
c := q.conn
if !c.isHandshakeComplete.Load() {
return quicError(errors.New("tls: SendSessionTicket called before handshake completed"))
}
if c.isClient {
return quicError(errors.New("tls: SendSessionTicket called on the client"))
}
if q.sessionTicketSent {
return quicError(errors.New("tls: SendSessionTicket called multiple times"))
}
q.sessionTicketSent = true
return quicError(c.sendSessionTicket(opts.EarlyData, opts.Extra))
}
// ConnectionState returns basic TLS details about the connection.
func (q *UQUICConn) ConnectionState() ConnectionState {
return q.conn.ConnectionState()
}
// SetTransportParameters sets the transport parameters to send to the peer.
//
// Server connections may delay setting the transport parameters until after
// receiving the client's transport parameters. See QUICTransportParametersRequired.
func (q *UQUICConn) SetTransportParameters(params []byte) {
if params == nil {
params = []byte{}
}
q.conn.quic.transportParams = params // this won't be used for building ClientHello when using a preset
// // instead, we set the transport parameters hold by the ClientHello
// for _, ext := range q.conn.Extensions {
// if qtp, ok := ext.(*QUICTransportParametersExtension); ok {
// qtp.TransportParametersExtData = params
// }
// }
if q.conn.quic.started {
<-q.conn.quic.signalc
<-q.conn.quic.blockedc
}
}
func (uc *UConn) QUICSetReadSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
uc.quic.events = append(uc.quic.events, QUICEvent{
Kind: QUICSetReadSecret,
Level: level,
Suite: suite,
Data: secret,
})
}
func (uc *UConn) QUICSetWriteSecret(level QUICEncryptionLevel, suite uint16, secret []byte) {
uc.quic.events = append(uc.quic.events, QUICEvent{
Kind: QUICSetWriteSecret,
Level: level,
Suite: suite,
Data: secret,
})
}
func (uc *UConn) QUICGetTransportParameters() ([]byte, error) {
if uc.quic.transportParams == nil {
uc.quic.events = append(uc.quic.events, QUICEvent{
Kind: QUICTransportParametersRequired,
})
}
for uc.quic.transportParams == nil {
if err := uc.quicWaitForSignal(); err != nil {
return nil, err
}
}
return uc.quic.transportParams, nil
}
@@ -0,0 +1,319 @@
package tls
import (
"crypto/rand"
"encoding/binary"
"math"
"math/big"
"github.com/refraction-networking/utls/internal/quicvarint"
)
const (
// RFC IDs
max_idle_timeout uint64 = 0x1
max_udp_payload_size uint64 = 0x3
initial_max_data uint64 = 0x4
initial_max_stream_data_bidi_local uint64 = 0x5
initial_max_stream_data_bidi_remote uint64 = 0x6
initial_max_stream_data_uni uint64 = 0x7
initial_max_streams_bidi uint64 = 0x8
initial_max_streams_uni uint64 = 0x9
max_ack_delay uint64 = 0xb
disable_active_migration uint64 = 0xc
active_connection_id_limit uint64 = 0xe
initial_source_connection_id uint64 = 0xf
version_information uint64 = 0x11 // RFC 9368
padding uint64 = 0x15
max_datagram_frame_size uint64 = 0x20 // RFC 9221
grease_quic_bit uint64 = 0x2ab2
// Legacy IDs from draft
version_information_legacy uint64 = 0xff73db // draft-ietf-quic-version-negotiation-13 and early
)
type TransportParameters []TransportParameter
func (tps TransportParameters) Marshal() []byte {
var b []byte
for _, tp := range tps {
b = quicvarint.Append(b, tp.ID())
b = quicvarint.Append(b, uint64(len(tp.Value())))
b = append(b, tp.Value()...)
}
return b
}
// TransportParameter represents a QUIC transport parameter.
//
// Caller will write the following to the wire:
//
// var b []byte
// b = quicvarint.Append(b, ID())
// b = quicvarint.Append(b, len(Value()))
// b = append(b, Value())
//
// Therefore Value() should return the exact bytes to be written to the wire AFTER the length field,
// i.e., the bytes MAY be a Variable Length Integer per RFC depending on the type of the transport
// parameter, but MUST NOT including the length field unless the parameter is defined so.
type TransportParameter interface {
ID() uint64
Value() []byte
}
type GREASETransportParameter struct {
IdOverride uint64 // if set to a valid GREASE ID, use this instead of randomly generated one.
Length uint16 // if len(ValueOverride) == 0, will generate random data of this size.
ValueOverride []byte // if len(ValueOverride) > 0, use this instead of random bytes.
}
const (
GREASE_MAX_MULTIPLIER = (0x3FFFFFFFFFFFFFFF - 27) / 31
)
// IsGREASEID returns true if id is a valid GREASE ID for
// transport parameters.
func (GREASETransportParameter) IsGREASEID(id uint64) bool {
return id >= 27 && (id-27)%31 == 0
}
// GetGREASEID returns a random valid GREASE ID for transport parameters.
func (GREASETransportParameter) GetGREASEID() uint64 {
max := big.NewInt(GREASE_MAX_MULTIPLIER)
randMultiply, err := rand.Int(rand.Reader, max)
if err != nil {
return 27
}
return 27 + randMultiply.Uint64()*31
}
func (g *GREASETransportParameter) ID() uint64 {
if !g.IsGREASEID(g.IdOverride) {
g.IdOverride = g.GetGREASEID()
}
return g.IdOverride
}
func (g *GREASETransportParameter) Value() []byte {
if len(g.ValueOverride) == 0 {
g.ValueOverride = make([]byte, g.Length)
rand.Read(g.ValueOverride)
}
return g.ValueOverride
}
type MaxIdleTimeout uint64 // in milliseconds
func (MaxIdleTimeout) ID() uint64 {
return max_idle_timeout
}
func (m MaxIdleTimeout) Value() []byte {
return quicvarint.Append([]byte{}, uint64(m))
}
type MaxUDPPayloadSize uint64
func (MaxUDPPayloadSize) ID() uint64 {
return max_udp_payload_size
}
func (m MaxUDPPayloadSize) Value() []byte {
return quicvarint.Append([]byte{}, uint64(m))
}
type InitialMaxData uint64
func (InitialMaxData) ID() uint64 {
return initial_max_data
}
func (i InitialMaxData) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type InitialMaxStreamDataBidiLocal uint64
func (InitialMaxStreamDataBidiLocal) ID() uint64 {
return initial_max_stream_data_bidi_local
}
func (i InitialMaxStreamDataBidiLocal) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type InitialMaxStreamDataBidiRemote uint64
func (InitialMaxStreamDataBidiRemote) ID() uint64 {
return initial_max_stream_data_bidi_remote
}
func (i InitialMaxStreamDataBidiRemote) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type InitialMaxStreamDataUni uint64
func (InitialMaxStreamDataUni) ID() uint64 {
return initial_max_stream_data_uni
}
func (i InitialMaxStreamDataUni) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type InitialMaxStreamsBidi uint64
func (InitialMaxStreamsBidi) ID() uint64 {
return initial_max_streams_bidi
}
func (i InitialMaxStreamsBidi) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type InitialMaxStreamsUni uint64
func (InitialMaxStreamsUni) ID() uint64 {
return initial_max_streams_uni
}
func (i InitialMaxStreamsUni) Value() []byte {
return quicvarint.Append([]byte{}, uint64(i))
}
type MaxAckDelay uint64
func (MaxAckDelay) ID() uint64 {
return max_ack_delay
}
func (m MaxAckDelay) Value() []byte {
return quicvarint.Append([]byte{}, uint64(m))
}
type DisableActiveMigration struct{}
func (*DisableActiveMigration) ID() uint64 {
return disable_active_migration
}
// Its Value MUST ALWAYS be empty.
func (*DisableActiveMigration) Value() []byte {
return []byte{}
}
type ActiveConnectionIDLimit uint64
func (ActiveConnectionIDLimit) ID() uint64 {
return active_connection_id_limit
}
func (a ActiveConnectionIDLimit) Value() []byte {
return quicvarint.Append([]byte{}, uint64(a))
}
type InitialSourceConnectionID []byte // if empty, will be set to the Connection ID used for the Initial packet.
func (InitialSourceConnectionID) ID() uint64 {
return initial_source_connection_id
}
func (i InitialSourceConnectionID) Value() []byte {
return []byte(i)
}
type VersionInformation struct {
ChoosenVersion uint32
AvailableVersions []uint32 // Also known as "Other Versions" in early drafts.
LegacyID bool // If true, use the legacy-assigned ID (0xff73db) instead of the RFC-assigned one (0x11).
}
const (
VERSION_NEGOTIATION uint32 = 0x00000000 // rfc9000
VERSION_1 uint32 = 0x00000001 // rfc9000
VERSION_2 uint32 = 0x6b3343cf // rfc9369
VERSION_GREASE uint32 = 0x0a0a0a0a // -> 0x?a?a?a?a
)
func (v *VersionInformation) ID() uint64 {
if v.LegacyID {
return version_information_legacy
}
return version_information
}
func (v *VersionInformation) Value() []byte {
var b []byte
b = binary.BigEndian.AppendUint32(b, v.ChoosenVersion)
for _, version := range v.AvailableVersions {
if version != VERSION_GREASE {
b = binary.BigEndian.AppendUint32(b, version)
} else {
b = binary.BigEndian.AppendUint32(b, v.GetGREASEVersion())
}
}
return b
}
func (*VersionInformation) GetGREASEVersion() uint32 {
// get a random uint32
max := big.NewInt(math.MaxUint32)
randVal, err := rand.Int(rand.Reader, max)
if err != nil {
return VERSION_GREASE
}
return uint32(randVal.Uint64()&math.MaxUint32) | 0x0a0a0a0a // all GREASE versions are in 0x?a?a?a?a
}
type PaddingTransportParameter []byte
func (PaddingTransportParameter) ID() uint64 {
return padding
}
func (p PaddingTransportParameter) Value() []byte {
return p
}
type MaxDatagramFrameSize uint64
func (MaxDatagramFrameSize) ID() uint64 {
return max_datagram_frame_size
}
func (m MaxDatagramFrameSize) Value() []byte {
return quicvarint.Append([]byte{}, uint64(m))
}
type GREASEQUICBit struct{}
func (*GREASEQUICBit) ID() uint64 {
return grease_quic_bit
}
// Its Value MUST ALWAYS be empty.
func (*GREASEQUICBit) Value() []byte {
return []byte{}
}
type FakeQUICTransportParameter struct {
Id uint64
Val []byte
}
func (f *FakeQUICTransportParameter) ID() uint64 {
if f.Id == 0 {
panic("it is not allowed to use a FakeQUICTransportParameter without setting the ID")
}
return f.Id
}
func (f *FakeQUICTransportParameter) Value() []byte {
return f.Val
}
+100
View File
@@ -0,0 +1,100 @@
package tls
import (
"net"
"sync"
"time"
)
type Roller struct {
HelloIDs []ClientHelloID
HelloIDMu sync.Mutex
WorkingHelloID *ClientHelloID
TcpDialTimeout time.Duration
TlsHandshakeTimeout time.Duration
r *prng
}
// NewRoller creates Roller object with default range of HelloIDs to cycle through until a
// working/unblocked one is found.
func NewRoller() (*Roller, error) {
r, err := newPRNG()
if err != nil {
return nil, err
}
tcpDialTimeoutInc := r.Intn(14)
tcpDialTimeoutInc = 7 + tcpDialTimeoutInc
tlsHandshakeTimeoutInc := r.Intn(20)
tlsHandshakeTimeoutInc = 11 + tlsHandshakeTimeoutInc
return &Roller{
HelloIDs: []ClientHelloID{
HelloChrome_Auto,
HelloFirefox_Auto,
HelloIOS_Auto,
HelloRandomized,
},
TcpDialTimeout: time.Second * time.Duration(tcpDialTimeoutInc),
TlsHandshakeTimeout: time.Second * time.Duration(tlsHandshakeTimeoutInc),
r: r,
}, nil
}
// Dial attempts to establish connection to given address using different HelloIDs.
// If a working HelloID is found, it is used again for subsequent Dials.
// If tcp connection fails or all HelloIDs are tried, returns with last error.
//
// Usage examples:
// Dial("tcp4", "google.com:443", "google.com")
// Dial("tcp", "10.23.144.22:443", "mywebserver.org")
func (c *Roller) Dial(network, addr, serverName string) (*UConn, error) {
helloIDs := make([]ClientHelloID, len(c.HelloIDs))
copy(helloIDs, c.HelloIDs)
c.r.rand.Shuffle(len(c.HelloIDs), func(i, j int) {
helloIDs[i], helloIDs[j] = helloIDs[j], helloIDs[i]
})
c.HelloIDMu.Lock()
workingHelloId := c.WorkingHelloID // keep using same helloID, if it works
c.HelloIDMu.Unlock()
if workingHelloId != nil {
helloIDFound := false
for i, ID := range helloIDs {
if ID == *workingHelloId {
helloIDs[i] = helloIDs[0]
helloIDs[0] = *workingHelloId // push working hello ID first
helloIDFound = true
break
}
}
if !helloIDFound {
helloIDs = append([]ClientHelloID{*workingHelloId}, helloIDs...)
}
}
var tcpConn net.Conn
var err error
for _, helloID := range helloIDs {
tcpConn, err = net.DialTimeout(network, addr, c.TcpDialTimeout)
if err != nil {
return nil, err // on tcp Dial failure return with error right away
}
client := UClient(tcpConn, nil, helloID)
client.SetSNI(serverName)
client.SetDeadline(time.Now().Add(c.TlsHandshakeTimeout))
err = client.Handshake()
client.SetDeadline(time.Time{}) // unset timeout
if err != nil {
continue // on tls Dial error keep trying HelloIDs
}
c.HelloIDMu.Lock()
c.WorkingHelloID = &client.ClientHelloID
c.HelloIDMu.Unlock()
return client, err
}
return nil, err
}
+361
View File
@@ -0,0 +1,361 @@
package tls
import (
"errors"
"fmt"
"github.com/refraction-networking/utls/internal/tls13"
)
// Tracking the state of calling conn.loadSession
type LoadSessionTrackerState int
const NeverCalled LoadSessionTrackerState = 0
const UtlsAboutToCall LoadSessionTrackerState = 1
const CalledByULoadSession LoadSessionTrackerState = 2
const CalledByGoTLS LoadSessionTrackerState = 3
// The state of the session controller
type sessionControllerState int
const NoSession sessionControllerState = 0
const SessionTicketExtInitialized sessionControllerState = 1
const SessionTicketExtAllSet sessionControllerState = 2
const PskExtInitialized sessionControllerState = 3
const PskExtAllSet sessionControllerState = 4
// sessionController is responsible for managing and controlling all session related states. It manages the lifecycle of the session ticket extension and the psk extension, including initialization, removal if the client hello spec doesn't contain any of them, and setting the prepared state to the client hello.
//
// Users should never directly modify the underlying state. Violations will result in undefined behaviors.
//
// Users should never construct sessionController by themselves, use the function `newSessionController` instead.
type sessionController struct {
// sessionTicketExt logically owns the session ticket extension
sessionTicketExt ISessionTicketExtension
// pskExtension logically owns the psk extension
pskExtension PreSharedKeyExtension
// uconnRef is a reference to the uconn
uconnRef *UConn
// state represents the internal state of the sessionController. Users are advised to modify the state only through designated methods and avoid direct manipulation, as doing so may result in undefined behavior.
state sessionControllerState
// loadSessionTracker keeps track of how the conn.loadSession method is being utilized.
loadSessionTracker LoadSessionTrackerState
// callingLoadSession is a boolean flag that indicates whether the `conn.loadSession` function is currently being invoked.
callingLoadSession bool
// locked is a boolean flag that becomes true once all states are appropriately set. Once `locked` is true, further modifications are disallowed, except for the binders.
locked bool
}
// newSessionController constructs a new SessionController
func newSessionController(uconn *UConn) *sessionController {
return &sessionController{
uconnRef: uconn,
sessionTicketExt: nil,
pskExtension: nil,
state: NoSession,
locked: false,
callingLoadSession: false,
loadSessionTracker: NeverCalled,
}
}
func (s *sessionController) isSessionLocked() bool {
return s.locked
}
type shouldLoadSessionResult int
const shouldReturn shouldLoadSessionResult = 0
const shouldSetTicket shouldLoadSessionResult = 1
const shouldSetPsk shouldLoadSessionResult = 2
const shouldLoad shouldLoadSessionResult = 3
// shouldLoadSession determines the appropriate action to take when it is time to load the session for the clientHello.
// There are several possible scenarios:
// - If a session ticket is already initialized, typically via the `initSessionTicketExt()` function, the ticket should be set in the client hello.
// - If a pre-shared key (PSK) is already initialized, typically via the `overridePskExt()` function, the PSK should be set in the client hello.
// - If both the `sessionTicketExt` and `pskExtension` are nil, which might occur if the client hello spec does not include them, we should skip the loadSession().
// - In all other cases, the function proceeds to load the session.
func (s *sessionController) shouldLoadSession() shouldLoadSessionResult {
if s.sessionTicketExt == nil && s.pskExtension == nil || s.uconnRef.clientHelloBuildStatus != NotBuilt {
// No need to load session since we don't have the related extensions.
return shouldReturn
}
if s.state == SessionTicketExtInitialized {
return shouldSetTicket
}
if s.state == PskExtInitialized {
return shouldSetPsk
}
return shouldLoad
}
// utlsAboutToLoadSession updates the loadSessionTracker to `UtlsAboutToCall` to signal the initiation of a session loading operation,
// provided that the preconditions are met. If the preconditions are not met (due to incorrect utls implementation), this function triggers a panic.
func (s *sessionController) utlsAboutToLoadSession() {
uAssert(s.state == NoSession && !s.locked, "tls: aboutToLoadSession failed: must only load session when the session of the client hello is not locked and when there's currently no session")
s.loadSessionTracker = UtlsAboutToCall
}
func (s *sessionController) assertHelloNotBuilt(caller string) {
if s.uconnRef.clientHelloBuildStatus != NotBuilt {
panic(fmt.Sprintf("tls: %s failed: we can't modify the session after the clientHello is built", caller))
}
}
func (s *sessionController) assertControllerState(caller string, desired sessionControllerState, moreDesiredStates ...sessionControllerState) {
if s.state != desired && !anyTrue(moreDesiredStates, func(_ int, state *sessionControllerState) bool {
return s.state == *state
}) {
panic(fmt.Sprintf("tls: %s failed: undesired controller state %d", caller, s.state))
}
}
func (s *sessionController) assertNotLocked(caller string) {
if s.locked {
panic(fmt.Sprintf("tls: %s failed: you must not modify the session after it's locked", caller))
}
}
func (s *sessionController) assertCanSkip(caller, extensionName string) {
if !s.uconnRef.skipResumptionOnNilExtension {
panic(fmt.Sprintf("tls: %s failed: session resumption is enabled, but there is no %s in the ClientHelloSpec; Please consider provide one in the ClientHelloSpec; If this is intentional, you may consider disable resumption by setting Config.SessionTicketsDisabled to true, or set Config.PreferSkipResumptionOnNilExtension to true to suppress this exception", caller, extensionName))
}
}
// finalCheck performs a comprehensive check on the updated state to ensure the correctness of the changes.
// If the checks pass successfully, the sessionController's state will be locked.
// Any failure in passing the tests indicates incorrect implementations in the utls, which will result in triggering a panic.
// Refer to the documentation for the `locked` field for more detailed information.
func (s *sessionController) finalCheck() {
s.assertControllerState("SessionController.finalCheck", PskExtAllSet, SessionTicketExtAllSet, NoSession)
s.locked = true
}
func initializationGuard[E Initializable, I func(E)](extension E, initializer I) {
uAssert(!extension.IsInitialized(), "tls: initialization failed: the extension is already initialized")
initializer(extension)
uAssert(extension.IsInitialized(), "tls: initialization failed: the extension is not initialized after initialization")
}
// initSessionTicketExt initializes the ticket and sets the state to `TicketInitialized`.
func (s *sessionController) initSessionTicketExt(session *SessionState, ticket []byte) {
s.assertNotLocked("initSessionTicketExt")
s.assertHelloNotBuilt("initSessionTicketExt")
s.assertControllerState("initSessionTicketExt", NoSession)
panicOnNil("initSessionTicketExt", session, ticket)
if s.sessionTicketExt == nil {
s.assertCanSkip("initSessionTicketExt", "session ticket extension")
return
}
initializationGuard(s.sessionTicketExt, func(e ISessionTicketExtension) {
s.sessionTicketExt.InitializeByUtls(session, ticket)
})
s.state = SessionTicketExtInitialized
}
// initPSK initializes the PSK extension using a valid session. The PSK extension
// should not be initialized previously, and the parameters must not be nil;
// otherwise, this function will trigger a panic.
func (s *sessionController) initPskExt(session *SessionState, earlySecret *tls13.EarlySecret, binderKey []byte, pskIdentities []pskIdentity) {
s.assertNotLocked("initPskExt")
s.assertHelloNotBuilt("initPskExt")
s.assertControllerState("initPskExt", NoSession)
panicOnNil("initPskExt", session, earlySecret, pskIdentities)
if s.pskExtension == nil {
s.assertCanSkip("initPskExt", "pre-shared key extension")
return
}
initializationGuard(s.pskExtension, func(e PreSharedKeyExtension) {
publicPskIdentities := mapSlice(pskIdentities, func(private pskIdentity) PskIdentity {
return PskIdentity{
Label: private.label,
ObfuscatedTicketAge: private.obfuscatedTicketAge,
}
})
e.InitializeByUtls(session, earlySecret.Secret(), binderKey, publicPskIdentities)
})
s.state = PskExtInitialized
}
// setSessionTicketToUConn write the ticket states from the session ticket extension to the client hello and handshake state.
func (s *sessionController) setSessionTicketToUConn() {
uAssert(s.sessionTicketExt != nil && s.state == SessionTicketExtInitialized, "tls: setSessionTicketExt failed: invalid state")
s.uconnRef.HandshakeState.Session = s.sessionTicketExt.GetSession()
s.uconnRef.HandshakeState.Hello.SessionTicket = s.sessionTicketExt.GetTicket()
s.state = SessionTicketExtAllSet
}
// setPskToUConn sets the psk to the handshake state and client hello.
func (s *sessionController) setPskToUConn() {
uAssert(s.pskExtension != nil && (s.state == PskExtInitialized || s.state == PskExtAllSet), "tls: setPskToUConn failed: invalid state")
pskCommon := s.pskExtension.GetPreSharedKeyCommon()
if s.state == PskExtInitialized {
s.uconnRef.HandshakeState.State13.EarlySecret = pskCommon.EarlySecret
s.uconnRef.HandshakeState.Session = pskCommon.Session
s.uconnRef.HandshakeState.Hello.PskIdentities = pskCommon.Identities
s.uconnRef.HandshakeState.Hello.PskBinders = pskCommon.Binders
} else if s.state == PskExtAllSet {
uAssert(s.uconnRef.HandshakeState.Session == pskCommon.Session && sliceEq(s.uconnRef.HandshakeState.State13.EarlySecret, pskCommon.EarlySecret) &&
allTrue(s.uconnRef.HandshakeState.Hello.PskIdentities, func(i int, psk *PskIdentity) bool {
return pskCommon.Identities[i].ObfuscatedTicketAge == psk.ObfuscatedTicketAge && sliceEq(pskCommon.Identities[i].Label, psk.Label)
}), "tls: setPskToUConn failed: only binders are allowed to change on state `PskAllSet`")
}
s.uconnRef.HandshakeState.State13.BinderKey = pskCommon.BinderKey
s.state = PskExtAllSet
}
// shouldUpdateBinders determines whether binders should be updated based on the presence of an initialized psk extension.
// This function returns true if an initialized psk extension exists. Binders are allowed to be updated when the state is `PskAllSet`,
// as the `BuildHandshakeState` function can be called multiple times in this case. However, it's important to note that
// the session state, apart from binders, should not be altered more than once.
func (s *sessionController) shouldUpdateBinders() bool {
if s.pskExtension == nil {
return false
}
return (s.state == PskExtInitialized || s.state == PskExtAllSet)
}
func (s *sessionController) updateBinders() {
uAssert(s.shouldUpdateBinders(), "tls: updateBinders failed: shouldn't update binders")
s.pskExtension.PatchBuiltHello(s.uconnRef.HandshakeState.Hello)
}
func (s *sessionController) overrideExtension(extension Initializable, override func(), initializedState sessionControllerState) error {
panicOnNil("overrideExtension", extension)
s.assertNotLocked("overrideExtension")
s.assertControllerState("overrideExtension", NoSession)
override()
if extension.IsInitialized() {
s.state = initializedState
}
return nil
}
// overridePskExt allows the user of utls to customize the psk extension.
func (s *sessionController) overridePskExt(pskExt PreSharedKeyExtension) error {
return s.overrideExtension(pskExt, func() { s.pskExtension = pskExt }, PskExtInitialized)
}
// overridePskExt allows the user of utls to customize the session ticket extension.
func (s *sessionController) overrideSessionTicketExt(sessionTicketExt ISessionTicketExtension) error {
return s.overrideExtension(sessionTicketExt, func() { s.sessionTicketExt = sessionTicketExt }, SessionTicketExtInitialized)
}
// syncSessionExts synchronizes the sessionController with the session-related
// extensions from the extension list after applying client hello specs.
//
// - If the extension list is missing the session ticket extension or PSK
// extension, owned extensions are dropped and states are reset.
// - If the user provides a session ticket extension or PSK extension, the
// corresponding extension from the extension list will be replaced.
// - If the user doesn't provide session-related extensions, the extensions
// from the extension list will be utilized.
//
// This function ensures that there is only one session ticket extension or PSK
// extension, and that the PSK extension is the last extension in the extension
// list.
func (s *sessionController) syncSessionExts() error {
uAssert(s.uconnRef.clientHelloBuildStatus == NotBuilt, "tls: checkSessionExts failed: we can't modify the session after the clientHello is built")
s.assertNotLocked("checkSessionExts")
s.assertHelloNotBuilt("checkSessionExts")
s.assertControllerState("checkSessionExts", NoSession, SessionTicketExtInitialized, PskExtInitialized)
numSessionExt := 0
hasPskExt := false
for i, e := range s.uconnRef.Extensions {
switch ext := e.(type) {
case ISessionTicketExtension:
uAssert(numSessionExt == 0, "tls: checkSessionExts failed: multiple ISessionTicketExtensions in the extension list")
if s.sessionTicketExt == nil {
// If there isn't a user-provided session ticket extension, use the one from the spec
s.sessionTicketExt = ext
} else {
// Otherwise, replace the one in the extension list with the user-provided one
s.uconnRef.Extensions[i] = s.sessionTicketExt
}
numSessionExt += 1
case PreSharedKeyExtension:
uAssert(i == len(s.uconnRef.Extensions)-1, "tls: checkSessionExts failed: PreSharedKeyExtension must be the last extension")
if s.pskExtension == nil {
// If there isn't a user-provided psk extension, use the one from the spec
s.pskExtension = ext
} else {
// Otherwise, replace the one in the extension list with the user-provided one
s.uconnRef.Extensions[i] = s.pskExtension
}
s.pskExtension.SetOmitEmptyPsk(s.uconnRef.config.OmitEmptyPsk)
hasPskExt = true
}
}
if numSessionExt == 0 {
if s.state == SessionTicketExtInitialized {
return errors.New("tls: checkSessionExts failed: the user provided a session ticket, but the specification doesn't contain one")
}
s.sessionTicketExt = nil
s.uconnRef.HandshakeState.Session = nil
s.uconnRef.HandshakeState.Hello.SessionTicket = nil
}
if !hasPskExt {
if s.state == PskExtInitialized {
return errors.New("tls: checkSessionExts failed: the user provided a psk, but the specification doesn't contain one")
}
s.pskExtension = nil
s.uconnRef.HandshakeState.State13.BinderKey = nil
s.uconnRef.HandshakeState.State13.EarlySecret = nil
s.uconnRef.HandshakeState.Session = nil
s.uconnRef.HandshakeState.Hello.PskIdentities = nil
}
return nil
}
// onEnterLoadSessionCheck is intended to be invoked upon entering the `conn.loadSession` function.
// It is designed to ensure the correctness of the utls implementation. If the utls implementation is found to be incorrect, this function will trigger a panic.
func (s *sessionController) onEnterLoadSessionCheck() {
uAssert(!s.locked, "tls: LoadSessionCoordinator.onEnterLoadSessionCheck failed: session is set and locked, no call to loadSession is allowed")
switch s.loadSessionTracker {
case UtlsAboutToCall, NeverCalled:
s.callingLoadSession = true
case CalledByULoadSession, CalledByGoTLS:
panic("tls: LoadSessionCoordinator.onEnterLoadSessionCheck failed: you must not call loadSession() twice")
default:
panic("tls: LoadSessionCoordinator.onEnterLoadSessionCheck failed: unimplemented state")
}
}
// onLoadSessionReturn is intended to be invoked upon returning from the `conn.loadSession` function.
// It serves as a validation step for the correctness of the underlying utls implementation.
// If the utls implementation is incorrect, this function will trigger a panic.
func (s *sessionController) onLoadSessionReturn() {
uAssert(s.callingLoadSession, "tls: LoadSessionCoordinator.onLoadSessionReturn failed: it's not loading sessions, perhaps this function is not being called by loadSession.")
switch s.loadSessionTracker {
case NeverCalled:
s.loadSessionTracker = CalledByGoTLS
case UtlsAboutToCall:
s.loadSessionTracker = CalledByULoadSession
default:
panic("tls: LoadSessionCoordinator.onLoadSessionReturn failed: unimplemented state")
}
s.callingLoadSession = false
}
// shouldLoadSessionWriteBinders checks if `conn.loadSession` should proceed to write binders and marshal the client hello. If the utls implementation
// is incorrect, this function will trigger a panic.
func (s *sessionController) shouldLoadSessionWriteBinders() bool {
uAssert(s.callingLoadSession, "tls: shouldWriteBinders failed: LoadSessionCoordinator isn't loading sessions, perhaps this function is not being called by loadSession.")
switch s.loadSessionTracker {
case NeverCalled:
return true
case UtlsAboutToCall:
return false
default:
panic("tls: shouldWriteBinders failed: unimplemented state")
}
}
+82
View File
@@ -0,0 +1,82 @@
package tls
import "io"
type ISessionTicketExtension interface {
TLSExtension
// If false is returned, utls will invoke `InitializeByUtls()` for the necessary initialization.
Initializable
// InitializeByUtls is invoked when IsInitialized() returns false.
// It initializes the extension using a real and valid TLS 1.2 session.
InitializeByUtls(session *SessionState, ticket []byte)
GetSession() *SessionState
GetTicket() []byte
}
// SessionTicketExtension implements session_ticket (35)
type SessionTicketExtension struct {
Session *SessionState
Ticket []byte
Initialized bool
}
func (e *SessionTicketExtension) writeToUConn(uc *UConn) error {
// session states are handled later. At this point tickets aren't
// being loaded by utls, so don't write anything to the UConn.
uc.HandshakeState.Hello.TicketSupported = true // This doesn't really matter, this field is only used to add session ticket ext in go tls.
return nil
}
func (e *SessionTicketExtension) Len() int {
return 4 + len(e.Ticket)
}
func (e *SessionTicketExtension) Read(b []byte) (int, error) {
if len(b) < e.Len() {
return 0, io.ErrShortBuffer
}
extBodyLen := e.Len() - 4
b[0] = byte(extensionSessionTicket >> 8)
b[1] = byte(extensionSessionTicket)
b[2] = byte(extBodyLen >> 8)
b[3] = byte(extBodyLen)
if extBodyLen > 0 {
copy(b[4:], e.Ticket)
}
return e.Len(), io.EOF
}
func (e *SessionTicketExtension) IsInitialized() bool {
return e.Initialized
}
func (e *SessionTicketExtension) InitializeByUtls(session *SessionState, ticket []byte) {
uAssert(!e.Initialized, "tls: InitializeByUtls failed: the SessionTicketExtension is initialized")
uAssert(session.version == VersionTLS12 && session != nil && ticket != nil, "tls: InitializeByUtls failed: the session is not a tls 1.2 session")
e.Session = session
e.Ticket = ticket
e.Initialized = true
}
func (e *SessionTicketExtension) UnmarshalJSON(_ []byte) error {
return nil // no-op
}
func (e *SessionTicketExtension) Write(_ []byte) (int, error) {
// RFC 5077, Section 3.2
return 0, nil
}
func (e *SessionTicketExtension) GetSession() *SessionState {
return e.Session
}
func (e *SessionTicketExtension) GetTicket() []byte {
return e.Ticket
}
File diff suppressed because it is too large Load Diff