mirror of
https://github.com/SagerNet/sing-shadowtls.git
synced 2025-03-31 10:47:35 +03:00
Import crypto/tls from 1.23.3
This commit is contained in:
parent
007caf5e65
commit
789a9918a5
30 changed files with 14821 additions and 2 deletions
4
go.mod
4
go.mod
|
@ -5,5 +5,7 @@ go 1.20
|
|||
require (
|
||||
github.com/sagernet/sing v0.5.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
)
|
||||
|
||||
require golang.org/x/sys v0.27.0
|
||||
|
|
2
go.sum
2
go.sum
|
@ -5,6 +5,8 @@ github.com/sagernet/sing v0.5.0/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfl
|
|||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
3
internal/README.md
Normal file
3
internal/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# internal
|
||||
|
||||
copied from go 1.23.3
|
149
internal/byteorder/byteorder.go
Normal file
149
internal/byteorder/byteorder.go
Normal file
|
@ -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),
|
||||
)
|
||||
}
|
258
internal/hpke/hpke.go
Normal file
258
internal/hpke/hpke.go
Normal file
|
@ -0,0 +1,258 @@
|
|||
// 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"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math/bits"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// 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(suiteID []byte, salt []byte, label string, inputKey []byte) []byte {
|
||||
labeledIKM := make([]byte, 0, 7+len(suiteID)+len(label)+len(inputKey))
|
||||
labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
|
||||
labeledIKM = append(labeledIKM, suiteID...)
|
||||
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 = binary.BigEndian.AppendUint16(labeledInfo, length)
|
||||
labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
|
||||
labeledInfo = append(labeledInfo, suiteID...)
|
||||
labeledInfo = append(labeledInfo, label...)
|
||||
labeledInfo = append(labeledInfo, info...)
|
||||
out := make([]byte, length)
|
||||
n, err := hkdf.Expand(kdf.hash.New, randomKey, labeledInfo).Read(out)
|
||||
if err != nil || n != int(length) {
|
||||
panic("hpke: LabeledExpand failed unexpectedly")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// dhKEM implements the KEM specified in RFC 9180, Section 4.1.
|
||||
type dhKEM struct {
|
||||
dh ecdh.Curve
|
||||
kdf hkdfKDF
|
||||
|
||||
suiteID []byte
|
||||
nSecret uint16
|
||||
}
|
||||
|
||||
var SupportedKEMs = map[uint16]struct {
|
||||
curve ecdh.Curve
|
||||
hash crypto.Hash
|
||||
nSecret uint16
|
||||
}{
|
||||
// RFC 9180 Section 7.1
|
||||
0x0020: {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: binary.BigEndian.AppendUint16([]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
|
||||
}
|
||||
|
||||
type Sender struct {
|
||||
aead cipher.AEAD
|
||||
kem *dhKEM
|
||||
|
||||
sharedSecret []byte
|
||||
|
||||
suiteID []byte
|
||||
|
||||
key []byte
|
||||
baseNonce []byte
|
||||
exporterSecret []byte
|
||||
|
||||
seqNum uint128
|
||||
}
|
||||
|
||||
var aesGCMNew = func(key []byte) (cipher.AEAD, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cipher.NewGCM(block)
|
||||
}
|
||||
|
||||
var SupportedAEADs = map[uint16]struct {
|
||||
keySize int
|
||||
nonceSize int
|
||||
aead func([]byte) (cipher.AEAD, error)
|
||||
}{
|
||||
// RFC 9180, Section 7.3
|
||||
0x0001: {keySize: 16, nonceSize: 12, aead: aesGCMNew},
|
||||
0x0002: {keySize: 32, nonceSize: 12, aead: aesGCMNew},
|
||||
0x0003: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New},
|
||||
}
|
||||
|
||||
var SupportedKDFs = map[uint16]func() *hkdfKDF{
|
||||
// RFC 9180, Section 7.2
|
||||
0x0001: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} },
|
||||
}
|
||||
|
||||
func SetupSender(kemID, kdfID, aeadID uint16, pub crypto.PublicKey, info []byte) ([]byte, *Sender, error) {
|
||||
suiteID := SuiteID(kemID, kdfID, aeadID)
|
||||
|
||||
kem, err := newDHKem(kemID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pubRecipient, ok := pub.(*ecdh.PublicKey)
|
||||
if !ok {
|
||||
return nil, nil, errors.New("incorrect public key type")
|
||||
}
|
||||
sharedSecret, encapsulatedKey, err := kem.Encap(pubRecipient)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
kdfInit, ok := SupportedKDFs[kdfID]
|
||||
if !ok {
|
||||
return nil, nil, errors.New("unsupported KDF id")
|
||||
}
|
||||
kdf := kdfInit()
|
||||
|
||||
aeadInfo, ok := SupportedAEADs[aeadID]
|
||||
if !ok {
|
||||
return nil, nil, errors.New("unsupported AEAD id")
|
||||
}
|
||||
|
||||
pskIDHash := kdf.LabeledExtract(suiteID, nil, "psk_id_hash", nil)
|
||||
infoHash := kdf.LabeledExtract(suiteID, nil, "info_hash", info)
|
||||
ksContext := append([]byte{0}, pskIDHash...)
|
||||
ksContext = append(ksContext, infoHash...)
|
||||
|
||||
secret := kdf.LabeledExtract(suiteID, sharedSecret, "secret", nil)
|
||||
|
||||
key := kdf.LabeledExpand(suiteID, secret, "key", ksContext, uint16(aeadInfo.keySize) /* Nk - key size for AEAD */)
|
||||
baseNonce := kdf.LabeledExpand(suiteID, secret, "base_nonce", ksContext, uint16(aeadInfo.nonceSize) /* Nn - nonce size for AEAD */)
|
||||
exporterSecret := kdf.LabeledExpand(suiteID, secret, "exp", ksContext, uint16(kdf.hash.Size()) /* Nh - hash output size of the kdf*/)
|
||||
|
||||
aead, err := aeadInfo.aead(key)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return encapsulatedKey, &Sender{
|
||||
kem: kem,
|
||||
aead: aead,
|
||||
sharedSecret: sharedSecret,
|
||||
suiteID: suiteID,
|
||||
key: key,
|
||||
baseNonce: baseNonce,
|
||||
exporterSecret: exporterSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Sender) nextNonce() []byte {
|
||||
nonce := s.seqNum.bytes()[16-s.aead.NonceSize():]
|
||||
for i := range s.baseNonce {
|
||||
nonce[i] ^= s.baseNonce[i]
|
||||
}
|
||||
// Message limit is, according to the RFC, 2^95+1, which
|
||||
// is somewhat confusing, but we do as we're told.
|
||||
if s.seqNum.bitLen() >= (s.aead.NonceSize()*8)-1 {
|
||||
panic("message limit reached")
|
||||
}
|
||||
s.seqNum = s.seqNum.addOne()
|
||||
return nonce
|
||||
}
|
||||
|
||||
func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) {
|
||||
ciphertext := s.aead.Seal(nil, s.nextNonce(), plaintext, aad)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func SuiteID(kemID, kdfID, aeadID uint16) []byte {
|
||||
suiteID := make([]byte, 0, 4+2+2+2)
|
||||
suiteID = append(suiteID, []byte("HPKE")...)
|
||||
suiteID = binary.BigEndian.AppendUint16(suiteID, kemID)
|
||||
suiteID = binary.BigEndian.AppendUint16(suiteID, kdfID)
|
||||
suiteID = binary.BigEndian.AppendUint16(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)
|
||||
}
|
||||
|
||||
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)
|
||||
binary.BigEndian.PutUint64(b[0:], u.hi)
|
||||
binary.BigEndian.PutUint64(b[8:], u.lo)
|
||||
return b
|
||||
}
|
887
internal/mlkem768/mlkem768.go
Normal file
887
internal/mlkem768/mlkem768.go
Normal file
|
@ -0,0 +1,887 @@
|
|||
// 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 mlkem768 implements the quantum-resistant key encapsulation method
|
||||
// ML-KEM (formerly known as Kyber).
|
||||
//
|
||||
// Only the recommended ML-KEM-768 parameter set is provided.
|
||||
//
|
||||
// The version currently implemented is the one specified by [NIST FIPS 203 ipd],
|
||||
// with the unintentional transposition of the matrix A reverted to match the
|
||||
// behavior of [Kyber version 3.0]. Future versions of this package might
|
||||
// introduce backwards incompatible changes to implement changes to FIPS 203.
|
||||
//
|
||||
// [Kyber version 3.0]: https://pq-crystals.org/kyber/data/kyber-specification-round3-20210804.pdf
|
||||
// [NIST FIPS 203 ipd]: https://doi.org/10.6028/NIST.FIPS.203.ipd
|
||||
package mlkem768
|
||||
|
||||
// This package targets security, correctness, simplicity, readability, and
|
||||
// reviewability as its primary goals. All critical operations are performed in
|
||||
// constant time.
|
||||
//
|
||||
// Variable and function names, as well as code layout, are selected to
|
||||
// facilitate reviewing the implementation against the NIST FIPS 203 ipd
|
||||
// document.
|
||||
//
|
||||
// Reviewers unfamiliar with polynomials or linear algebra might find the
|
||||
// background at https://words.filippo.io/kyber-math/ useful.
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
|
||||
"github.com/sagernet/sing-shadowtls/internal/byteorder"
|
||||
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
// ML-KEM global constants.
|
||||
n = 256
|
||||
q = 3329
|
||||
|
||||
log2q = 12
|
||||
|
||||
// ML-KEM-768 parameters. The code makes assumptions based on these values,
|
||||
// they can't be changed blindly.
|
||||
k = 3
|
||||
η = 2
|
||||
du = 10
|
||||
dv = 4
|
||||
|
||||
// encodingSizeX is the byte size of a ringElement or nttElement encoded
|
||||
// by ByteEncode_X (FIPS 203 (DRAFT), Algorithm 4).
|
||||
encodingSize12 = n * log2q / 8
|
||||
encodingSize10 = n * du / 8
|
||||
encodingSize4 = n * dv / 8
|
||||
encodingSize1 = n * 1 / 8
|
||||
|
||||
messageSize = encodingSize1
|
||||
decryptionKeySize = k * encodingSize12
|
||||
encryptionKeySize = k*encodingSize12 + 32
|
||||
|
||||
CiphertextSize = k*encodingSize10 + encodingSize4
|
||||
EncapsulationKeySize = encryptionKeySize
|
||||
DecapsulationKeySize = decryptionKeySize + encryptionKeySize + 32 + 32
|
||||
SharedKeySize = 32
|
||||
SeedSize = 32 + 32
|
||||
)
|
||||
|
||||
// A DecapsulationKey is the secret key used to decapsulate a shared key from a
|
||||
// ciphertext. It includes various precomputed values.
|
||||
type DecapsulationKey struct {
|
||||
dk [DecapsulationKeySize]byte
|
||||
encryptionKey
|
||||
decryptionKey
|
||||
}
|
||||
|
||||
// Bytes returns the extended encoding of the decapsulation key, according to
|
||||
// FIPS 203 (DRAFT).
|
||||
func (dk *DecapsulationKey) Bytes() []byte {
|
||||
var b [DecapsulationKeySize]byte
|
||||
copy(b[:], dk.dk[:])
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// EncapsulationKey returns the public encapsulation key necessary to produce
|
||||
// ciphertexts.
|
||||
func (dk *DecapsulationKey) EncapsulationKey() []byte {
|
||||
var b [EncapsulationKeySize]byte
|
||||
copy(b[:], dk.dk[decryptionKeySize:])
|
||||
return b[:]
|
||||
}
|
||||
|
||||
// encryptionKey is the parsed and expanded form of a PKE encryption key.
|
||||
type encryptionKey struct {
|
||||
t [k]nttElement // ByteDecode₁₂(ek[:384k])
|
||||
A [k * k]nttElement // A[i*k+j] = sampleNTT(ρ, j, i)
|
||||
}
|
||||
|
||||
// decryptionKey is the parsed and expanded form of a PKE decryption key.
|
||||
type decryptionKey struct {
|
||||
s [k]nttElement // ByteDecode₁₂(dk[:decryptionKeySize])
|
||||
}
|
||||
|
||||
// GenerateKey generates a new decapsulation key, drawing random bytes from
|
||||
// crypto/rand. The decapsulation key must be kept secret.
|
||||
func GenerateKey() (*DecapsulationKey, error) {
|
||||
// The actual logic is in a separate function to outline this allocation.
|
||||
dk := &DecapsulationKey{}
|
||||
return generateKey(dk)
|
||||
}
|
||||
|
||||
func generateKey(dk *DecapsulationKey) (*DecapsulationKey, error) {
|
||||
var d [32]byte
|
||||
if _, err := rand.Read(d[:]); err != nil {
|
||||
return nil, errors.New("mlkem768: crypto/rand Read failed: " + err.Error())
|
||||
}
|
||||
var z [32]byte
|
||||
if _, err := rand.Read(z[:]); err != nil {
|
||||
return nil, errors.New("mlkem768: crypto/rand Read failed: " + err.Error())
|
||||
}
|
||||
return kemKeyGen(dk, &d, &z), nil
|
||||
}
|
||||
|
||||
// NewKeyFromSeed deterministically generates a decapsulation key from a 64-byte
|
||||
// seed in the "d || z" form. The seed must be uniformly random.
|
||||
func NewKeyFromSeed(seed []byte) (*DecapsulationKey, error) {
|
||||
// The actual logic is in a separate function to outline this allocation.
|
||||
dk := &DecapsulationKey{}
|
||||
return newKeyFromSeed(dk, seed)
|
||||
}
|
||||
|
||||
func newKeyFromSeed(dk *DecapsulationKey, seed []byte) (*DecapsulationKey, error) {
|
||||
if len(seed) != SeedSize {
|
||||
return nil, errors.New("mlkem768: invalid seed length")
|
||||
}
|
||||
d := (*[32]byte)(seed[:32])
|
||||
z := (*[32]byte)(seed[32:])
|
||||
return kemKeyGen(dk, d, z), nil
|
||||
}
|
||||
|
||||
// NewKeyFromExtendedEncoding parses a decapsulation key from its FIPS 203
|
||||
// (DRAFT) extended encoding.
|
||||
func NewKeyFromExtendedEncoding(decapsulationKey []byte) (*DecapsulationKey, error) {
|
||||
// The actual logic is in a separate function to outline this allocation.
|
||||
dk := &DecapsulationKey{}
|
||||
return newKeyFromExtendedEncoding(dk, decapsulationKey)
|
||||
}
|
||||
|
||||
func newKeyFromExtendedEncoding(dk *DecapsulationKey, dkBytes []byte) (*DecapsulationKey, error) {
|
||||
if len(dkBytes) != DecapsulationKeySize {
|
||||
return nil, errors.New("mlkem768: invalid decapsulation key length")
|
||||
}
|
||||
|
||||
// Note that we don't check that H(ek) matches ekPKE, as that's not
|
||||
// specified in FIPS 203 (DRAFT). This is one reason to prefer the seed
|
||||
// private key format.
|
||||
dk.dk = [DecapsulationKeySize]byte(dkBytes)
|
||||
|
||||
dkPKE := dkBytes[:decryptionKeySize]
|
||||
if err := parseDK(&dk.decryptionKey, dkPKE); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ekPKE := dkBytes[decryptionKeySize : decryptionKeySize+encryptionKeySize]
|
||||
if err := parseEK(&dk.encryptionKey, ekPKE); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dk, nil
|
||||
}
|
||||
|
||||
// kemKeyGen generates a decapsulation key.
|
||||
//
|
||||
// It implements ML-KEM.KeyGen according to FIPS 203 (DRAFT), Algorithm 15, and
|
||||
// K-PKE.KeyGen according to FIPS 203 (DRAFT), Algorithm 12. The two are merged
|
||||
// to save copies and allocations.
|
||||
func kemKeyGen(dk *DecapsulationKey, d, z *[32]byte) *DecapsulationKey {
|
||||
if dk == nil {
|
||||
dk = &DecapsulationKey{}
|
||||
}
|
||||
|
||||
G := sha3.Sum512(d[:])
|
||||
ρ, σ := G[:32], G[32:]
|
||||
|
||||
A := &dk.A
|
||||
for i := byte(0); i < k; i++ {
|
||||
for j := byte(0); j < k; j++ {
|
||||
// Note that this is consistent with Kyber round 3, rather than with
|
||||
// the initial draft of FIPS 203, because NIST signaled that the
|
||||
// change was involuntary and will be reverted.
|
||||
A[i*k+j] = sampleNTT(ρ, j, i)
|
||||
}
|
||||
}
|
||||
|
||||
var N byte
|
||||
s := &dk.s
|
||||
for i := range s {
|
||||
s[i] = ntt(samplePolyCBD(σ, N))
|
||||
N++
|
||||
}
|
||||
e := make([]nttElement, k)
|
||||
for i := range e {
|
||||
e[i] = ntt(samplePolyCBD(σ, N))
|
||||
N++
|
||||
}
|
||||
|
||||
t := &dk.t
|
||||
for i := range t { // t = A ◦ s + e
|
||||
t[i] = e[i]
|
||||
for j := range s {
|
||||
t[i] = polyAdd(t[i], nttMul(A[i*k+j], s[j]))
|
||||
}
|
||||
}
|
||||
|
||||
// dkPKE ← ByteEncode₁₂(s)
|
||||
// ekPKE ← ByteEncode₁₂(t) || ρ
|
||||
// ek ← ekPKE
|
||||
// dk ← dkPKE || ek || H(ek) || z
|
||||
dkB := dk.dk[:0]
|
||||
|
||||
for i := range s {
|
||||
dkB = polyByteEncode(dkB, s[i])
|
||||
}
|
||||
|
||||
for i := range t {
|
||||
dkB = polyByteEncode(dkB, t[i])
|
||||
}
|
||||
dkB = append(dkB, ρ...)
|
||||
|
||||
H := sha3.New256()
|
||||
H.Write(dkB[decryptionKeySize:])
|
||||
dkB = H.Sum(dkB)
|
||||
|
||||
dkB = append(dkB, z[:]...)
|
||||
|
||||
if len(dkB) != len(dk.dk) {
|
||||
panic("mlkem768: internal error: invalid decapsulation key size")
|
||||
}
|
||||
|
||||
return dk
|
||||
}
|
||||
|
||||
// Encapsulate generates a shared key and an associated ciphertext from an
|
||||
// encapsulation key, drawing random bytes from crypto/rand.
|
||||
// If the encapsulation key is not valid, Encapsulate returns an error.
|
||||
//
|
||||
// The shared key must be kept secret.
|
||||
func Encapsulate(encapsulationKey []byte) (ciphertext, sharedKey []byte, err error) {
|
||||
// The actual logic is in a separate function to outline this allocation.
|
||||
var cc [CiphertextSize]byte
|
||||
return encapsulate(&cc, encapsulationKey)
|
||||
}
|
||||
|
||||
func encapsulate(cc *[CiphertextSize]byte, encapsulationKey []byte) (ciphertext, sharedKey []byte, err error) {
|
||||
if len(encapsulationKey) != EncapsulationKeySize {
|
||||
return nil, nil, errors.New("mlkem768: invalid encapsulation key length")
|
||||
}
|
||||
var m [messageSize]byte
|
||||
if _, err := rand.Read(m[:]); err != nil {
|
||||
return nil, nil, errors.New("mlkem768: crypto/rand Read failed: " + err.Error())
|
||||
}
|
||||
return kemEncaps(cc, encapsulationKey, &m)
|
||||
}
|
||||
|
||||
// kemEncaps generates a shared key and an associated ciphertext.
|
||||
//
|
||||
// It implements ML-KEM.Encaps according to FIPS 203 (DRAFT), Algorithm 16.
|
||||
func kemEncaps(cc *[CiphertextSize]byte, ek []byte, m *[messageSize]byte) (c, K []byte, err error) {
|
||||
if cc == nil {
|
||||
cc = &[CiphertextSize]byte{}
|
||||
}
|
||||
|
||||
H := sha3.Sum256(ek[:])
|
||||
g := sha3.New512()
|
||||
g.Write(m[:])
|
||||
g.Write(H[:])
|
||||
G := g.Sum(nil)
|
||||
K, r := G[:SharedKeySize], G[SharedKeySize:]
|
||||
var ex encryptionKey
|
||||
if err := parseEK(&ex, ek[:]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
c = pkeEncrypt(cc, &ex, m, r)
|
||||
return c, K, nil
|
||||
}
|
||||
|
||||
// parseEK parses an encryption key from its encoded form.
|
||||
//
|
||||
// It implements the initial stages of K-PKE.Encrypt according to FIPS 203
|
||||
// (DRAFT), Algorithm 13.
|
||||
func parseEK(ex *encryptionKey, ekPKE []byte) error {
|
||||
if len(ekPKE) != encryptionKeySize {
|
||||
return errors.New("mlkem768: invalid encryption key length")
|
||||
}
|
||||
|
||||
for i := range ex.t {
|
||||
var err error
|
||||
ex.t[i], err = polyByteDecode[nttElement](ekPKE[:encodingSize12])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ekPKE = ekPKE[encodingSize12:]
|
||||
}
|
||||
ρ := ekPKE
|
||||
|
||||
for i := byte(0); i < k; i++ {
|
||||
for j := byte(0); j < k; j++ {
|
||||
// See the note in pkeKeyGen about the order of the indices being
|
||||
// consistent with Kyber round 3.
|
||||
ex.A[i*k+j] = sampleNTT(ρ, j, i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pkeEncrypt encrypt a plaintext message.
|
||||
//
|
||||
// It implements K-PKE.Encrypt according to FIPS 203 (DRAFT), Algorithm 13,
|
||||
// although the computation of t and AT is done in parseEK.
|
||||
func pkeEncrypt(cc *[CiphertextSize]byte, ex *encryptionKey, m *[messageSize]byte, rnd []byte) []byte {
|
||||
var N byte
|
||||
r, e1 := make([]nttElement, k), make([]ringElement, k)
|
||||
for i := range r {
|
||||
r[i] = ntt(samplePolyCBD(rnd, N))
|
||||
N++
|
||||
}
|
||||
for i := range e1 {
|
||||
e1[i] = samplePolyCBD(rnd, N)
|
||||
N++
|
||||
}
|
||||
e2 := samplePolyCBD(rnd, N)
|
||||
|
||||
u := make([]ringElement, k) // NTT⁻¹(AT ◦ r) + e1
|
||||
for i := range u {
|
||||
u[i] = e1[i]
|
||||
for j := range r {
|
||||
// Note that i and j are inverted, as we need the transposed of A.
|
||||
u[i] = polyAdd(u[i], inverseNTT(nttMul(ex.A[j*k+i], r[j])))
|
||||
}
|
||||
}
|
||||
|
||||
μ := ringDecodeAndDecompress1(m)
|
||||
|
||||
var vNTT nttElement // t⊺ ◦ r
|
||||
for i := range ex.t {
|
||||
vNTT = polyAdd(vNTT, nttMul(ex.t[i], r[i]))
|
||||
}
|
||||
v := polyAdd(polyAdd(inverseNTT(vNTT), e2), μ)
|
||||
|
||||
c := cc[:0]
|
||||
for _, f := range u {
|
||||
c = ringCompressAndEncode10(c, f)
|
||||
}
|
||||
c = ringCompressAndEncode4(c, v)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
// Decapsulate generates a shared key from a ciphertext and a decapsulation key.
|
||||
// If the ciphertext is not valid, Decapsulate returns an error.
|
||||
//
|
||||
// The shared key must be kept secret.
|
||||
func Decapsulate(dk *DecapsulationKey, ciphertext []byte) (sharedKey []byte, err error) {
|
||||
if len(ciphertext) != CiphertextSize {
|
||||
return nil, errors.New("mlkem768: invalid ciphertext length")
|
||||
}
|
||||
c := (*[CiphertextSize]byte)(ciphertext)
|
||||
return kemDecaps(dk, c), nil
|
||||
}
|
||||
|
||||
// kemDecaps produces a shared key from a ciphertext.
|
||||
//
|
||||
// It implements ML-KEM.Decaps according to FIPS 203 (DRAFT), Algorithm 17.
|
||||
func kemDecaps(dk *DecapsulationKey, c *[CiphertextSize]byte) (K []byte) {
|
||||
h := dk.dk[decryptionKeySize+encryptionKeySize : decryptionKeySize+encryptionKeySize+32]
|
||||
z := dk.dk[decryptionKeySize+encryptionKeySize+32:]
|
||||
|
||||
m := pkeDecrypt(&dk.decryptionKey, c)
|
||||
g := sha3.New512()
|
||||
g.Write(m[:])
|
||||
g.Write(h)
|
||||
G := g.Sum(nil)
|
||||
Kprime, r := G[:SharedKeySize], G[SharedKeySize:]
|
||||
J := sha3.NewShake256()
|
||||
J.Write(z)
|
||||
J.Write(c[:])
|
||||
Kout := make([]byte, SharedKeySize)
|
||||
J.Read(Kout)
|
||||
var cc [CiphertextSize]byte
|
||||
c1 := pkeEncrypt(&cc, &dk.encryptionKey, (*[32]byte)(m), r)
|
||||
|
||||
subtle.ConstantTimeCopy(subtle.ConstantTimeCompare(c[:], c1), Kout, Kprime)
|
||||
return Kout
|
||||
}
|
||||
|
||||
// parseDK parses a decryption key from its encoded form.
|
||||
//
|
||||
// It implements the computation of s from K-PKE.Decrypt according to FIPS 203
|
||||
// (DRAFT), Algorithm 14.
|
||||
func parseDK(dx *decryptionKey, dkPKE []byte) error {
|
||||
if len(dkPKE) != decryptionKeySize {
|
||||
return errors.New("mlkem768: invalid decryption key length")
|
||||
}
|
||||
|
||||
for i := range dx.s {
|
||||
f, err := polyByteDecode[nttElement](dkPKE[:encodingSize12])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dx.s[i] = f
|
||||
dkPKE = dkPKE[encodingSize12:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// pkeDecrypt decrypts a ciphertext.
|
||||
//
|
||||
// It implements K-PKE.Decrypt according to FIPS 203 (DRAFT), Algorithm 14,
|
||||
// although the computation of s is done in parseDK.
|
||||
func pkeDecrypt(dx *decryptionKey, c *[CiphertextSize]byte) []byte {
|
||||
u := make([]ringElement, k)
|
||||
for i := range u {
|
||||
b := (*[encodingSize10]byte)(c[encodingSize10*i : encodingSize10*(i+1)])
|
||||
u[i] = ringDecodeAndDecompress10(b)
|
||||
}
|
||||
|
||||
b := (*[encodingSize4]byte)(c[encodingSize10*k:])
|
||||
v := ringDecodeAndDecompress4(b)
|
||||
|
||||
var mask nttElement // s⊺ ◦ NTT(u)
|
||||
for i := range dx.s {
|
||||
mask = polyAdd(mask, nttMul(dx.s[i], ntt(u[i])))
|
||||
}
|
||||
w := polySub(v, inverseNTT(mask))
|
||||
|
||||
return ringCompressAndEncode1(nil, w)
|
||||
}
|
||||
|
||||
// fieldElement is an integer modulo q, an element of ℤ_q. It is always reduced.
|
||||
type fieldElement uint16
|
||||
|
||||
// fieldCheckReduced checks that a value a is < q.
|
||||
func fieldCheckReduced(a uint16) (fieldElement, error) {
|
||||
if a >= q {
|
||||
return 0, errors.New("unreduced field element")
|
||||
}
|
||||
return fieldElement(a), nil
|
||||
}
|
||||
|
||||
// fieldReduceOnce reduces a value a < 2q.
|
||||
func fieldReduceOnce(a uint16) fieldElement {
|
||||
x := a - q
|
||||
// If x underflowed, then x >= 2¹⁶ - q > 2¹⁵, so the top bit is set.
|
||||
x += (x >> 15) * q
|
||||
return fieldElement(x)
|
||||
}
|
||||
|
||||
func fieldAdd(a, b fieldElement) fieldElement {
|
||||
x := uint16(a + b)
|
||||
return fieldReduceOnce(x)
|
||||
}
|
||||
|
||||
func fieldSub(a, b fieldElement) fieldElement {
|
||||
x := uint16(a - b + q)
|
||||
return fieldReduceOnce(x)
|
||||
}
|
||||
|
||||
const (
|
||||
barrettMultiplier = 5039 // 2¹² * 2¹² / q
|
||||
barrettShift = 24 // log₂(2¹² * 2¹²)
|
||||
)
|
||||
|
||||
// fieldReduce reduces a value a < 2q² using Barrett reduction, to avoid
|
||||
// potentially variable-time division.
|
||||
func fieldReduce(a uint32) fieldElement {
|
||||
quotient := uint32((uint64(a) * barrettMultiplier) >> barrettShift)
|
||||
return fieldReduceOnce(uint16(a - quotient*q))
|
||||
}
|
||||
|
||||
func fieldMul(a, b fieldElement) fieldElement {
|
||||
x := uint32(a) * uint32(b)
|
||||
return fieldReduce(x)
|
||||
}
|
||||
|
||||
// fieldMulSub returns a * (b - c). This operation is fused to save a
|
||||
// fieldReduceOnce after the subtraction.
|
||||
func fieldMulSub(a, b, c fieldElement) fieldElement {
|
||||
x := uint32(a) * uint32(b-c+q)
|
||||
return fieldReduce(x)
|
||||
}
|
||||
|
||||
// fieldAddMul returns a * b + c * d. This operation is fused to save a
|
||||
// fieldReduceOnce and a fieldReduce.
|
||||
func fieldAddMul(a, b, c, d fieldElement) fieldElement {
|
||||
x := uint32(a) * uint32(b)
|
||||
x += uint32(c) * uint32(d)
|
||||
return fieldReduce(x)
|
||||
}
|
||||
|
||||
// compress maps a field element uniformly to the range 0 to 2ᵈ-1, according to
|
||||
// FIPS 203 (DRAFT), Definition 4.5.
|
||||
func compress(x fieldElement, d uint8) uint16 {
|
||||
// We want to compute (x * 2ᵈ) / q, rounded to nearest integer, with 1/2
|
||||
// rounding up (see FIPS 203 (DRAFT), Section 2.3).
|
||||
|
||||
// Barrett reduction produces a quotient and a remainder in the range [0, 2q),
|
||||
// such that dividend = quotient * q + remainder.
|
||||
dividend := uint32(x) << d // x * 2ᵈ
|
||||
quotient := uint32(uint64(dividend) * barrettMultiplier >> barrettShift)
|
||||
remainder := dividend - quotient*q
|
||||
|
||||
// Since the remainder is in the range [0, 2q), not [0, q), we need to
|
||||
// portion it into three spans for rounding.
|
||||
//
|
||||
// [ 0, q/2 ) -> round to 0
|
||||
// [ q/2, q + q/2 ) -> round to 1
|
||||
// [ q + q/2, 2q ) -> round to 2
|
||||
//
|
||||
// We can convert that to the following logic: add 1 if remainder > q/2,
|
||||
// then add 1 again if remainder > q + q/2.
|
||||
//
|
||||
// Note that if remainder > x, then ⌊x⌋ - remainder underflows, and the top
|
||||
// bit of the difference will be set.
|
||||
quotient += (q/2 - remainder) >> 31 & 1
|
||||
quotient += (q + q/2 - remainder) >> 31 & 1
|
||||
|
||||
// quotient might have overflowed at this point, so reduce it by masking.
|
||||
var mask uint32 = (1 << d) - 1
|
||||
return uint16(quotient & mask)
|
||||
}
|
||||
|
||||
// decompress maps a number x between 0 and 2ᵈ-1 uniformly to the full range of
|
||||
// field elements, according to FIPS 203 (DRAFT), Definition 4.6.
|
||||
func decompress(y uint16, d uint8) fieldElement {
|
||||
// We want to compute (y * q) / 2ᵈ, rounded to nearest integer, with 1/2
|
||||
// rounding up (see FIPS 203 (DRAFT), Section 2.3).
|
||||
|
||||
dividend := uint32(y) * q
|
||||
quotient := dividend >> d // (y * q) / 2ᵈ
|
||||
|
||||
// The d'th least-significant bit of the dividend (the most significant bit
|
||||
// of the remainder) is 1 for the top half of the values that divide to the
|
||||
// same quotient, which are the ones that round up.
|
||||
quotient += dividend >> (d - 1) & 1
|
||||
|
||||
// quotient is at most (2¹¹-1) * q / 2¹¹ + 1 = 3328, so it didn't overflow.
|
||||
return fieldElement(quotient)
|
||||
}
|
||||
|
||||
// ringElement is a polynomial, an element of R_q, represented as an array
|
||||
// according to FIPS 203 (DRAFT), Section 2.4.
|
||||
type ringElement [n]fieldElement
|
||||
|
||||
// polyAdd adds two ringElements or nttElements.
|
||||
func polyAdd[T ~[n]fieldElement](a, b T) (s T) {
|
||||
for i := range s {
|
||||
s[i] = fieldAdd(a[i], b[i])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// polySub subtracts two ringElements or nttElements.
|
||||
func polySub[T ~[n]fieldElement](a, b T) (s T) {
|
||||
for i := range s {
|
||||
s[i] = fieldSub(a[i], b[i])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// polyByteEncode appends the 384-byte encoding of f to b.
|
||||
//
|
||||
// It implements ByteEncode₁₂, according to FIPS 203 (DRAFT), Algorithm 4.
|
||||
func polyByteEncode[T ~[n]fieldElement](b []byte, f T) []byte {
|
||||
out, B := sliceForAppend(b, encodingSize12)
|
||||
for i := 0; i < n; i += 2 {
|
||||
x := uint32(f[i]) | uint32(f[i+1])<<12
|
||||
B[0] = uint8(x)
|
||||
B[1] = uint8(x >> 8)
|
||||
B[2] = uint8(x >> 16)
|
||||
B = B[3:]
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// polyByteDecode decodes the 384-byte encoding of a polynomial, checking that
|
||||
// all the coefficients are properly reduced. This achieves the "Modulus check"
|
||||
// step of ML-KEM Encapsulation Input Validation.
|
||||
//
|
||||
// polyByteDecode is also used in ML-KEM Decapsulation, where the input
|
||||
// validation is not required, but implicitly allowed by the specification.
|
||||
//
|
||||
// It implements ByteDecode₁₂, according to FIPS 203 (DRAFT), Algorithm 5.
|
||||
func polyByteDecode[T ~[n]fieldElement](b []byte) (T, error) {
|
||||
if len(b) != encodingSize12 {
|
||||
return T{}, errors.New("mlkem768: invalid encoding length")
|
||||
}
|
||||
var f T
|
||||
for i := 0; i < n; i += 2 {
|
||||
d := uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16
|
||||
const mask12 = 0b1111_1111_1111
|
||||
var err error
|
||||
if f[i], err = fieldCheckReduced(uint16(d & mask12)); err != nil {
|
||||
return T{}, errors.New("mlkem768: invalid polynomial encoding")
|
||||
}
|
||||
if f[i+1], err = fieldCheckReduced(uint16(d >> 12)); err != nil {
|
||||
return T{}, errors.New("mlkem768: invalid polynomial encoding")
|
||||
}
|
||||
b = b[3:]
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
||||
|
||||
// ringCompressAndEncode1 appends a 32-byte encoding of a ring element to s,
|
||||
// compressing one coefficients per bit.
|
||||
//
|
||||
// It implements Compress₁, according to FIPS 203 (DRAFT), Definition 4.5,
|
||||
// followed by ByteEncode₁, according to FIPS 203 (DRAFT), Algorithm 4.
|
||||
func ringCompressAndEncode1(s []byte, f ringElement) []byte {
|
||||
s, b := sliceForAppend(s, encodingSize1)
|
||||
for i := range b {
|
||||
b[i] = 0
|
||||
}
|
||||
for i := range f {
|
||||
b[i/8] |= uint8(compress(f[i], 1) << (i % 8))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ringDecodeAndDecompress1 decodes a 32-byte slice to a ring element where each
|
||||
// bit is mapped to 0 or ⌈q/2⌋.
|
||||
//
|
||||
// It implements ByteDecode₁, according to FIPS 203 (DRAFT), Algorithm 5,
|
||||
// followed by Decompress₁, according to FIPS 203 (DRAFT), Definition 4.6.
|
||||
func ringDecodeAndDecompress1(b *[encodingSize1]byte) ringElement {
|
||||
var f ringElement
|
||||
for i := range f {
|
||||
b_i := b[i/8] >> (i % 8) & 1
|
||||
const halfQ = (q + 1) / 2 // ⌈q/2⌋, rounded up per FIPS 203 (DRAFT), Section 2.3
|
||||
f[i] = fieldElement(b_i) * halfQ // 0 decompresses to 0, and 1 to ⌈q/2⌋
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// ringCompressAndEncode4 appends a 128-byte encoding of a ring element to s,
|
||||
// compressing two coefficients per byte.
|
||||
//
|
||||
// It implements Compress₄, according to FIPS 203 (DRAFT), Definition 4.5,
|
||||
// followed by ByteEncode₄, according to FIPS 203 (DRAFT), Algorithm 4.
|
||||
func ringCompressAndEncode4(s []byte, f ringElement) []byte {
|
||||
s, b := sliceForAppend(s, encodingSize4)
|
||||
for i := 0; i < n; i += 2 {
|
||||
b[i/2] = uint8(compress(f[i], 4) | compress(f[i+1], 4)<<4)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ringDecodeAndDecompress4 decodes a 128-byte encoding of a ring element where
|
||||
// each four bits are mapped to an equidistant distribution.
|
||||
//
|
||||
// It implements ByteDecode₄, according to FIPS 203 (DRAFT), Algorithm 5,
|
||||
// followed by Decompress₄, according to FIPS 203 (DRAFT), Definition 4.6.
|
||||
func ringDecodeAndDecompress4(b *[encodingSize4]byte) ringElement {
|
||||
var f ringElement
|
||||
for i := 0; i < n; i += 2 {
|
||||
f[i] = fieldElement(decompress(uint16(b[i/2]&0b1111), 4))
|
||||
f[i+1] = fieldElement(decompress(uint16(b[i/2]>>4), 4))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// ringCompressAndEncode10 appends a 320-byte encoding of a ring element to s,
|
||||
// compressing four coefficients per five bytes.
|
||||
//
|
||||
// It implements Compress₁₀, according to FIPS 203 (DRAFT), Definition 4.5,
|
||||
// followed by ByteEncode₁₀, according to FIPS 203 (DRAFT), Algorithm 4.
|
||||
func ringCompressAndEncode10(s []byte, f ringElement) []byte {
|
||||
s, b := sliceForAppend(s, encodingSize10)
|
||||
for i := 0; i < n; i += 4 {
|
||||
var x uint64
|
||||
x |= uint64(compress(f[i+0], 10))
|
||||
x |= uint64(compress(f[i+1], 10)) << 10
|
||||
x |= uint64(compress(f[i+2], 10)) << 20
|
||||
x |= uint64(compress(f[i+3], 10)) << 30
|
||||
b[0] = uint8(x)
|
||||
b[1] = uint8(x >> 8)
|
||||
b[2] = uint8(x >> 16)
|
||||
b[3] = uint8(x >> 24)
|
||||
b[4] = uint8(x >> 32)
|
||||
b = b[5:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// ringDecodeAndDecompress10 decodes a 320-byte encoding of a ring element where
|
||||
// each ten bits are mapped to an equidistant distribution.
|
||||
//
|
||||
// It implements ByteDecode₁₀, according to FIPS 203 (DRAFT), Algorithm 5,
|
||||
// followed by Decompress₁₀, according to FIPS 203 (DRAFT), Definition 4.6.
|
||||
func ringDecodeAndDecompress10(bb *[encodingSize10]byte) ringElement {
|
||||
b := bb[:]
|
||||
var f ringElement
|
||||
for i := 0; i < n; i += 4 {
|
||||
x := uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | uint64(b[4])<<32
|
||||
b = b[5:]
|
||||
f[i] = fieldElement(decompress(uint16(x>>0&0b11_1111_1111), 10))
|
||||
f[i+1] = fieldElement(decompress(uint16(x>>10&0b11_1111_1111), 10))
|
||||
f[i+2] = fieldElement(decompress(uint16(x>>20&0b11_1111_1111), 10))
|
||||
f[i+3] = fieldElement(decompress(uint16(x>>30&0b11_1111_1111), 10))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// samplePolyCBD draws a ringElement from the special Dη distribution given a
|
||||
// stream of random bytes generated by the PRF function, according to FIPS 203
|
||||
// (DRAFT), Algorithm 7 and Definition 4.1.
|
||||
func samplePolyCBD(s []byte, b byte) ringElement {
|
||||
prf := sha3.NewShake256()
|
||||
prf.Write(s)
|
||||
prf.Write([]byte{b})
|
||||
B := make([]byte, 128)
|
||||
prf.Read(B)
|
||||
|
||||
// SamplePolyCBD simply draws four (2η) bits for each coefficient, and adds
|
||||
// the first two and subtracts the last two.
|
||||
|
||||
var f ringElement
|
||||
for i := 0; i < n; i += 2 {
|
||||
b := B[i/2]
|
||||
b_7, b_6, b_5, b_4 := b>>7, b>>6&1, b>>5&1, b>>4&1
|
||||
b_3, b_2, b_1, b_0 := b>>3&1, b>>2&1, b>>1&1, b&1
|
||||
f[i] = fieldSub(fieldElement(b_0+b_1), fieldElement(b_2+b_3))
|
||||
f[i+1] = fieldSub(fieldElement(b_4+b_5), fieldElement(b_6+b_7))
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
// nttElement is an NTT representation, an element of T_q, represented as an
|
||||
// array according to FIPS 203 (DRAFT), Section 2.4.
|
||||
type nttElement [n]fieldElement
|
||||
|
||||
// gammas are the values ζ^2BitRev7(i)+1 mod q for each index i.
|
||||
var gammas = [128]fieldElement{17, 3312, 2761, 568, 583, 2746, 2649, 680, 1637, 1692, 723, 2606, 2288, 1041, 1100, 2229, 1409, 1920, 2662, 667, 3281, 48, 233, 3096, 756, 2573, 2156, 1173, 3015, 314, 3050, 279, 1703, 1626, 1651, 1678, 2789, 540, 1789, 1540, 1847, 1482, 952, 2377, 1461, 1868, 2687, 642, 939, 2390, 2308, 1021, 2437, 892, 2388, 941, 733, 2596, 2337, 992, 268, 3061, 641, 2688, 1584, 1745, 2298, 1031, 2037, 1292, 3220, 109, 375, 2954, 2549, 780, 2090, 1239, 1645, 1684, 1063, 2266, 319, 3010, 2773, 556, 757, 2572, 2099, 1230, 561, 2768, 2466, 863, 2594, 735, 2804, 525, 1092, 2237, 403, 2926, 1026, 2303, 1143, 2186, 2150, 1179, 2775, 554, 886, 2443, 1722, 1607, 1212, 2117, 1874, 1455, 1029, 2300, 2110, 1219, 2935, 394, 885, 2444, 2154, 1175}
|
||||
|
||||
// nttMul multiplies two nttElements.
|
||||
//
|
||||
// It implements MultiplyNTTs, according to FIPS 203 (DRAFT), Algorithm 10.
|
||||
func nttMul(f, g nttElement) nttElement {
|
||||
var h nttElement
|
||||
// We use i += 2 for bounds check elimination. See https://go.dev/issue/66826.
|
||||
for i := 0; i < 256; i += 2 {
|
||||
a0, a1 := f[i], f[i+1]
|
||||
b0, b1 := g[i], g[i+1]
|
||||
h[i] = fieldAddMul(a0, b0, fieldMul(a1, b1), gammas[i/2])
|
||||
h[i+1] = fieldAddMul(a0, b1, a1, b0)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// zetas are the values ζ^BitRev7(k) mod q for each index k.
|
||||
var zetas = [128]fieldElement{1, 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, 2786, 3260, 569, 1746, 296, 2447, 1339, 1476, 3046, 56, 2240, 1333, 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289, 331, 3253, 1756, 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, 2319, 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, 2474, 3110, 1227, 910, 17, 2761, 583, 2649, 1637, 723, 2288, 1100, 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703, 1651, 2789, 1789, 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, 1584, 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, 2099, 561, 2466, 2594, 2804, 1092, 403, 1026, 1143, 2150, 2775, 886, 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154}
|
||||
|
||||
// ntt maps a ringElement to its nttElement representation.
|
||||
//
|
||||
// It implements NTT, according to FIPS 203 (DRAFT), Algorithm 8.
|
||||
func ntt(f ringElement) nttElement {
|
||||
k := 1
|
||||
for len := 128; len >= 2; len /= 2 {
|
||||
for start := 0; start < 256; start += 2 * len {
|
||||
zeta := zetas[k]
|
||||
k++
|
||||
// Bounds check elimination hint.
|
||||
f, flen := f[start:start+len], f[start+len:start+len+len]
|
||||
for j := 0; j < len; j++ {
|
||||
t := fieldMul(zeta, flen[j])
|
||||
flen[j] = fieldSub(f[j], t)
|
||||
f[j] = fieldAdd(f[j], t)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nttElement(f)
|
||||
}
|
||||
|
||||
// inverseNTT maps a nttElement back to the ringElement it represents.
|
||||
//
|
||||
// It implements NTT⁻¹, according to FIPS 203 (DRAFT), Algorithm 9.
|
||||
func inverseNTT(f nttElement) ringElement {
|
||||
k := 127
|
||||
for len := 2; len <= 128; len *= 2 {
|
||||
for start := 0; start < 256; start += 2 * len {
|
||||
zeta := zetas[k]
|
||||
k--
|
||||
// Bounds check elimination hint.
|
||||
f, flen := f[start:start+len], f[start+len:start+len+len]
|
||||
for j := 0; j < len; j++ {
|
||||
t := f[j]
|
||||
f[j] = fieldAdd(t, flen[j])
|
||||
flen[j] = fieldMulSub(zeta, flen[j], t)
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range f {
|
||||
f[i] = fieldMul(f[i], 3303) // 3303 = 128⁻¹ mod q
|
||||
}
|
||||
return ringElement(f)
|
||||
}
|
||||
|
||||
// sampleNTT draws a uniformly random nttElement from a stream of uniformly
|
||||
// random bytes generated by the XOF function, according to FIPS 203 (DRAFT),
|
||||
// Algorithm 6 and Definition 4.2.
|
||||
func sampleNTT(rho []byte, ii, jj byte) nttElement {
|
||||
B := sha3.NewShake128()
|
||||
B.Write(rho)
|
||||
B.Write([]byte{ii, jj})
|
||||
|
||||
// SampleNTT essentially draws 12 bits at a time from r, interprets them in
|
||||
// little-endian, and rejects values higher than q, until it drew 256
|
||||
// values. (The rejection rate is approximately 19%.)
|
||||
//
|
||||
// To do this from a bytes stream, it draws three bytes at a time, and
|
||||
// splits them into two uint16 appropriately masked.
|
||||
//
|
||||
// r₀ r₁ r₂
|
||||
// |- - - - - - - -|- - - - - - - -|- - - - - - - -|
|
||||
//
|
||||
// Uint16(r₀ || r₁)
|
||||
// |- - - - - - - - - - - - - - - -|
|
||||
// |- - - - - - - - - - - -|
|
||||
// d₁
|
||||
//
|
||||
// Uint16(r₁ || r₂)
|
||||
// |- - - - - - - - - - - - - - - -|
|
||||
// |- - - - - - - - - - - -|
|
||||
// d₂
|
||||
//
|
||||
// Note that in little-endian, the rightmost bits are the most significant
|
||||
// bits (dropped with a mask) and the leftmost bits are the least
|
||||
// significant bits (dropped with a right shift).
|
||||
|
||||
var a nttElement
|
||||
var j int // index into a
|
||||
var buf [24]byte // buffered reads from B
|
||||
off := len(buf) // index into buf, starts in a "buffer fully consumed" state
|
||||
for {
|
||||
if off >= len(buf) {
|
||||
B.Read(buf[:])
|
||||
off = 0
|
||||
}
|
||||
d1 := byteorder.LeUint16(buf[off:]) & 0b1111_1111_1111
|
||||
d2 := byteorder.LeUint16(buf[off+1:]) >> 4
|
||||
off += 3
|
||||
if d1 < q {
|
||||
a[j] = fieldElement(d1)
|
||||
j++
|
||||
}
|
||||
if j >= len(a) {
|
||||
break
|
||||
}
|
||||
if d2 < q {
|
||||
a[j] = fieldElement(d2)
|
||||
j++
|
||||
}
|
||||
if j >= len(a) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
111
internal/tls/alert.go
Normal file
111
internal/tls/alert.go
Normal 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()
|
||||
}
|
293
internal/tls/auth.go
Normal file
293
internal/tls/auth.go
Normal file
|
@ -0,0 +1,293 @@
|
|||
// 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 {
|
||||
if needFIPS() && !isSupportedSignatureAlgorithm(preferredAlg, defaultSupportedSignatureAlgorithmsFIPS) {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
15
internal/tls/boring.go
Normal file
15
internal/tls/boring.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
// 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.
|
||||
|
||||
//go:build boringcrypto
|
||||
|
||||
package tls
|
||||
|
||||
import "crypto/internal/boring/fipstls"
|
||||
|
||||
// needFIPS returns fipstls.Required(), which is not available without the
|
||||
// boringcrypto build tag.
|
||||
func needFIPS() bool {
|
||||
return fipstls.Required()
|
||||
}
|
95
internal/tls/cache.go
Normal file
95
internal/tls/cache.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE 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
|
||||
}
|
713
internal/tls/cipher_suites.go
Normal file
713
internal/tls/cipher_suites.go
Normal file
|
@ -0,0 +1,713 @@
|
|||
// 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"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// 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.0–1.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.0–1.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.0–1.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 http://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 (
|
||||
hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ
|
||||
hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL
|
||||
// Keep in sync with crypto/aes/cipher_s390x.go.
|
||||
hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCBC && cpu.S390X.HasAESCTR &&
|
||||
(cpu.S390X.HasGHASH || cpu.S390X.HasAESGCM)
|
||||
|
||||
hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 ||
|
||||
runtime.GOARCH == "arm64" && hasGCMAsmARM64 ||
|
||||
runtime.GOARCH == "s390x" && hasGCMAsmS390X
|
||||
)
|
||||
|
||||
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()
|
||||
aead, err = cipher.NewGCM(aes)
|
||||
//}
|
||||
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)
|
||||
}
|
||||
aead, err := cipher.NewGCM(aes)
|
||||
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 cipherSuites {
|
||||
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
|
||||
)
|
1643
internal/tls/common.go
Normal file
1643
internal/tls/common.go
Normal file
File diff suppressed because it is too large
Load diff
120
internal/tls/common_string.go
Normal file
120
internal/tls/common_string.go
Normal 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[x25519Kyber768Draft00-25497]
|
||||
}
|
||||
|
||||
const (
|
||||
_CurveID_name_0 = "CurveP256CurveP384CurveP521"
|
||||
_CurveID_name_1 = "X25519"
|
||||
_CurveID_name_2 = "X25519Kyber768Draft00"
|
||||
)
|
||||
|
||||
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 == 25497:
|
||||
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]]
|
||||
}
|
1676
internal/tls/conn.go
Normal file
1676
internal/tls/conn.go
Normal file
File diff suppressed because it is too large
Load diff
115
internal/tls/defaults.go
Normal file
115
internal/tls/defaults.go
Normal file
|
@ -0,0 +1,115 @@
|
|||
// 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 (
|
||||
_ "unsafe"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func defaultCurvePreferences() []CurveID {
|
||||
return []CurveID{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,
|
||||
}
|
||||
|
||||
func defaultCipherSuites() []uint16 {
|
||||
suites := slices.Clone(cipherSuitesPreferenceOrder)
|
||||
return slices.DeleteFunc(suites, func(c uint16) bool {
|
||||
return disabledCipherSuites[c]
|
||||
})
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
|
||||
var defaultSupportedVersionsFIPS = []uint16{
|
||||
VersionTLS12,
|
||||
}
|
||||
|
||||
// defaultCurvePreferencesFIPS are the FIPS-allowed curves,
|
||||
// in preference order (most preferable first).
|
||||
var defaultCurvePreferencesFIPS = []CurveID{CurveP256, CurveP384, CurveP521}
|
||||
|
||||
// defaultSupportedSignatureAlgorithmsFIPS currently are a subset of
|
||||
// defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1.
|
||||
var defaultSupportedSignatureAlgorithmsFIPS = []SignatureScheme{
|
||||
PSSWithSHA256,
|
||||
PSSWithSHA384,
|
||||
PSSWithSHA512,
|
||||
PKCS1WithSHA256,
|
||||
ECDSAWithP256AndSHA256,
|
||||
PKCS1WithSHA384,
|
||||
ECDSAWithP384AndSHA384,
|
||||
PKCS1WithSHA512,
|
||||
ECDSAWithP521AndSHA512,
|
||||
}
|
||||
|
||||
// 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,
|
||||
TLS_RSA_WITH_AES_128_GCM_SHA256,
|
||||
TLS_RSA_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,
|
||||
}
|
285
internal/tls/ech.go
Normal file
285
internal/tls/ech.go
Normal file
|
@ -0,0 +1,285 @@
|
|||
// 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 (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/sagernet/sing-shadowtls/internal/hpke"
|
||||
"github.com/sagernet/sing/common"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
// 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)
|
||||
// Skip the length prefix
|
||||
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 {
|
||||
var ec echConfig
|
||||
ec.raw = []byte(s)
|
||||
if !s.ReadUint16(&ec.Version) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if !s.ReadUint16(&ec.Length) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if len(ec.raw) < int(ec.Length)+4 {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
ec.raw = ec.raw[:ec.Length+4]
|
||||
if ec.Version != extensionEncryptedClientHello {
|
||||
s.Skip(int(ec.Length))
|
||||
continue
|
||||
}
|
||||
if !s.ReadUint8(&ec.ConfigID) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if !s.ReadUint16(&ec.KemID) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if !s.ReadUint16LengthPrefixed((*cryptobyte.String)(&ec.PublicKey)) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
var cipherSuites cryptobyte.String
|
||||
if !s.ReadUint16LengthPrefixed(&cipherSuites) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
for !cipherSuites.Empty() {
|
||||
var c echCipher
|
||||
if !cipherSuites.ReadUint16(&c.KDFID) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if !cipherSuites.ReadUint16(&c.AEADID) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
ec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c)
|
||||
}
|
||||
if !s.ReadUint8(&ec.MaxNameLength) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
var publicName cryptobyte.String
|
||||
if !s.ReadUint8LengthPrefixed(&publicName) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
ec.PublicName = publicName
|
||||
var extensions cryptobyte.String
|
||||
if !s.ReadUint16LengthPrefixed(&extensions) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
for !extensions.Empty() {
|
||||
var e echExtension
|
||||
if !extensions.ReadUint16(&e.Type) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
if !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) {
|
||||
return nil, errMalformedECHConfig
|
||||
}
|
||||
ec.Extensions = append(ec.Extensions, e)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, error) {
|
||||
h, err := inner.marshalMsg(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
h = h[4:] // strip four byte prefix
|
||||
|
||||
var paddingLen int
|
||||
if inner.serverName != "" {
|
||||
paddingLen = common.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 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 *echContext, 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"
|
||||
}
|
171
internal/tls/generate_cert.go
Normal file
171
internal/tls/generate_cert.go
Normal file
|
@ -0,0 +1,171 @@
|
|||
// 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.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// Generate a self-signed X.509 certificate for a TLS server. Outputs to
|
||||
// 'cert.pem' and 'key.pem' and will overwrite existing files.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"flag"
|
||||
"log"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
host = flag.String("host", "", "Comma-separated hostnames and IPs to generate a certificate for")
|
||||
validFrom = flag.String("start-date", "", "Creation date formatted as Jan 1 15:04:05 2011")
|
||||
validFor = flag.Duration("duration", 365*24*time.Hour, "Duration that certificate is valid for")
|
||||
isCA = flag.Bool("ca", false, "whether this cert should be its own Certificate Authority")
|
||||
rsaBits = flag.Int("rsa-bits", 2048, "Size of RSA key to generate. Ignored if --ecdsa-curve is set")
|
||||
ecdsaCurve = flag.String("ecdsa-curve", "", "ECDSA curve to use to generate a key. Valid values are P224, P256 (recommended), P384, P521")
|
||||
ed25519Key = flag.Bool("ed25519", false, "Generate an Ed25519 key")
|
||||
)
|
||||
|
||||
func publicKey(priv any) any {
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case *ecdsa.PrivateKey:
|
||||
return &k.PublicKey
|
||||
case ed25519.PrivateKey:
|
||||
return k.Public().(ed25519.PublicKey)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
if len(*host) == 0 {
|
||||
log.Fatalf("Missing required --host parameter")
|
||||
}
|
||||
|
||||
var priv any
|
||||
var err error
|
||||
switch *ecdsaCurve {
|
||||
case "":
|
||||
if *ed25519Key {
|
||||
_, priv, err = ed25519.GenerateKey(rand.Reader)
|
||||
} else {
|
||||
priv, err = rsa.GenerateKey(rand.Reader, *rsaBits)
|
||||
}
|
||||
case "P224":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
|
||||
case "P256":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
case "P384":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
|
||||
case "P521":
|
||||
priv, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||
default:
|
||||
log.Fatalf("Unrecognized elliptic curve: %q", *ecdsaCurve)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate private key: %v", err)
|
||||
}
|
||||
|
||||
// ECDSA, ED25519 and RSA subject keys should have the DigitalSignature
|
||||
// KeyUsage bits set in the x509.Certificate template
|
||||
keyUsage := x509.KeyUsageDigitalSignature
|
||||
// Only RSA subject keys should have the KeyEncipherment KeyUsage bits set. In
|
||||
// the context of TLS this KeyUsage is particular to RSA key exchange and
|
||||
// authentication.
|
||||
if _, isRSA := priv.(*rsa.PrivateKey); isRSA {
|
||||
keyUsage |= x509.KeyUsageKeyEncipherment
|
||||
}
|
||||
|
||||
var notBefore time.Time
|
||||
if len(*validFrom) == 0 {
|
||||
notBefore = time.Now()
|
||||
} else {
|
||||
notBefore, err = time.Parse("Jan 2 15:04:05 2006", *validFrom)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse creation date: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
notAfter := notBefore.Add(*validFor)
|
||||
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to generate serial number: %v", err)
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Acme Co"},
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
|
||||
KeyUsage: keyUsage,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
hosts := strings.Split(*host, ",")
|
||||
for _, h := range hosts {
|
||||
if ip := net.ParseIP(h); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, h)
|
||||
}
|
||||
}
|
||||
|
||||
if *isCA {
|
||||
template.IsCA = true
|
||||
template.KeyUsage |= x509.KeyUsageCertSign
|
||||
}
|
||||
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, publicKey(priv), priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to create certificate: %v", err)
|
||||
}
|
||||
|
||||
certOut, err := os.Create("cert.pem")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open cert.pem for writing: %v", err)
|
||||
}
|
||||
if err := pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to cert.pem: %v", err)
|
||||
}
|
||||
if err := certOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing cert.pem: %v", err)
|
||||
}
|
||||
log.Print("wrote cert.pem\n")
|
||||
|
||||
keyOut, err := os.OpenFile("key.pem", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to open key.pem for writing: %v", err)
|
||||
}
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to marshal private key: %v", err)
|
||||
}
|
||||
if err := pem.Encode(keyOut, &pem.Block{Type: "PRIVATE KEY", Bytes: privBytes}); err != nil {
|
||||
log.Fatalf("Failed to write data to key.pem: %v", err)
|
||||
}
|
||||
if err := keyOut.Close(); err != nil {
|
||||
log.Fatalf("Error closing key.pem: %v", err)
|
||||
}
|
||||
log.Print("wrote key.pem\n")
|
||||
}
|
1265
internal/tls/handshake_client.go
Normal file
1265
internal/tls/handshake_client.go
Normal file
File diff suppressed because it is too large
Load diff
915
internal/tls/handshake_client_tls13.go
Normal file
915
internal/tls/handshake_client_tls13.go
Normal file
|
@ -0,0 +1,915 @@
|
|||
// 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 (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"hash"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowtls/internal/mlkem768"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type clientHandshakeStateTLS13 struct {
|
||||
c *Conn
|
||||
ctx context.Context
|
||||
serverHello *serverHelloMsg
|
||||
hello *clientHelloMsg
|
||||
keyShareKeys *keySharePrivateKeys
|
||||
|
||||
session *SessionState
|
||||
earlySecret []byte
|
||||
binderKey []byte
|
||||
|
||||
certReq *certificateRequestMsgTLS13
|
||||
usingPSK bool
|
||||
sentDummyCCS bool
|
||||
suite *cipherSuiteTLS13
|
||||
transcript hash.Hash
|
||||
masterSecret []byte
|
||||
trafficSecret []byte // client_application_traffic_secret_0
|
||||
|
||||
echContext *echContext
|
||||
}
|
||||
|
||||
// handshake requires hs.c, hs.hello, hs.serverHello, hs.keyShareKeys, and,
|
||||
// optionally, hs.session, hs.earlySecret and hs.binderKey to be set.
|
||||
func (hs *clientHandshakeStateTLS13) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
if needFIPS() {
|
||||
return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode")
|
||||
}
|
||||
|
||||
// The server must not select TLS 1.3 in a renegotiation. See RFC 8446,
|
||||
// sections 4.1.2 and 4.1.3.
|
||||
if c.handshakes > 0 {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return errors.New("tls: server selected TLS 1.3 in a renegotiation")
|
||||
}
|
||||
|
||||
// Consistency check on the presence of a keyShare and its parameters.
|
||||
if hs.keyShareKeys == nil || hs.keyShareKeys.ecdhe == nil || len(hs.hello.keyShares) == 0 {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hs.transcript = hs.suite.hash.New()
|
||||
|
||||
if err := transcriptMsg(hs.hello, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hs.echContext != nil {
|
||||
hs.echContext.innerTranscript = hs.suite.hash.New()
|
||||
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.processHelloRetryRequest(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var echRetryConfigList []byte
|
||||
if hs.echContext != nil {
|
||||
confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash)
|
||||
confTranscript.Write(hs.serverHello.original[:30])
|
||||
confTranscript.Write(make([]byte, 8))
|
||||
confTranscript.Write(hs.serverHello.original[38:])
|
||||
acceptConfirmation := hs.suite.expandLabel(
|
||||
hs.suite.extract(hs.echContext.innerHello.random, nil),
|
||||
"ech accept confirmation",
|
||||
confTranscript.Sum(nil),
|
||||
8,
|
||||
)
|
||||
if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.random[len(hs.serverHello.random)-8:]) == 1 {
|
||||
hs.hello = hs.echContext.innerHello
|
||||
c.serverName = c.config.ServerName
|
||||
hs.transcript = hs.echContext.innerTranscript
|
||||
c.echAccepted = true
|
||||
|
||||
if hs.serverHello.encryptedClientHello != nil {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: unexpected encrypted_client_hello extension in server hello despite ECH being accepted")
|
||||
}
|
||||
|
||||
if hs.hello.serverName == "" && hs.serverHello.serverNameAck {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: unexpected server_name extension in server hello")
|
||||
}
|
||||
} else {
|
||||
hs.echContext.echRejected = true
|
||||
// If the server sent us retry configs, we'll return these to
|
||||
// the user so they can update their Config.
|
||||
echRetryConfigList = hs.serverHello.encryptedClientHello
|
||||
}
|
||||
}
|
||||
|
||||
if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.buffering = true
|
||||
if err := hs.processServerHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendDummyChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishHandshakeKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerParameters(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readServerFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientCertificate(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendClientFinished(); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hs.echContext != nil && hs.echContext.echRejected {
|
||||
c.sendAlert(alertECHRequired)
|
||||
return &ECHRejectionError{echRetryConfigList}
|
||||
}
|
||||
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkServerHelloOrHRR does validity checks that apply to both ServerHello and
|
||||
// HelloRetryRequest messages. It sets hs.suite.
|
||||
func (hs *clientHandshakeStateTLS13) checkServerHelloOrHRR() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.serverHello.supportedVersion == 0 {
|
||||
c.sendAlert(alertMissingExtension)
|
||||
return errors.New("tls: server selected TLS 1.3 using the legacy version field")
|
||||
}
|
||||
|
||||
if hs.serverHello.supportedVersion != VersionTLS13 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid version after a HelloRetryRequest")
|
||||
}
|
||||
|
||||
if hs.serverHello.vers != VersionTLS12 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an incorrect legacy version")
|
||||
}
|
||||
|
||||
if hs.serverHello.ocspStapling ||
|
||||
hs.serverHello.ticketSupported ||
|
||||
hs.serverHello.extendedMasterSecret ||
|
||||
hs.serverHello.secureRenegotiationSupported ||
|
||||
len(hs.serverHello.secureRenegotiation) != 0 ||
|
||||
len(hs.serverHello.alpnProtocol) != 0 ||
|
||||
len(hs.serverHello.scts) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a ServerHello extension forbidden in TLS 1.3")
|
||||
}
|
||||
|
||||
if !bytes.Equal(hs.hello.sessionId, hs.serverHello.sessionId) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not echo the legacy session ID")
|
||||
}
|
||||
|
||||
if hs.serverHello.compressionMethod != compressionNone {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported compression format")
|
||||
}
|
||||
|
||||
selectedSuite := mutualCipherSuiteTLS13(hs.hello.cipherSuites, hs.serverHello.cipherSuite)
|
||||
if hs.suite != nil && selectedSuite != hs.suite {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server changed cipher suite after a HelloRetryRequest")
|
||||
}
|
||||
if selectedSuite == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server chose an unconfigured cipher suite")
|
||||
}
|
||||
hs.suite = selectedSuite
|
||||
c.cipherSuite = hs.suite.id
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// sendDummyChangeCipherSpec sends a ChangeCipherSpec record for compatibility
|
||||
// with middleboxes that didn't implement TLS correctly. See RFC 8446, Appendix D.4.
|
||||
func (hs *clientHandshakeStateTLS13) sendDummyChangeCipherSpec() error {
|
||||
if hs.c.quic != nil {
|
||||
return nil
|
||||
}
|
||||
if hs.sentDummyCCS {
|
||||
return nil
|
||||
}
|
||||
hs.sentDummyCCS = true
|
||||
|
||||
return hs.c.writeChangeCipherRecord()
|
||||
}
|
||||
|
||||
// processHelloRetryRequest handles the HRR in hs.serverHello, modifies and
|
||||
// resends hs.hello, and reads the new ServerHello into hs.serverHello.
|
||||
func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error {
|
||||
c := hs.c
|
||||
|
||||
// The first ClientHello gets double-hashed into the transcript upon a
|
||||
// HelloRetryRequest. (The idea is that the server might offload transcript
|
||||
// storage to the client in the cookie.) See RFC 8446, Section 4.4.1.
|
||||
chHash := hs.transcript.Sum(nil)
|
||||
hs.transcript.Reset()
|
||||
hs.transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
hs.transcript.Write(chHash)
|
||||
if err := transcriptMsg(hs.serverHello, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var isInnerHello bool
|
||||
hello := hs.hello
|
||||
if hs.echContext != nil {
|
||||
chHash = hs.echContext.innerTranscript.Sum(nil)
|
||||
hs.echContext.innerTranscript.Reset()
|
||||
hs.echContext.innerTranscript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
hs.echContext.innerTranscript.Write(chHash)
|
||||
|
||||
if hs.serverHello.encryptedClientHello != nil {
|
||||
if len(hs.serverHello.encryptedClientHello) != 8 {
|
||||
hs.c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: malformed encrypted client hello extension")
|
||||
}
|
||||
|
||||
confTranscript := cloneHash(hs.echContext.innerTranscript, hs.suite.hash)
|
||||
hrrHello := make([]byte, len(hs.serverHello.original))
|
||||
copy(hrrHello, hs.serverHello.original)
|
||||
hrrHello = bytes.Replace(hrrHello, hs.serverHello.encryptedClientHello, make([]byte, 8), 1)
|
||||
confTranscript.Write(hrrHello)
|
||||
acceptConfirmation := hs.suite.expandLabel(
|
||||
hs.suite.extract(hs.echContext.innerHello.random, nil),
|
||||
"hrr ech accept confirmation",
|
||||
confTranscript.Sum(nil),
|
||||
8,
|
||||
)
|
||||
if subtle.ConstantTimeCompare(acceptConfirmation, hs.serverHello.encryptedClientHello) == 1 {
|
||||
hello = hs.echContext.innerHello
|
||||
c.serverName = c.config.ServerName
|
||||
isInnerHello = true
|
||||
c.echAccepted = true
|
||||
}
|
||||
}
|
||||
|
||||
if err := transcriptMsg(hs.serverHello, hs.echContext.innerTranscript); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if hs.serverHello.encryptedClientHello != nil {
|
||||
// Unsolicited ECH extension should be rejected
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: unexpected ECH extension in serverHello")
|
||||
}
|
||||
|
||||
// The only HelloRetryRequest extensions we support are key_share and
|
||||
// cookie, and clients must abort the handshake if the HRR would not result
|
||||
// in any change in the ClientHello.
|
||||
if hs.serverHello.selectedGroup == 0 && hs.serverHello.cookie == nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest message")
|
||||
}
|
||||
|
||||
if hs.serverHello.cookie != nil {
|
||||
hello.cookie = hs.serverHello.cookie
|
||||
}
|
||||
|
||||
if hs.serverHello.serverShare.group != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received malformed key_share extension")
|
||||
}
|
||||
|
||||
// If the server sent a key_share extension selecting a group, ensure it's
|
||||
// a group we advertised but did not send a key share for, and send a key
|
||||
// share for it this time.
|
||||
if curveID := hs.serverHello.selectedGroup; curveID != 0 {
|
||||
if !slices.Contains(hello.supportedCurves, curveID) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
if slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
||||
return ks.group == curveID
|
||||
}) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server sent an unnecessary HelloRetryRequest key_share")
|
||||
}
|
||||
// Note: we don't support selecting X25519Kyber768Draft00 in a HRR,
|
||||
// because we currently only support it at all when CurvePreferences is
|
||||
// empty, which will cause us to also send a key share for it.
|
||||
//
|
||||
// This will have to change once we support selecting hybrid KEMs
|
||||
// without sending key shares for them.
|
||||
if _, ok := curveForCurveID(curveID); !ok {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: CurvePreferences includes unsupported curve")
|
||||
}
|
||||
key, err := generateECDHEKey(c.config.rand(), curveID)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
hs.keyShareKeys = &keySharePrivateKeys{curveID: curveID, ecdhe: key}
|
||||
hello.keyShares = []keyShare{{group: curveID, data: key.PublicKey().Bytes()}}
|
||||
}
|
||||
|
||||
if len(hello.pskIdentities) > 0 {
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash == hs.suite.hash {
|
||||
// Update binders and obfuscated_ticket_age.
|
||||
ticketAge := c.config.time().Sub(time.Unix(int64(hs.session.createdAt), 0))
|
||||
hello.pskIdentities[0].obfuscatedTicketAge = uint32(ticketAge/time.Millisecond) + hs.session.ageAdd
|
||||
|
||||
transcript := hs.suite.hash.New()
|
||||
transcript.Write([]byte{typeMessageHash, 0, 0, uint8(len(chHash))})
|
||||
transcript.Write(chHash)
|
||||
if err := transcriptMsg(hs.serverHello, transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := computeAndUpdatePSK(hello, hs.binderKey, transcript, hs.suite.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Server selected a cipher suite incompatible with the PSK.
|
||||
hello.pskIdentities = nil
|
||||
hello.pskBinders = nil
|
||||
}
|
||||
}
|
||||
|
||||
if hello.earlyData {
|
||||
hello.earlyData = false
|
||||
c.quicRejectedEarlyData()
|
||||
}
|
||||
|
||||
if isInnerHello {
|
||||
// Any extensions which have changed in hello, but are mirrored in the
|
||||
// outer hello and compressed, need to be copied to the outer hello, so
|
||||
// they can be properly decompressed by the server. For now, the only
|
||||
// extension which may have changed is keyShares.
|
||||
hs.hello.keyShares = hello.keyShares
|
||||
hs.echContext.innerHello = hello
|
||||
if err := transcriptMsg(hs.echContext.innerHello, hs.echContext.innerTranscript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := computeAndUpdateOuterECHExtension(hs.hello, hs.echContext.innerHello, hs.echContext, false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
hs.hello = hello
|
||||
}
|
||||
|
||||
if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
hs.serverHello = serverHello
|
||||
|
||||
if err := hs.checkServerHelloOrHRR(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.didHRR = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) processServerHello() error {
|
||||
c := hs.c
|
||||
|
||||
if bytes.Equal(hs.serverHello.random, helloRetryRequestRandom) {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: server sent two HelloRetryRequest messages")
|
||||
}
|
||||
|
||||
if len(hs.serverHello.cookie) != 0 {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent a cookie in a normal ServerHello")
|
||||
}
|
||||
|
||||
if hs.serverHello.selectedGroup != 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: malformed key_share extension")
|
||||
}
|
||||
|
||||
if hs.serverHello.serverShare.group == 0 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server did not send a key share")
|
||||
}
|
||||
if !slices.ContainsFunc(hs.hello.keyShares, func(ks keyShare) bool {
|
||||
return ks.group == hs.serverHello.serverShare.group
|
||||
}) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected unsupported group")
|
||||
}
|
||||
|
||||
if !hs.serverHello.selectedIdentityPresent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if int(hs.serverHello.selectedIdentity) >= len(hs.hello.pskIdentities) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK")
|
||||
}
|
||||
|
||||
if len(hs.hello.pskIdentities) != 1 || hs.session == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
pskSuite := cipherSuiteTLS13ByID(hs.session.cipherSuite)
|
||||
if pskSuite == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if pskSuite.hash != hs.suite.hash {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: server selected an invalid PSK and cipher suite pair")
|
||||
}
|
||||
|
||||
hs.usingPSK = true
|
||||
c.didResume = true
|
||||
c.peerCertificates = hs.session.peerCertificates
|
||||
c.activeCertHandles = hs.session.activeCertHandles
|
||||
c.verifiedChains = hs.session.verifiedChains
|
||||
c.ocspResponse = hs.session.ocspResponse
|
||||
c.scts = hs.session.scts
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error {
|
||||
c := hs.c
|
||||
|
||||
ecdhePeerData := hs.serverHello.serverShare.data
|
||||
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
||||
if len(ecdhePeerData) != x25519PublicKeySize+mlkem768.CiphertextSize {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
ecdhePeerData = hs.serverHello.serverShare.data[:x25519PublicKeySize]
|
||||
}
|
||||
peerKey, err := hs.keyShareKeys.ecdhe.Curve().NewPublicKey(ecdhePeerData)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
sharedKey, err := hs.keyShareKeys.ecdhe.ECDH(peerKey)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid server key share")
|
||||
}
|
||||
if hs.serverHello.serverShare.group == x25519Kyber768Draft00 {
|
||||
if hs.keyShareKeys.kyber == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
ciphertext := hs.serverHello.serverShare.data[x25519PublicKeySize:]
|
||||
kyberShared, err := kyberDecapsulate(hs.keyShareKeys.kyber, ciphertext)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid Kyber server key share")
|
||||
}
|
||||
sharedKey = append(sharedKey, kyberShared...)
|
||||
}
|
||||
c.curveID = hs.serverHello.serverShare.group
|
||||
|
||||
earlySecret := hs.earlySecret
|
||||
if !hs.usingPSK {
|
||||
earlySecret = hs.suite.extract(nil, nil)
|
||||
}
|
||||
|
||||
handshakeSecret := hs.suite.extract(sharedKey,
|
||||
hs.suite.deriveSecret(earlySecret, "derived", nil))
|
||||
|
||||
clientSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
clientHandshakeTrafficLabel, hs.transcript)
|
||||
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret)
|
||||
serverSecret := hs.suite.deriveSecret(handshakeSecret,
|
||||
serverHandshakeTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret)
|
||||
|
||||
if c.quic != nil {
|
||||
if c.hand.Len() != 0 {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
}
|
||||
c.quicSetWriteSecret(QUICEncryptionLevelHandshake, hs.suite.id, clientSecret)
|
||||
c.quicSetReadSecret(QUICEncryptionLevelHandshake, hs.suite.id, serverSecret)
|
||||
}
|
||||
|
||||
err = c.config.writeKeyLog(keyLogLabelClientHandshake, hs.hello.random, clientSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerHandshake, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
hs.masterSecret = hs.suite.extract(nil,
|
||||
hs.suite.deriveSecret(handshakeSecret, "derived", nil))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerParameters() error {
|
||||
c := hs.c
|
||||
|
||||
msg, err := c.readHandshake(hs.transcript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
encryptedExtensions, ok := msg.(*encryptedExtensionsMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(encryptedExtensions, msg)
|
||||
}
|
||||
|
||||
if err := checkALPN(hs.hello.alpnProtocols, encryptedExtensions.alpnProtocol, c.quic != nil); err != nil {
|
||||
// RFC 8446 specifies that no_application_protocol is sent by servers, but
|
||||
// does not specify how clients handle the selection of an incompatible protocol.
|
||||
// RFC 9001 Section 8.1 specifies that QUIC clients send no_application_protocol
|
||||
// in this case. Always sending no_application_protocol seems reasonable.
|
||||
c.sendAlert(alertNoApplicationProtocol)
|
||||
return err
|
||||
}
|
||||
c.clientProtocol = encryptedExtensions.alpnProtocol
|
||||
|
||||
if c.quic != nil {
|
||||
if encryptedExtensions.quicTransportParameters == nil {
|
||||
// RFC 9001 Section 8.2.
|
||||
c.sendAlert(alertMissingExtension)
|
||||
return errors.New("tls: server did not send a quic_transport_parameters extension")
|
||||
}
|
||||
c.quicSetTransportParameters(encryptedExtensions.quicTransportParameters)
|
||||
} else {
|
||||
if encryptedExtensions.quicTransportParameters != nil {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent an unexpected quic_transport_parameters extension")
|
||||
}
|
||||
}
|
||||
|
||||
if !hs.hello.earlyData && encryptedExtensions.earlyData {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent an unexpected early_data extension")
|
||||
}
|
||||
if hs.hello.earlyData && !encryptedExtensions.earlyData {
|
||||
c.quicRejectedEarlyData()
|
||||
}
|
||||
if encryptedExtensions.earlyData {
|
||||
if hs.session.cipherSuite != c.cipherSuite {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: server accepted 0-RTT with the wrong cipher suite")
|
||||
}
|
||||
if hs.session.alpnProtocol != c.clientProtocol {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: server accepted 0-RTT with the wrong ALPN")
|
||||
}
|
||||
}
|
||||
if hs.echContext != nil && !hs.echContext.echRejected && encryptedExtensions.echRetryConfigs != nil {
|
||||
c.sendAlert(alertUnsupportedExtension)
|
||||
return errors.New("tls: server sent ECH retry configs after accepting ECH")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
// Either a PSK or a certificate is always used, but not both.
|
||||
// See RFC 8446, Section 4.1.1.
|
||||
if hs.usingPSK {
|
||||
// Make sure the connection is still being verified whether or not this
|
||||
// is a resumption. Resumptions currently don't reverify certificates so
|
||||
// they don't call verifyServerCertificate. See Issue 31641.
|
||||
if c.config.VerifyConnection != nil {
|
||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
msg, err := c.readHandshake(hs.transcript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certReq, ok := msg.(*certificateRequestMsgTLS13)
|
||||
if ok {
|
||||
hs.certReq = certReq
|
||||
|
||||
msg, err = c.readHandshake(hs.transcript)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
certMsg, ok := msg.(*certificateMsgTLS13)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
if len(certMsg.certificate.Certificate) == 0 {
|
||||
c.sendAlert(alertDecodeError)
|
||||
return errors.New("tls: received empty certificates message")
|
||||
}
|
||||
|
||||
c.scts = certMsg.certificate.SignedCertificateTimestamps
|
||||
c.ocspResponse = certMsg.certificate.OCSPStaple
|
||||
|
||||
if err := c.verifyServerCertificate(certMsg.certificate.Certificate); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// certificateVerifyMsg is included in the transcript, but not until
|
||||
// after we verify the handshake signature, since the state before
|
||||
// this message was sent is used.
|
||||
msg, err = c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.4.3.
|
||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, supportedSignatureAlgorithms()) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
||||
}
|
||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
if sigType == signaturePKCS1v15 || sigHash == crypto.SHA1 {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: certificate used with invalid signature algorithm")
|
||||
}
|
||||
signed := signedMessage(sigHash, serverSignatureContext, hs.transcript)
|
||||
if err := verifyHandshakeSignature(sigType, c.peerCertificates[0].PublicKey,
|
||||
sigHash, signed, certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid signature by the server certificate: " + err.Error())
|
||||
}
|
||||
|
||||
if err := transcriptMsg(certVerify, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) readServerFinished() error {
|
||||
c := hs.c
|
||||
|
||||
// finishedMsg is included in the transcript, but not until after we
|
||||
// check the client version, since the state before this message was
|
||||
// sent is used during verification.
|
||||
msg, err := c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(finished, msg)
|
||||
}
|
||||
|
||||
expectedMAC := hs.suite.finishedHash(c.in.trafficSecret, hs.transcript)
|
||||
if !hmac.Equal(expectedMAC, finished.verifyData) {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid server finished hash")
|
||||
}
|
||||
|
||||
if err := transcriptMsg(finished, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Derive secrets that take context through the server Finished.
|
||||
|
||||
hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
clientApplicationTrafficLabel, hs.transcript)
|
||||
serverSecret := hs.suite.deriveSecret(hs.masterSecret,
|
||||
serverApplicationTrafficLabel, hs.transcript)
|
||||
c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret)
|
||||
|
||||
err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
err = c.config.writeKeyLog(keyLogLabelServerTraffic, hs.hello.random, serverSecret)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
c.ekm = hs.suite.exportKeyingMaterial(hs.masterSecret, hs.transcript)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientCertificate() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.certReq == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if hs.echContext != nil && hs.echContext.echRejected {
|
||||
if _, err := hs.c.writeHandshakeRecord(&certificateMsgTLS13{}, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
cert, err := c.getClientCertificate(&CertificateRequestInfo{
|
||||
AcceptableCAs: hs.certReq.certificateAuthorities,
|
||||
SignatureSchemes: hs.certReq.supportedSignatureAlgorithms,
|
||||
Version: c.vers,
|
||||
ctx: hs.ctx,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsgTLS13)
|
||||
|
||||
certMsg.certificate = *cert
|
||||
certMsg.scts = hs.certReq.scts && len(cert.SignedCertificateTimestamps) > 0
|
||||
certMsg.ocspStapling = hs.certReq.ocspStapling && len(cert.OCSPStaple) > 0
|
||||
|
||||
if _, err := hs.c.writeHandshakeRecord(certMsg, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we sent an empty certificate message, skip the CertificateVerify.
|
||||
if len(cert.Certificate) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
certVerifyMsg := new(certificateVerifyMsg)
|
||||
certVerifyMsg.hasSignatureAlgorithm = true
|
||||
|
||||
certVerifyMsg.signatureAlgorithm, err = selectSignatureScheme(c.vers, cert, hs.certReq.supportedSignatureAlgorithms)
|
||||
if err != nil {
|
||||
// getClientCertificate returned a certificate incompatible with the
|
||||
// CertificateRequestInfo supported signature algorithms.
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
|
||||
sigType, sigHash, err := typeAndHashFromSignatureScheme(certVerifyMsg.signatureAlgorithm)
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
signed := signedMessage(sigHash, clientSignatureContext, hs.transcript)
|
||||
signOpts := crypto.SignerOpts(sigHash)
|
||||
if sigType == signatureRSAPSS {
|
||||
signOpts = &rsa.PSSOptions{SaltLength: rsa.PSSSaltLengthEqualsHash, Hash: sigHash}
|
||||
}
|
||||
sig, err := cert.PrivateKey.(crypto.Signer).Sign(c.config.rand(), signed, signOpts)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return errors.New("tls: failed to sign handshake: " + err.Error())
|
||||
}
|
||||
certVerifyMsg.signature = sig
|
||||
|
||||
if _, err := hs.c.writeHandshakeRecord(certVerifyMsg, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *clientHandshakeStateTLS13) sendClientFinished() error {
|
||||
c := hs.c
|
||||
|
||||
finished := &finishedMsg{
|
||||
verifyData: hs.suite.finishedHash(c.out.trafficSecret, hs.transcript),
|
||||
}
|
||||
|
||||
if _, err := hs.c.writeHandshakeRecord(finished, hs.transcript); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, hs.trafficSecret)
|
||||
|
||||
if !c.config.SessionTicketsDisabled && c.config.ClientSessionCache != nil {
|
||||
c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret,
|
||||
resumptionLabel, hs.transcript)
|
||||
}
|
||||
|
||||
if c.quic != nil {
|
||||
if c.hand.Len() != 0 {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
}
|
||||
c.quicSetWriteSecret(QUICEncryptionLevelApplication, hs.suite.id, hs.trafficSecret)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error {
|
||||
if !c.isClient {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return errors.New("tls: received new session ticket from a client")
|
||||
}
|
||||
|
||||
if c.config.SessionTicketsDisabled || c.config.ClientSessionCache == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// See RFC 8446, Section 4.6.1.
|
||||
if msg.lifetime == 0 {
|
||||
return nil
|
||||
}
|
||||
lifetime := time.Duration(msg.lifetime) * time.Second
|
||||
if lifetime > maxSessionTicketLifetime {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: received a session ticket with invalid lifetime")
|
||||
}
|
||||
|
||||
// RFC 9001, Section 4.6.1
|
||||
if c.quic != nil && msg.maxEarlyData != 0 && msg.maxEarlyData != 0xffffffff {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: invalid early data for QUIC connection")
|
||||
}
|
||||
|
||||
cipherSuite := cipherSuiteTLS13ByID(c.cipherSuite)
|
||||
if cipherSuite == nil || c.resumptionSecret == nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
|
||||
psk := cipherSuite.expandLabel(c.resumptionSecret, "resumption",
|
||||
msg.nonce, cipherSuite.hash.Size())
|
||||
|
||||
session := c.sessionState()
|
||||
session.secret = psk
|
||||
session.useBy = uint64(c.config.time().Add(lifetime).Unix())
|
||||
session.ageAdd = msg.ageAdd
|
||||
session.EarlyData = c.quic != nil && msg.maxEarlyData == 0xffffffff // RFC 9001, Section 4.6.1
|
||||
session.ticket = msg.label
|
||||
if c.quic != nil && c.quic.enableSessionEvents {
|
||||
c.quicStoreSession(session)
|
||||
return nil
|
||||
}
|
||||
cs := &ClientSessionState{session: session}
|
||||
if cacheKey := c.clientSessionCacheKey(); cacheKey != "" {
|
||||
c.config.ClientSessionCache.Put(cacheKey, cs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
1939
internal/tls/handshake_messages.go
Normal file
1939
internal/tls/handshake_messages.go
Normal file
File diff suppressed because it is too large
Load diff
956
internal/tls/handshake_server.go
Normal file
956
internal/tls/handshake_server.go
Normal file
|
@ -0,0 +1,956 @@
|
|||
// 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 (
|
||||
"context"
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/ed25519"
|
||||
"crypto/rsa"
|
||||
"crypto/subtle"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-shadowtls/internal/byteorder"
|
||||
)
|
||||
|
||||
// serverHandshakeState contains details of a server handshake in progress.
|
||||
// It's discarded once the handshake has completed.
|
||||
type serverHandshakeState struct {
|
||||
c *Conn
|
||||
ctx context.Context
|
||||
clientHello *clientHelloMsg
|
||||
hello *serverHelloMsg
|
||||
suite *cipherSuite
|
||||
ecdheOk bool
|
||||
ecSignOk bool
|
||||
rsaDecryptOk bool
|
||||
rsaSignOk bool
|
||||
sessionState *SessionState
|
||||
finishedHash finishedHash
|
||||
masterSecret []byte
|
||||
cert *Certificate
|
||||
}
|
||||
|
||||
// serverHandshake performs a TLS handshake as a server.
|
||||
func (c *Conn) serverHandshake(ctx context.Context) error {
|
||||
clientHello, err := c.readClientHello(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.vers == VersionTLS13 {
|
||||
hs := serverHandshakeStateTLS13{
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
clientHello: clientHello,
|
||||
}
|
||||
return hs.handshake()
|
||||
}
|
||||
|
||||
hs := serverHandshakeState{
|
||||
c: c,
|
||||
ctx: ctx,
|
||||
clientHello: clientHello,
|
||||
}
|
||||
return hs.handshake()
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) handshake() error {
|
||||
c := hs.c
|
||||
|
||||
if err := hs.processClientHello(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// For an overview of TLS handshaking, see RFC 5246, Section 7.3.
|
||||
c.buffering = true
|
||||
if err := hs.checkForResumption(); err != nil {
|
||||
return err
|
||||
}
|
||||
if hs.sessionState != nil {
|
||||
// The client has included a session ticket and so we do an abbreviated handshake.
|
||||
if err := hs.doResumeHandshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendFinished(c.serverFinished[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientFinishedIsFirst = false
|
||||
if err := hs.readFinished(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// The client didn't include a session ticket, or it wasn't
|
||||
// valid so we do a full handshake.
|
||||
if err := hs.pickCipherSuite(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.doFullHandshake(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.establishKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.readFinished(c.clientFinished[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
c.clientFinishedIsFirst = true
|
||||
c.buffering = true
|
||||
if err := hs.sendSessionTicket(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := hs.sendFinished(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ekm = ekmFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random)
|
||||
c.isHandshakeComplete.Store(true)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// readClientHello reads a ClientHello message and selects the protocol version.
|
||||
func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) {
|
||||
// clientHelloMsg is included in the transcript, but we haven't initialized
|
||||
// it yet. The respective handshake functions will record it themselves.
|
||||
msg, err := c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clientHello, ok := msg.(*clientHelloMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return nil, unexpectedMessageError(clientHello, msg)
|
||||
}
|
||||
|
||||
var configForClient *Config
|
||||
originalConfig := c.config
|
||||
if c.config.GetConfigForClient != nil {
|
||||
chi := clientHelloInfo(ctx, c, clientHello)
|
||||
if configForClient, err = c.config.GetConfigForClient(chi); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return nil, err
|
||||
} else if configForClient != nil {
|
||||
c.config = configForClient
|
||||
}
|
||||
}
|
||||
c.ticketKeys = originalConfig.ticketKeys(configForClient)
|
||||
|
||||
clientVersions := clientHello.supportedVersions
|
||||
if len(clientHello.supportedVersions) == 0 {
|
||||
clientVersions = supportedVersionsFromMax(clientHello.vers)
|
||||
}
|
||||
c.vers, ok = c.config.mutualVersion(roleServer, clientVersions)
|
||||
if !ok {
|
||||
c.sendAlert(alertProtocolVersion)
|
||||
return nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions)
|
||||
}
|
||||
c.haveVers = true
|
||||
c.in.version = c.vers
|
||||
c.out.version = c.vers
|
||||
|
||||
return clientHello, nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) processClientHello() error {
|
||||
c := hs.c
|
||||
|
||||
hs.hello = new(serverHelloMsg)
|
||||
hs.hello.vers = c.vers
|
||||
|
||||
foundCompression := false
|
||||
// We only support null compression, so check that the client offered it.
|
||||
for _, compression := range hs.clientHello.compressionMethods {
|
||||
if compression == compressionNone {
|
||||
foundCompression = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !foundCompression {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: client does not support uncompressed connections")
|
||||
}
|
||||
|
||||
hs.hello.random = make([]byte, 32)
|
||||
serverRandom := hs.hello.random
|
||||
// Downgrade protection canaries. See RFC 8446, Section 4.1.3.
|
||||
maxVers := c.config.maxSupportedVersion(roleServer)
|
||||
if maxVers >= VersionTLS12 && c.vers < maxVers || testingOnlyForceDowngradeCanary {
|
||||
if c.vers == VersionTLS12 {
|
||||
copy(serverRandom[24:], downgradeCanaryTLS12)
|
||||
} else {
|
||||
copy(serverRandom[24:], downgradeCanaryTLS11)
|
||||
}
|
||||
serverRandom = serverRandom[:24]
|
||||
}
|
||||
_, err := io.ReadFull(c.config.rand(), serverRandom)
|
||||
if err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(hs.clientHello.secureRenegotiation) != 0 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: initial handshake had non-empty renegotiation extension")
|
||||
}
|
||||
|
||||
hs.hello.extendedMasterSecret = hs.clientHello.extendedMasterSecret
|
||||
hs.hello.secureRenegotiationSupported = hs.clientHello.secureRenegotiationSupported
|
||||
hs.hello.compressionMethod = compressionNone
|
||||
if len(hs.clientHello.serverName) > 0 {
|
||||
c.serverName = hs.clientHello.serverName
|
||||
}
|
||||
|
||||
selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, false)
|
||||
if err != nil {
|
||||
c.sendAlert(alertNoApplicationProtocol)
|
||||
return err
|
||||
}
|
||||
hs.hello.alpnProtocol = selectedProto
|
||||
c.clientProtocol = selectedProto
|
||||
|
||||
hs.cert, err = c.config.getCertificate(clientHelloInfo(hs.ctx, c, hs.clientHello))
|
||||
if err != nil {
|
||||
if err == errNoCertificates {
|
||||
c.sendAlert(alertUnrecognizedName)
|
||||
} else {
|
||||
c.sendAlert(alertInternalError)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if hs.clientHello.scts {
|
||||
hs.hello.scts = hs.cert.SignedCertificateTimestamps
|
||||
}
|
||||
|
||||
hs.ecdheOk = supportsECDHE(c.config, c.vers, hs.clientHello.supportedCurves, hs.clientHello.supportedPoints)
|
||||
|
||||
if hs.ecdheOk && len(hs.clientHello.supportedPoints) > 0 {
|
||||
// Although omitting the ec_point_formats extension is permitted, some
|
||||
// old OpenSSL version will refuse to handshake if not present.
|
||||
//
|
||||
// Per RFC 4492, section 5.1.2, implementations MUST support the
|
||||
// uncompressed point format. See golang.org/issue/31943.
|
||||
hs.hello.supportedPoints = []uint8{pointFormatUncompressed}
|
||||
}
|
||||
|
||||
if priv, ok := hs.cert.PrivateKey.(crypto.Signer); ok {
|
||||
switch priv.Public().(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
hs.ecSignOk = true
|
||||
case ed25519.PublicKey:
|
||||
hs.ecSignOk = true
|
||||
case *rsa.PublicKey:
|
||||
hs.rsaSignOk = true
|
||||
default:
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported signing key type (%T)", priv.Public())
|
||||
}
|
||||
}
|
||||
if priv, ok := hs.cert.PrivateKey.(crypto.Decrypter); ok {
|
||||
switch priv.Public().(type) {
|
||||
case *rsa.PublicKey:
|
||||
hs.rsaDecryptOk = true
|
||||
default:
|
||||
c.sendAlert(alertInternalError)
|
||||
return fmt.Errorf("tls: unsupported decryption key type (%T)", priv.Public())
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// negotiateALPN picks a shared ALPN protocol that both sides support in server
|
||||
// preference order. If ALPN is not configured or the peer doesn't support it,
|
||||
// it returns "" and no error.
|
||||
func negotiateALPN(serverProtos, clientProtos []string, quic bool) (string, error) {
|
||||
if len(serverProtos) == 0 || len(clientProtos) == 0 {
|
||||
if quic && len(serverProtos) != 0 {
|
||||
// RFC 9001, Section 8.1
|
||||
return "", fmt.Errorf("tls: client did not request an application protocol")
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
var http11fallback bool
|
||||
for _, s := range serverProtos {
|
||||
for _, c := range clientProtos {
|
||||
if s == c {
|
||||
return s, nil
|
||||
}
|
||||
if s == "h2" && c == "http/1.1" {
|
||||
http11fallback = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// As a special case, let http/1.1 clients connect to h2 servers as if they
|
||||
// didn't support ALPN. We used not to enforce protocol overlap, so over
|
||||
// time a number of HTTP servers were configured with only "h2", but
|
||||
// expected to accept connections from "http/1.1" clients. See Issue 46310.
|
||||
if http11fallback {
|
||||
return "", nil
|
||||
}
|
||||
return "", fmt.Errorf("tls: client requested unsupported application protocols (%s)", clientProtos)
|
||||
}
|
||||
|
||||
// supportsECDHE returns whether ECDHE key exchanges can be used with this
|
||||
// pre-TLS 1.3 client.
|
||||
func supportsECDHE(c *Config, version uint16, supportedCurves []CurveID, supportedPoints []uint8) bool {
|
||||
supportsCurve := false
|
||||
for _, curve := range supportedCurves {
|
||||
if c.supportsCurve(version, curve) {
|
||||
supportsCurve = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
supportsPointFormat := false
|
||||
for _, pointFormat := range supportedPoints {
|
||||
if pointFormat == pointFormatUncompressed {
|
||||
supportsPointFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Per RFC 8422, Section 5.1.2, if the Supported Point Formats extension is
|
||||
// missing, uncompressed points are supported. If supportedPoints is empty,
|
||||
// the extension must be missing, as an empty extension body is rejected by
|
||||
// the parser. See https://go.dev/issue/49126.
|
||||
if len(supportedPoints) == 0 {
|
||||
supportsPointFormat = true
|
||||
}
|
||||
|
||||
return supportsCurve && supportsPointFormat
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) pickCipherSuite() error {
|
||||
c := hs.c
|
||||
|
||||
preferenceOrder := cipherSuitesPreferenceOrder
|
||||
if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) {
|
||||
preferenceOrder = cipherSuitesPreferenceOrderNoAES
|
||||
}
|
||||
|
||||
configCipherSuites := c.config.cipherSuites()
|
||||
preferenceList := make([]uint16, 0, len(configCipherSuites))
|
||||
for _, suiteID := range preferenceOrder {
|
||||
for _, id := range configCipherSuites {
|
||||
if id == suiteID {
|
||||
preferenceList = append(preferenceList, id)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hs.suite = selectCipherSuite(preferenceList, hs.clientHello.cipherSuites, hs.cipherSuiteOk)
|
||||
if hs.suite == nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: no cipher suite supported by both client and server")
|
||||
}
|
||||
c.cipherSuite = hs.suite.id
|
||||
|
||||
for _, id := range hs.clientHello.cipherSuites {
|
||||
if id == TLS_FALLBACK_SCSV {
|
||||
// The client is doing a fallback connection. See RFC 7507.
|
||||
if hs.clientHello.vers < c.config.maxSupportedVersion(roleServer) {
|
||||
c.sendAlert(alertInappropriateFallback)
|
||||
return errors.New("tls: client using inappropriate protocol fallback")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
|
||||
if c.flags&suiteECDHE != 0 {
|
||||
if !hs.ecdheOk {
|
||||
return false
|
||||
}
|
||||
if c.flags&suiteECSign != 0 {
|
||||
if !hs.ecSignOk {
|
||||
return false
|
||||
}
|
||||
} else if !hs.rsaSignOk {
|
||||
return false
|
||||
}
|
||||
} else if !hs.rsaDecryptOk {
|
||||
return false
|
||||
}
|
||||
if hs.c.vers < VersionTLS12 && c.flags&suiteTLS12 != 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checkForResumption reports whether we should perform resumption on this connection.
|
||||
func (hs *serverHandshakeState) checkForResumption() error {
|
||||
c := hs.c
|
||||
|
||||
if c.config.SessionTicketsDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
var sessionState *SessionState
|
||||
if c.config.UnwrapSession != nil {
|
||||
ss, err := c.config.UnwrapSession(hs.clientHello.sessionTicket, c.connectionStateLocked())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ss == nil {
|
||||
return nil
|
||||
}
|
||||
sessionState = ss
|
||||
} else {
|
||||
plaintext := c.config.decryptTicket(hs.clientHello.sessionTicket, c.ticketKeys)
|
||||
if plaintext == nil {
|
||||
return nil
|
||||
}
|
||||
ss, err := ParseSessionState(plaintext)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sessionState = ss
|
||||
}
|
||||
|
||||
// TLS 1.2 tickets don't natively have a lifetime, but we want to avoid
|
||||
// re-wrapping the same master secret in different tickets over and over for
|
||||
// too long, weakening forward secrecy.
|
||||
createdAt := time.Unix(int64(sessionState.createdAt), 0)
|
||||
if c.config.time().Sub(createdAt) > maxSessionTicketLifetime {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Never resume a session for a different TLS version.
|
||||
if c.vers != sessionState.version {
|
||||
return nil
|
||||
}
|
||||
|
||||
cipherSuiteOk := false
|
||||
// Check that the client is still offering the ciphersuite in the session.
|
||||
for _, id := range hs.clientHello.cipherSuites {
|
||||
if id == sessionState.cipherSuite {
|
||||
cipherSuiteOk = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !cipherSuiteOk {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check that we also support the ciphersuite from the session.
|
||||
suite := selectCipherSuite([]uint16{sessionState.cipherSuite},
|
||||
c.config.cipherSuites(), hs.cipherSuiteOk)
|
||||
if suite == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
sessionHasClientCerts := len(sessionState.peerCertificates) != 0
|
||||
needClientCerts := requiresClientCert(c.config.ClientAuth)
|
||||
if needClientCerts && !sessionHasClientCerts {
|
||||
return nil
|
||||
}
|
||||
if sessionHasClientCerts && c.config.ClientAuth == NoClientCert {
|
||||
return nil
|
||||
}
|
||||
if sessionHasClientCerts && c.config.time().After(sessionState.peerCertificates[0].NotAfter) {
|
||||
return nil
|
||||
}
|
||||
if sessionHasClientCerts && c.config.ClientAuth >= VerifyClientCertIfGiven &&
|
||||
len(sessionState.verifiedChains) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RFC 7627, Section 5.3
|
||||
if !sessionState.extMasterSecret && hs.clientHello.extendedMasterSecret {
|
||||
return nil
|
||||
}
|
||||
if sessionState.extMasterSecret && !hs.clientHello.extendedMasterSecret {
|
||||
// Aborting is somewhat harsh, but it's a MUST and it would indicate a
|
||||
// weird downgrade in client capabilities.
|
||||
return errors.New("tls: session supported extended_master_secret but client does not")
|
||||
}
|
||||
|
||||
c.peerCertificates = sessionState.peerCertificates
|
||||
c.ocspResponse = sessionState.ocspResponse
|
||||
c.scts = sessionState.scts
|
||||
c.verifiedChains = sessionState.verifiedChains
|
||||
c.extMasterSecret = sessionState.extMasterSecret
|
||||
hs.sessionState = sessionState
|
||||
hs.suite = suite
|
||||
c.didResume = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) doResumeHandshake() error {
|
||||
c := hs.c
|
||||
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
c.cipherSuite = hs.suite.id
|
||||
// We echo the client's session ID in the ServerHello to let it know
|
||||
// that we're doing a resumption.
|
||||
hs.hello.sessionId = hs.clientHello.sessionId
|
||||
// We always send a new session ticket, even if it wraps the same master
|
||||
// secret and it's potentially encrypted with the same key, to help the
|
||||
// client avoid cross-connection tracking from a network observer.
|
||||
hs.hello.ticketSupported = true
|
||||
hs.finishedHash = newFinishedHash(c.vers, hs.suite)
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.config.VerifyConnection != nil {
|
||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hs.masterSecret = hs.sessionState.secret
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) doFullHandshake() error {
|
||||
c := hs.c
|
||||
|
||||
if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
|
||||
hs.hello.ocspStapling = true
|
||||
}
|
||||
|
||||
hs.hello.ticketSupported = hs.clientHello.ticketSupported && !c.config.SessionTicketsDisabled
|
||||
hs.hello.cipherSuite = hs.suite.id
|
||||
|
||||
hs.finishedHash = newFinishedHash(hs.c.vers, hs.suite)
|
||||
if c.config.ClientAuth == NoClientCert {
|
||||
// No need to keep a full record of the handshake if client
|
||||
// certificates won't be used.
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
}
|
||||
if err := transcriptMsg(hs.clientHello, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := hs.c.writeHandshakeRecord(hs.hello, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
certMsg := new(certificateMsg)
|
||||
certMsg.certificates = hs.cert.Certificate
|
||||
if _, err := hs.c.writeHandshakeRecord(certMsg, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hs.hello.ocspStapling {
|
||||
certStatus := new(certificateStatusMsg)
|
||||
certStatus.response = hs.cert.OCSPStaple
|
||||
if _, err := hs.c.writeHandshakeRecord(certStatus, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
keyAgreement := hs.suite.ka(c.vers)
|
||||
skx, err := keyAgreement.generateServerKeyExchange(c.config, hs.cert, hs.clientHello, hs.hello)
|
||||
if err != nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
if skx != nil {
|
||||
if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ {
|
||||
c.curveID = CurveID(byteorder.BeUint16(skx.key[1:]))
|
||||
}
|
||||
if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var certReq *certificateRequestMsg
|
||||
if c.config.ClientAuth >= RequestClientCert {
|
||||
// Request a client certificate
|
||||
certReq = new(certificateRequestMsg)
|
||||
certReq.certificateTypes = []byte{
|
||||
byte(certTypeRSASign),
|
||||
byte(certTypeECDSASign),
|
||||
}
|
||||
if c.vers >= VersionTLS12 {
|
||||
certReq.hasSignatureAlgorithm = true
|
||||
certReq.supportedSignatureAlgorithms = supportedSignatureAlgorithms()
|
||||
}
|
||||
|
||||
// An empty list of certificateAuthorities signals to
|
||||
// the client that it may send any certificate in response
|
||||
// to our request. When we know the CAs we trust, then
|
||||
// we can send them down, so that the client can choose
|
||||
// an appropriate certificate to give to us.
|
||||
if c.config.ClientCAs != nil {
|
||||
certReq.certificateAuthorities = c.config.ClientCAs.Subjects()
|
||||
}
|
||||
if _, err := hs.c.writeHandshakeRecord(certReq, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
helloDone := new(serverHelloDoneMsg)
|
||||
if _, err := hs.c.writeHandshakeRecord(helloDone, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := c.flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var pub crypto.PublicKey // public key for client auth, if any
|
||||
|
||||
msg, err := c.readHandshake(&hs.finishedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If we requested a client certificate, then the client must send a
|
||||
// certificate message, even if it's empty.
|
||||
if c.config.ClientAuth >= RequestClientCert {
|
||||
certMsg, ok := msg.(*certificateMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certMsg, msg)
|
||||
}
|
||||
|
||||
if err := c.processCertsFromClient(Certificate{
|
||||
Certificate: certMsg.certificates,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(certMsg.certificates) != 0 {
|
||||
pub = c.peerCertificates[0].PublicKey
|
||||
}
|
||||
|
||||
msg, err = c.readHandshake(&hs.finishedHash)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.config.VerifyConnection != nil {
|
||||
if err := c.config.VerifyConnection(c.connectionStateLocked()); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Get client key exchange
|
||||
ckx, ok := msg.(*clientKeyExchangeMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(ckx, msg)
|
||||
}
|
||||
|
||||
preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers)
|
||||
if err != nil {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return err
|
||||
}
|
||||
if hs.hello.extendedMasterSecret {
|
||||
c.extMasterSecret = true
|
||||
hs.masterSecret = extMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
|
||||
hs.finishedHash.Sum())
|
||||
} else {
|
||||
hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret,
|
||||
hs.clientHello.random, hs.hello.random)
|
||||
}
|
||||
if err := c.config.writeKeyLog(keyLogLabelTLS12, hs.clientHello.random, hs.masterSecret); err != nil {
|
||||
c.sendAlert(alertInternalError)
|
||||
return err
|
||||
}
|
||||
|
||||
// If we received a client cert in response to our certificate request message,
|
||||
// the client will send us a certificateVerifyMsg immediately after the
|
||||
// clientKeyExchangeMsg. This message is a digest of all preceding
|
||||
// handshake-layer messages that is signed using the private key corresponding
|
||||
// to the client's certificate. This allows us to verify that the client is in
|
||||
// possession of the private key of the certificate.
|
||||
if len(c.peerCertificates) > 0 {
|
||||
// certificateVerifyMsg is included in the transcript, but not until
|
||||
// after we verify the handshake signature, since the state before
|
||||
// this message was sent is used.
|
||||
msg, err = c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
certVerify, ok := msg.(*certificateVerifyMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(certVerify, msg)
|
||||
}
|
||||
|
||||
var sigType uint8
|
||||
var sigHash crypto.Hash
|
||||
if c.vers >= VersionTLS12 {
|
||||
if !isSupportedSignatureAlgorithm(certVerify.signatureAlgorithm, certReq.supportedSignatureAlgorithms) {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return errors.New("tls: client certificate used with invalid signature algorithm")
|
||||
}
|
||||
sigType, sigHash, err = typeAndHashFromSignatureScheme(certVerify.signatureAlgorithm)
|
||||
if err != nil {
|
||||
return c.sendAlert(alertInternalError)
|
||||
}
|
||||
} else {
|
||||
sigType, sigHash, err = legacyTypeAndHashFromPublicKey(pub)
|
||||
if err != nil {
|
||||
c.sendAlert(alertIllegalParameter)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
signed := hs.finishedHash.hashForClientCertificate(sigType, sigHash)
|
||||
if err := verifyHandshakeSignature(sigType, pub, sigHash, signed, certVerify.signature); err != nil {
|
||||
c.sendAlert(alertDecryptError)
|
||||
return errors.New("tls: invalid signature by the client certificate: " + err.Error())
|
||||
}
|
||||
|
||||
if err := transcriptMsg(certVerify, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
hs.finishedHash.discardHandshakeBuffer()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) establishKeys() error {
|
||||
c := hs.c
|
||||
|
||||
clientMAC, serverMAC, clientKey, serverKey, clientIV, serverIV := keysFromMasterSecret(c.vers, hs.suite, hs.masterSecret, hs.clientHello.random, hs.hello.random, hs.suite.macLen, hs.suite.keyLen, hs.suite.ivLen)
|
||||
|
||||
var clientCipher, serverCipher any
|
||||
var clientHash, serverHash hash.Hash
|
||||
|
||||
if hs.suite.aead == nil {
|
||||
clientCipher = hs.suite.cipher(clientKey, clientIV, true /* for reading */)
|
||||
clientHash = hs.suite.mac(clientMAC)
|
||||
serverCipher = hs.suite.cipher(serverKey, serverIV, false /* not for reading */)
|
||||
serverHash = hs.suite.mac(serverMAC)
|
||||
} else {
|
||||
clientCipher = hs.suite.aead(clientKey, clientIV)
|
||||
serverCipher = hs.suite.aead(serverKey, serverIV)
|
||||
}
|
||||
|
||||
c.in.prepareCipherSpec(c.vers, clientCipher, clientHash)
|
||||
c.out.prepareCipherSpec(c.vers, serverCipher, serverHash)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) readFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
if err := c.readChangeCipherSpec(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// finishedMsg is included in the transcript, but not until after we
|
||||
// check the client version, since the state before this message was
|
||||
// sent is used during verification.
|
||||
msg, err := c.readHandshake(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientFinished, ok := msg.(*finishedMsg)
|
||||
if !ok {
|
||||
c.sendAlert(alertUnexpectedMessage)
|
||||
return unexpectedMessageError(clientFinished, msg)
|
||||
}
|
||||
|
||||
verify := hs.finishedHash.clientSum(hs.masterSecret)
|
||||
if len(verify) != len(clientFinished.verifyData) ||
|
||||
subtle.ConstantTimeCompare(verify, clientFinished.verifyData) != 1 {
|
||||
c.sendAlert(alertHandshakeFailure)
|
||||
return errors.New("tls: client's Finished message is incorrect")
|
||||
}
|
||||
|
||||
if err := transcriptMsg(clientFinished, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(out, verify)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) sendSessionTicket() error {
|
||||
if !hs.hello.ticketSupported {
|
||||
return nil
|
||||
}
|
||||
|
||||
c := hs.c
|
||||
m := new(newSessionTicketMsg)
|
||||
|
||||
state := c.sessionState()
|
||||
state.secret = hs.masterSecret
|
||||
if hs.sessionState != nil {
|
||||
// If this is re-wrapping an old key, then keep
|
||||
// the original time it was created.
|
||||
state.createdAt = hs.sessionState.createdAt
|
||||
}
|
||||
if c.config.WrapSession != nil {
|
||||
var err error
|
||||
m.ticket, err = c.config.WrapSession(c.connectionStateLocked(), state)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
stateBytes, err := state.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.ticket, err = c.config.encryptTicket(stateBytes, c.ticketKeys)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := hs.c.writeHandshakeRecord(m, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hs *serverHandshakeState) sendFinished(out []byte) error {
|
||||
c := hs.c
|
||||
|
||||
if err := c.writeChangeCipherRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
finished := new(finishedMsg)
|
||||
finished.verifyData = hs.finishedHash.serverSum(hs.masterSecret)
|
||||
if _, err := hs.c.writeHandshakeRecord(finished, &hs.finishedHash); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
copy(out, finished.verifyData)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processCertsFromClient takes a chain of client certificates either from a
|
||||
// Certificates message and verifies them.
|
||||
func (c *Conn) processCertsFromClient(certificate Certificate) error {
|
||||
certificates := certificate.Certificate
|
||||
certs := make([]*x509.Certificate, len(certificates))
|
||||
var err error
|
||||
for i, asn1Data := range certificates {
|
||||
if certs[i], err = x509.ParseCertificate(asn1Data); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return errors.New("tls: failed to parse client certificate: " + err.Error())
|
||||
}
|
||||
if certs[i].PublicKeyAlgorithm == x509.RSA {
|
||||
n := certs[i].PublicKey.(*rsa.PublicKey).N.BitLen()
|
||||
if max, ok := checkKeySize(n); !ok {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return fmt.Errorf("tls: client sent certificate containing RSA key larger than %d bits", max)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(certs) == 0 && requiresClientCert(c.config.ClientAuth) {
|
||||
if c.vers == VersionTLS13 {
|
||||
c.sendAlert(alertCertificateRequired)
|
||||
} else {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
}
|
||||
return errors.New("tls: client didn't provide a certificate")
|
||||
}
|
||||
|
||||
if c.config.ClientAuth >= VerifyClientCertIfGiven && len(certs) > 0 {
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: c.config.ClientCAs,
|
||||
CurrentTime: c.config.time(),
|
||||
Intermediates: x509.NewCertPool(),
|
||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
chains, err := certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
var errCertificateInvalid x509.CertificateInvalidError
|
||||
if errors.As(err, &x509.UnknownAuthorityError{}) {
|
||||
c.sendAlert(alertUnknownCA)
|
||||
} else if errors.As(err, &errCertificateInvalid) && errCertificateInvalid.Reason == x509.Expired {
|
||||
c.sendAlert(alertCertificateExpired)
|
||||
} else {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
}
|
||||
return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err}
|
||||
}
|
||||
|
||||
c.verifiedChains = chains
|
||||
}
|
||||
|
||||
c.peerCertificates = certs
|
||||
c.ocspResponse = certificate.OCSPStaple
|
||||
c.scts = certificate.SignedCertificateTimestamps
|
||||
|
||||
if len(certs) > 0 {
|
||||
switch certs[0].PublicKey.(type) {
|
||||
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
|
||||
default:
|
||||
c.sendAlert(alertUnsupportedCertificate)
|
||||
return fmt.Errorf("tls: client certificate contains an unsupported public key of type %T", certs[0].PublicKey)
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.VerifyPeerCertificate != nil {
|
||||
if err := c.config.VerifyPeerCertificate(certificates, c.verifiedChains); err != nil {
|
||||
c.sendAlert(alertBadCertificate)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) *ClientHelloInfo {
|
||||
supportedVersions := clientHello.supportedVersions
|
||||
if len(clientHello.supportedVersions) == 0 {
|
||||
supportedVersions = supportedVersionsFromMax(clientHello.vers)
|
||||
}
|
||||
|
||||
return &ClientHelloInfo{
|
||||
CipherSuites: clientHello.cipherSuites,
|
||||
ServerName: clientHello.serverName,
|
||||
SupportedCurves: clientHello.supportedCurves,
|
||||
SupportedPoints: clientHello.supportedPoints,
|
||||
SignatureSchemes: clientHello.supportedSignatureAlgorithms,
|
||||
SupportedProtos: clientHello.alpnProtocols,
|
||||
SupportedVersions: supportedVersions,
|
||||
Conn: c.conn,
|
||||
config: c.config,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
1038
internal/tls/handshake_server_tls13.go
Normal file
1038
internal/tls/handshake_server_tls13.go
Normal file
File diff suppressed because it is too large
Load diff
368
internal/tls/key_agreement.go
Normal file
368
internal/tls/key_agreement.go
Normal file
|
@ -0,0 +1,368 @@
|
|||
// 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.0–1.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")
|
||||
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
|
||||
}
|
201
internal/tls/key_schedule.go
Normal file
201
internal/tls/key_schedule.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
// 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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/sing-shadowtls/internal/mlkem768"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// This file contains the functions necessary to compute the TLS 1.3 key
|
||||
// schedule. See RFC 8446, Section 7.
|
||||
|
||||
const (
|
||||
resumptionBinderLabel = "res binder"
|
||||
clientEarlyTrafficLabel = "c e traffic"
|
||||
clientHandshakeTrafficLabel = "c hs traffic"
|
||||
serverHandshakeTrafficLabel = "s hs traffic"
|
||||
clientApplicationTrafficLabel = "c ap traffic"
|
||||
serverApplicationTrafficLabel = "s ap traffic"
|
||||
exporterLabel = "exp master"
|
||||
resumptionLabel = "res master"
|
||||
trafficUpdateLabel = "traffic upd"
|
||||
)
|
||||
|
||||
// expandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
|
||||
func (c *cipherSuiteTLS13) expandLabel(secret []byte, label string, context []byte, length int) []byte {
|
||||
var hkdfLabel cryptobyte.Builder
|
||||
hkdfLabel.AddUint16(uint16(length))
|
||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes([]byte("tls13 "))
|
||||
b.AddBytes([]byte(label))
|
||||
})
|
||||
hkdfLabel.AddUint8LengthPrefixed(func(b *cryptobyte.Builder) {
|
||||
b.AddBytes(context)
|
||||
})
|
||||
hkdfLabelBytes, err := hkdfLabel.Bytes()
|
||||
if err != nil {
|
||||
// Rather than calling BytesOrPanic, we explicitly handle this error, in
|
||||
// order to provide a reasonable error message. It should be basically
|
||||
// impossible for this to panic, and routing errors back through the
|
||||
// tree rooted in this function is quite painful. The labels are fixed
|
||||
// size, and the context is either a fixed-length computed hash, or
|
||||
// parsed from a field which has the same length limitation. As such, an
|
||||
// error here is likely to only be caused during development.
|
||||
//
|
||||
// NOTE: another reasonable approach here 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(fmt.Errorf("failed to construct HKDF label: %s", err))
|
||||
}
|
||||
out := make([]byte, length)
|
||||
n, err := hkdf.Expand(c.hash.New, secret, hkdfLabelBytes).Read(out)
|
||||
if err != nil || n != length {
|
||||
panic("tls: HKDF-Expand-Label invocation failed unexpectedly")
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// deriveSecret implements Derive-Secret from RFC 8446, Section 7.1.
|
||||
func (c *cipherSuiteTLS13) deriveSecret(secret []byte, label string, transcript hash.Hash) []byte {
|
||||
if transcript == nil {
|
||||
transcript = c.hash.New()
|
||||
}
|
||||
return c.expandLabel(secret, label, transcript.Sum(nil), c.hash.Size())
|
||||
}
|
||||
|
||||
// extract implements HKDF-Extract with the cipher suite hash.
|
||||
func (c *cipherSuiteTLS13) extract(newSecret, currentSecret []byte) []byte {
|
||||
if newSecret == nil {
|
||||
newSecret = make([]byte, c.hash.Size())
|
||||
}
|
||||
return hkdf.Extract(c.hash.New, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
// 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 c.expandLabel(trafficSecret, trafficUpdateLabel, 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 = c.expandLabel(trafficSecret, "key", nil, c.keyLen)
|
||||
iv = c.expandLabel(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 := c.expandLabel(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(masterSecret []byte, transcript hash.Hash) func(string, []byte, int) ([]byte, error) {
|
||||
expMasterSecret := c.deriveSecret(masterSecret, exporterLabel, transcript)
|
||||
return func(label string, context []byte, length int) ([]byte, error) {
|
||||
secret := c.deriveSecret(expMasterSecret, label, nil)
|
||||
h := c.hash.New()
|
||||
h.Write(context)
|
||||
return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil
|
||||
}
|
||||
}
|
||||
|
||||
type keySharePrivateKeys struct {
|
||||
curveID CurveID
|
||||
ecdhe *ecdh.PrivateKey
|
||||
kyber *mlkem768.DecapsulationKey
|
||||
}
|
||||
|
||||
// kyberDecapsulate implements decapsulation according to Kyber Round 3.
|
||||
func kyberDecapsulate(dk *mlkem768.DecapsulationKey, c []byte) ([]byte, error) {
|
||||
K, err := mlkem768.Decapsulate(dk, c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return kyberSharedSecret(K, c), nil
|
||||
}
|
||||
|
||||
// kyberEncapsulate implements encapsulation according to Kyber Round 3.
|
||||
func kyberEncapsulate(ek []byte) (c, ss []byte, err error) {
|
||||
c, ss, err = mlkem768.Encapsulate(ek)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return c, kyberSharedSecret(ss, c), nil
|
||||
}
|
||||
|
||||
func kyberSharedSecret(K, c []byte) []byte {
|
||||
// Package mlkem768 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.Sum256(c)
|
||||
h.Write(ch[:])
|
||||
out := make([]byte, 32)
|
||||
h.Read(out)
|
||||
return out
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
9
internal/tls/notboring.go
Normal file
9
internal/tls/notboring.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// 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.
|
||||
|
||||
//go:build !boringcrypto
|
||||
|
||||
package tls
|
||||
|
||||
func needFIPS() bool { return false }
|
301
internal/tls/prf.go
Normal file
301
internal/tls/prf.go
Normal file
|
@ -0,0 +1,301 @@
|
|||
// 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"
|
||||
)
|
||||
|
||||
// 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(result, secret, label, seed []byte) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
|
||||
func prf12(hashFunc func() hash.Hash) func(result, secret, label, seed []byte) {
|
||||
return func(result, secret, label, seed []byte) {
|
||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
||||
copy(labelAndSeed, label)
|
||||
copy(labelAndSeed[len(label):], seed)
|
||||
|
||||
pHash(result, secret, labelAndSeed, hashFunc)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
masterSecretLength = 48 // Length of a master secret in TLS 1.1.
|
||||
finishedVerifyLength = 12 // Length of verify_data in a Finished message.
|
||||
)
|
||||
|
||||
var (
|
||||
masterSecretLabel = []byte("master secret")
|
||||
extendedMasterSecretLabel = []byte("extended master secret")
|
||||
keyExpansionLabel = []byte("key expansion")
|
||||
clientFinishedLabel = []byte("client finished")
|
||||
serverFinishedLabel = []byte("server finished")
|
||||
)
|
||||
|
||||
func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), 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) func(result, secret, label, seed []byte) {
|
||||
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...)
|
||||
|
||||
masterSecret := make([]byte, masterSecretLength)
|
||||
prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed)
|
||||
return masterSecret
|
||||
}
|
||||
|
||||
// extMasterFromPreMasterSecret generates the extended master secret from the
|
||||
// pre-master secret. See RFC 7627.
|
||||
func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte {
|
||||
masterSecret := make([]byte, masterSecretLength)
|
||||
prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, transcript)
|
||||
return masterSecret
|
||||
}
|
||||
|
||||
// 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 := make([]byte, n)
|
||||
prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed)
|
||||
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 func(result, secret, label, seed []byte)
|
||||
}
|
||||
|
||||
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 {
|
||||
out := make([]byte, finishedVerifyLength)
|
||||
h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
|
||||
return out
|
||||
}
|
||||
|
||||
// serverSum returns the contents of the verify_data member of a server's
|
||||
// Finished message.
|
||||
func (h finishedHash) serverSum(masterSecret []byte) []byte {
|
||||
out := make([]byte, finishedVerifyLength)
|
||||
h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
|
||||
return out
|
||||
}
|
||||
|
||||
// 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...)
|
||||
}
|
||||
|
||||
keyMaterial := make([]byte, length)
|
||||
prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed)
|
||||
return keyMaterial, nil
|
||||
}
|
||||
}
|
500
internal/tls/quic.go
Normal file
500
internal/tls/quic.go
Normal 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.13"))
|
||||
}
|
||||
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
|
||||
}
|
425
internal/tls/ticket.go
Normal file
425
internal/tls/ticket.go
Normal file
|
@ -0,0 +1,425 @@
|
|||
// 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.0–1.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
|
||||
}
|
364
internal/tls/tls.go
Normal file
364
internal/tls/tls.go
Normal file
|
@ -0,0 +1,364 @@
|
|||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
cert.Leaf = x509Cert
|
||||
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")
|
||||
}
|
2
tls.go
2
tls.go
|
@ -1,7 +1,7 @@
|
|||
package shadowtls
|
||||
|
||||
import (
|
||||
sTLS "github.com/sagernet/sing-shadowtls/tls"
|
||||
sTLS "github.com/sagernet/sing-shadowtls/internal/tls"
|
||||
)
|
||||
|
||||
type (
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue