fix(nix): update vendorHash and vendor dir for new deps
This commit is contained in:
+20
@@ -0,0 +1,20 @@
|
||||
package boring
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const Enabled bool = false
|
||||
|
||||
func NewGCMTLS(_ cipher.Block) (cipher.AEAD, error) {
|
||||
return nil, errors.New("boring not implemented")
|
||||
}
|
||||
|
||||
func NewGCMTLS13(_ cipher.Block) (cipher.AEAD, error) {
|
||||
return nil, errors.New("boring not implemented")
|
||||
}
|
||||
|
||||
func Unreachable() {
|
||||
// do nothing
|
||||
}
|
||||
+149
@@ -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),
|
||||
)
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package fips140tls
|
||||
|
||||
func Required() bool {
|
||||
return false
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package helper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/cryptobyte"
|
||||
)
|
||||
|
||||
// Uint8to16 converts a slice of uint8 to a slice of uint16.
|
||||
// e.g. []uint8{0x00, 0x01, 0x00, 0x02} -> []uint16{0x0001, 0x0002}
|
||||
func Uint8to16(in []uint8) ([]uint16, error) {
|
||||
s := cryptobyte.String(in)
|
||||
var out []uint16
|
||||
for !s.Empty() {
|
||||
var v uint16
|
||||
if s.ReadUint16(&v) {
|
||||
out = append(out, v)
|
||||
} else {
|
||||
return nil, errors.New("ReadUint16 failed")
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/hkdf"
|
||||
"hash"
|
||||
)
|
||||
|
||||
func Extract[H hash.Hash](h func() H, secret, salt []byte) []byte {
|
||||
res, err := hkdf.Extract(h, secret, salt)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func Expand[H hash.Hash](h func() H, pseudorandomKey []byte, info string, keyLength int) []byte {
|
||||
res, err := hkdf.Expand(h, pseudorandomKey, info, keyLength)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
+333
@@ -0,0 +1,333 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package hpke
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdh"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/bits"
|
||||
|
||||
"github.com/refraction-networking/utls/internal/byteorder"
|
||||
"github.com/refraction-networking/utls/internal/hkdf"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// testingOnlyGenerateKey is only used during testing, to provide
|
||||
// a fixed test key to use when checking the RFC 9180 vectors.
|
||||
var testingOnlyGenerateKey func() (*ecdh.PrivateKey, error)
|
||||
|
||||
type hkdfKDF struct {
|
||||
hash crypto.Hash
|
||||
}
|
||||
|
||||
func (kdf *hkdfKDF) LabeledExtract(sid []byte, salt []byte, label string, inputKey []byte) []byte {
|
||||
labeledIKM := make([]byte, 0, 7+len(sid)+len(label)+len(inputKey))
|
||||
labeledIKM = append(labeledIKM, []byte("HPKE-v1")...)
|
||||
labeledIKM = append(labeledIKM, sid...)
|
||||
labeledIKM = append(labeledIKM, label...)
|
||||
labeledIKM = append(labeledIKM, inputKey...)
|
||||
return hkdf.Extract(kdf.hash.New, labeledIKM, salt)
|
||||
}
|
||||
|
||||
func (kdf *hkdfKDF) LabeledExpand(suiteID []byte, randomKey []byte, label string, info []byte, length uint16) []byte {
|
||||
labeledInfo := make([]byte, 0, 2+7+len(suiteID)+len(label)+len(info))
|
||||
labeledInfo = byteorder.BEAppendUint16(labeledInfo, length)
|
||||
labeledInfo = append(labeledInfo, []byte("HPKE-v1")...)
|
||||
labeledInfo = append(labeledInfo, suiteID...)
|
||||
labeledInfo = append(labeledInfo, label...)
|
||||
labeledInfo = append(labeledInfo, info...)
|
||||
return hkdf.Expand(kdf.hash.New, randomKey, string(labeledInfo), int(length))
|
||||
}
|
||||
|
||||
// dhKEM implements the KEM specified in RFC 9180, Section 4.1.
|
||||
type dhKEM struct {
|
||||
dh ecdh.Curve
|
||||
kdf hkdfKDF
|
||||
|
||||
suiteID []byte
|
||||
nSecret uint16
|
||||
}
|
||||
|
||||
type KemID uint16
|
||||
|
||||
const DHKEM_X25519_HKDF_SHA256 = 0x0020
|
||||
|
||||
var SupportedKEMs = map[uint16]struct {
|
||||
curve ecdh.Curve
|
||||
hash crypto.Hash
|
||||
nSecret uint16
|
||||
}{
|
||||
// RFC 9180 Section 7.1
|
||||
DHKEM_X25519_HKDF_SHA256: {ecdh.X25519(), crypto.SHA256, 32},
|
||||
}
|
||||
|
||||
func newDHKem(kemID uint16) (*dhKEM, error) {
|
||||
suite, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported suite ID")
|
||||
}
|
||||
return &dhKEM{
|
||||
dh: suite.curve,
|
||||
kdf: hkdfKDF{suite.hash},
|
||||
suiteID: byteorder.BEAppendUint16([]byte("KEM"), kemID),
|
||||
nSecret: suite.nSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (dh *dhKEM) ExtractAndExpand(dhKey, kemContext []byte) []byte {
|
||||
eaePRK := dh.kdf.LabeledExtract(dh.suiteID[:], nil, "eae_prk", dhKey)
|
||||
return dh.kdf.LabeledExpand(dh.suiteID[:], eaePRK, "shared_secret", kemContext, dh.nSecret)
|
||||
}
|
||||
|
||||
func (dh *dhKEM) Encap(pubRecipient *ecdh.PublicKey) (sharedSecret []byte, encapPub []byte, err error) {
|
||||
var privEph *ecdh.PrivateKey
|
||||
if testingOnlyGenerateKey != nil {
|
||||
privEph, err = testingOnlyGenerateKey()
|
||||
} else {
|
||||
privEph, err = dh.dh.GenerateKey(rand.Reader)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
dhVal, err := privEph.ECDH(pubRecipient)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
encPubEph := privEph.PublicKey().Bytes()
|
||||
|
||||
encPubRecip := pubRecipient.Bytes()
|
||||
kemContext := append(encPubEph, encPubRecip...)
|
||||
|
||||
return dh.ExtractAndExpand(dhVal, kemContext), encPubEph, nil
|
||||
}
|
||||
|
||||
func (dh *dhKEM) Decap(encPubEph []byte, secRecipient *ecdh.PrivateKey) ([]byte, error) {
|
||||
pubEph, err := dh.dh.NewPublicKey(encPubEph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dhVal, err := secRecipient.ECDH(pubEph)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kemContext := append(encPubEph, secRecipient.PublicKey().Bytes()...)
|
||||
|
||||
return dh.ExtractAndExpand(dhVal, kemContext), nil
|
||||
}
|
||||
|
||||
type context struct {
|
||||
aead cipher.AEAD
|
||||
|
||||
sharedSecret []byte
|
||||
|
||||
suiteID []byte
|
||||
|
||||
key []byte
|
||||
baseNonce []byte
|
||||
exporterSecret []byte
|
||||
|
||||
seqNum uint128
|
||||
}
|
||||
|
||||
type Sender struct {
|
||||
*context
|
||||
}
|
||||
|
||||
type Receipient struct {
|
||||
*context
|
||||
}
|
||||
|
||||
var aesGCMNew = func(key []byte) (cipher.AEAD, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cipher.NewGCM(block)
|
||||
}
|
||||
|
||||
type AEADID uint16
|
||||
|
||||
const (
|
||||
AEAD_AES_128_GCM = 0x0001
|
||||
AEAD_AES_256_GCM = 0x0002
|
||||
AEAD_ChaCha20Poly1305 = 0x0003
|
||||
)
|
||||
|
||||
var SupportedAEADs = map[uint16]struct {
|
||||
keySize int
|
||||
nonceSize int
|
||||
aead func([]byte) (cipher.AEAD, error)
|
||||
}{
|
||||
// RFC 9180, Section 7.3
|
||||
AEAD_AES_128_GCM: {keySize: 16, nonceSize: 12, aead: aesGCMNew},
|
||||
AEAD_AES_256_GCM: {keySize: 32, nonceSize: 12, aead: aesGCMNew},
|
||||
AEAD_ChaCha20Poly1305: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New},
|
||||
}
|
||||
|
||||
type KDFID uint16
|
||||
|
||||
const KDF_HKDF_SHA256 = 0x0001
|
||||
|
||||
var SupportedKDFs = map[uint16]func() *hkdfKDF{
|
||||
// RFC 9180, Section 7.2
|
||||
KDF_HKDF_SHA256: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} },
|
||||
}
|
||||
|
||||
func newContext(sharedSecret []byte, kemID, kdfID, aeadID uint16, info []byte) (*context, error) {
|
||||
sid := suiteID(kemID, kdfID, aeadID)
|
||||
|
||||
kdfInit, ok := SupportedKDFs[kdfID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KDF id")
|
||||
}
|
||||
kdf := kdfInit()
|
||||
|
||||
aeadInfo, ok := SupportedAEADs[aeadID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported AEAD id")
|
||||
}
|
||||
|
||||
pskIDHash := kdf.LabeledExtract(sid, nil, "psk_id_hash", nil)
|
||||
infoHash := kdf.LabeledExtract(sid, nil, "info_hash", info)
|
||||
ksContext := append([]byte{0}, pskIDHash...)
|
||||
ksContext = append(ksContext, infoHash...)
|
||||
|
||||
secret := kdf.LabeledExtract(sid, sharedSecret, "secret", nil)
|
||||
|
||||
key := kdf.LabeledExpand(sid, secret, "key", ksContext, uint16(aeadInfo.keySize) /* Nk - key size for AEAD */)
|
||||
baseNonce := kdf.LabeledExpand(sid, secret, "base_nonce", ksContext, uint16(aeadInfo.nonceSize) /* Nn - nonce size for AEAD */)
|
||||
exporterSecret := kdf.LabeledExpand(sid, secret, "exp", ksContext, uint16(kdf.hash.Size()) /* Nh - hash output size of the kdf*/)
|
||||
|
||||
aead, err := aeadInfo.aead(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &context{
|
||||
aead: aead,
|
||||
sharedSecret: sharedSecret,
|
||||
suiteID: sid,
|
||||
key: key,
|
||||
baseNonce: baseNonce,
|
||||
exporterSecret: exporterSecret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SetupSender(kemID, kdfID, aeadID uint16, pub *ecdh.PublicKey, info []byte) ([]byte, *Sender, error) {
|
||||
kem, err := newDHKem(kemID)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
sharedSecret, encapsulatedKey, err := kem.Encap(pub)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return encapsulatedKey, &Sender{context}, nil
|
||||
}
|
||||
|
||||
func SetupReceipient(kemID, kdfID, aeadID uint16, priv *ecdh.PrivateKey, info, encPubEph []byte) (*Receipient, error) {
|
||||
kem, err := newDHKem(kemID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sharedSecret, err := kem.Decap(encPubEph, priv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
context, err := newContext(sharedSecret, kemID, kdfID, aeadID, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Receipient{context}, nil
|
||||
}
|
||||
|
||||
func (ctx *context) nextNonce() []byte {
|
||||
nonce := ctx.seqNum.bytes()[16-ctx.aead.NonceSize():]
|
||||
for i := range ctx.baseNonce {
|
||||
nonce[i] ^= ctx.baseNonce[i]
|
||||
}
|
||||
return nonce
|
||||
}
|
||||
|
||||
func (ctx *context) incrementNonce() {
|
||||
// Message limit is, according to the RFC, 2^95+1, which
|
||||
// is somewhat confusing, but we do as we're told.
|
||||
if ctx.seqNum.bitLen() >= (ctx.aead.NonceSize()*8)-1 {
|
||||
panic("message limit reached")
|
||||
}
|
||||
ctx.seqNum = ctx.seqNum.addOne()
|
||||
}
|
||||
|
||||
func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) {
|
||||
ciphertext := s.aead.Seal(nil, s.nextNonce(), plaintext, aad)
|
||||
s.incrementNonce()
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func (r *Receipient) Open(aad, ciphertext []byte) ([]byte, error) {
|
||||
plaintext, err := r.aead.Open(nil, r.nextNonce(), ciphertext, aad)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r.incrementNonce()
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func suiteID(kemID, kdfID, aeadID uint16) []byte {
|
||||
suiteID := make([]byte, 0, 4+2+2+2)
|
||||
suiteID = append(suiteID, []byte("HPKE")...)
|
||||
suiteID = byteorder.BEAppendUint16(suiteID, kemID)
|
||||
suiteID = byteorder.BEAppendUint16(suiteID, kdfID)
|
||||
suiteID = byteorder.BEAppendUint16(suiteID, aeadID)
|
||||
return suiteID
|
||||
}
|
||||
|
||||
func ParseHPKEPublicKey(kemID uint16, bytes []byte) (*ecdh.PublicKey, error) {
|
||||
kemInfo, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KEM id")
|
||||
}
|
||||
return kemInfo.curve.NewPublicKey(bytes)
|
||||
}
|
||||
|
||||
func ParseHPKEPrivateKey(kemID uint16, bytes []byte) (*ecdh.PrivateKey, error) {
|
||||
kemInfo, ok := SupportedKEMs[kemID]
|
||||
if !ok {
|
||||
return nil, errors.New("unsupported KEM id")
|
||||
}
|
||||
return kemInfo.curve.NewPrivateKey(bytes)
|
||||
}
|
||||
|
||||
type uint128 struct {
|
||||
hi, lo uint64
|
||||
}
|
||||
|
||||
func (u uint128) addOne() uint128 {
|
||||
lo, carry := bits.Add64(u.lo, 1, 0)
|
||||
return uint128{u.hi + carry, lo}
|
||||
}
|
||||
|
||||
func (u uint128) bitLen() int {
|
||||
return bits.Len64(u.hi) + bits.Len64(u.lo)
|
||||
}
|
||||
|
||||
func (u uint128) bytes() []byte {
|
||||
b := make([]byte, 16)
|
||||
byteorder.BEPutUint64(b[0:], u.hi)
|
||||
byteorder.BEPutUint64(b[8:], u.lo)
|
||||
return b
|
||||
}
|
||||
Generated
Vendored
+157
@@ -0,0 +1,157 @@
|
||||
// Copyright 2024 The quic-go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file of
|
||||
// the quic-go repository.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// The PacketType is the Long Header Type
|
||||
type PacketType uint8
|
||||
|
||||
const (
|
||||
// PacketTypeInitial is the packet type of an Initial packet
|
||||
PacketTypeInitial PacketType = 1 + iota
|
||||
// PacketTypeRetry is the packet type of a Retry packet
|
||||
PacketTypeRetry
|
||||
// PacketTypeHandshake is the packet type of a Handshake packet
|
||||
PacketTypeHandshake
|
||||
// PacketType0RTT is the packet type of a 0-RTT packet
|
||||
PacketType0RTT
|
||||
)
|
||||
|
||||
func (t PacketType) String() string {
|
||||
switch t {
|
||||
case PacketTypeInitial:
|
||||
return "Initial"
|
||||
case PacketTypeRetry:
|
||||
return "Retry"
|
||||
case PacketTypeHandshake:
|
||||
return "Handshake"
|
||||
case PacketType0RTT:
|
||||
return "0-RTT Protected"
|
||||
default:
|
||||
return fmt.Sprintf("unknown packet type: %d", t)
|
||||
}
|
||||
}
|
||||
|
||||
type ECN uint8
|
||||
|
||||
const (
|
||||
ECNUnsupported ECN = iota
|
||||
ECNNon // 00
|
||||
ECT1 // 01
|
||||
ECT0 // 10
|
||||
ECNCE // 11
|
||||
)
|
||||
|
||||
func ParseECNHeaderBits(bits byte) ECN {
|
||||
switch bits {
|
||||
case 0:
|
||||
return ECNNon
|
||||
case 0b00000010:
|
||||
return ECT0
|
||||
case 0b00000001:
|
||||
return ECT1
|
||||
case 0b00000011:
|
||||
return ECNCE
|
||||
default:
|
||||
panic("invalid ECN bits")
|
||||
}
|
||||
}
|
||||
|
||||
func (e ECN) ToHeaderBits() byte {
|
||||
//nolint:exhaustive // There are only 4 values.
|
||||
switch e {
|
||||
case ECNNon:
|
||||
return 0
|
||||
case ECT0:
|
||||
return 0b00000010
|
||||
case ECT1:
|
||||
return 0b00000001
|
||||
case ECNCE:
|
||||
return 0b00000011
|
||||
default:
|
||||
panic("ECN unsupported")
|
||||
}
|
||||
}
|
||||
|
||||
func (e ECN) String() string {
|
||||
switch e {
|
||||
case ECNUnsupported:
|
||||
return "ECN unsupported"
|
||||
case ECNNon:
|
||||
return "Not-ECT"
|
||||
case ECT1:
|
||||
return "ECT(1)"
|
||||
case ECT0:
|
||||
return "ECT(0)"
|
||||
case ECNCE:
|
||||
return "CE"
|
||||
default:
|
||||
return fmt.Sprintf("invalid ECN value: %d", e)
|
||||
}
|
||||
}
|
||||
|
||||
// A ByteCount in QUIC
|
||||
type ByteCount int64
|
||||
|
||||
// MaxByteCount is the maximum value of a ByteCount
|
||||
const MaxByteCount = ByteCount(1<<62 - 1)
|
||||
|
||||
// InvalidByteCount is an invalid byte count
|
||||
const InvalidByteCount ByteCount = -1
|
||||
|
||||
// A StatelessResetToken is a stateless reset token.
|
||||
type StatelessResetToken [16]byte
|
||||
|
||||
// MaxPacketBufferSize maximum packet size of any QUIC packet, based on
|
||||
// ethernet's max size, minus the IP and UDP headers. IPv6 has a 40 byte header,
|
||||
// UDP adds an additional 8 bytes. This is a total overhead of 48 bytes.
|
||||
// Ethernet's max packet size is 1500 bytes, 1500 - 48 = 1452.
|
||||
const MaxPacketBufferSize = 1452
|
||||
|
||||
// MaxLargePacketBufferSize is used when using GSO
|
||||
const MaxLargePacketBufferSize = 20 * 1024
|
||||
|
||||
// MinInitialPacketSize is the minimum size an Initial packet is required to have.
|
||||
const MinInitialPacketSize = 1200
|
||||
|
||||
// MinUnknownVersionPacketSize is the minimum size a packet with an unknown version
|
||||
// needs to have in order to trigger a Version Negotiation packet.
|
||||
const MinUnknownVersionPacketSize = MinInitialPacketSize
|
||||
|
||||
// MinStatelessResetSize is the minimum size of a stateless reset packet that we send
|
||||
const MinStatelessResetSize = 1 /* first byte */ + 20 /* max. conn ID length */ + 4 /* max. packet number length */ + 1 /* min. payload length */ + 16 /* token */
|
||||
|
||||
// MinConnectionIDLenInitial is the minimum length of the destination connection ID on an Initial packet.
|
||||
const MinConnectionIDLenInitial = 8
|
||||
|
||||
// DefaultAckDelayExponent is the default ack delay exponent
|
||||
const DefaultAckDelayExponent = 3
|
||||
|
||||
// DefaultActiveConnectionIDLimit is the default active connection ID limit
|
||||
const DefaultActiveConnectionIDLimit = 2
|
||||
|
||||
// MaxAckDelayExponent is the maximum ack delay exponent
|
||||
const MaxAckDelayExponent = 20
|
||||
|
||||
// DefaultMaxAckDelay is the default max_ack_delay
|
||||
const DefaultMaxAckDelay = 25 * time.Millisecond
|
||||
|
||||
// MaxMaxAckDelay is the maximum max_ack_delay
|
||||
const MaxMaxAckDelay = (1<<14 - 1) * time.Millisecond
|
||||
|
||||
// MaxConnIDLen is the maximum length of the connection ID
|
||||
const MaxConnIDLen = 20
|
||||
|
||||
// InvalidPacketLimitAES is the maximum number of packets that we can fail to decrypt when using
|
||||
// AEAD_AES_128_GCM or AEAD_AES_265_GCM.
|
||||
const InvalidPacketLimitAES = 1 << 52
|
||||
|
||||
// InvalidPacketLimitChaCha is the maximum number of packets that we can fail to decrypt when using AEAD_CHACHA20_POLY1305.
|
||||
const InvalidPacketLimitChaCha = 1 << 36
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
// Copyright 2024 The quic-go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file of
|
||||
// the quic-go repository.
|
||||
|
||||
package quicvarint
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/refraction-networking/utls/internal/quicvarint/protocol"
|
||||
)
|
||||
|
||||
// taken from the QUIC draft
|
||||
const (
|
||||
// Min is the minimum value allowed for a QUIC varint.
|
||||
Min = 0
|
||||
|
||||
// Max is the maximum allowed value for a QUIC varint (2^62-1).
|
||||
Max = maxVarInt8
|
||||
|
||||
maxVarInt1 = 63
|
||||
maxVarInt2 = 16383
|
||||
maxVarInt4 = 1073741823
|
||||
maxVarInt8 = 4611686018427387903
|
||||
)
|
||||
|
||||
// Read reads a number in the QUIC varint format from r.
|
||||
func Read(r io.ByteReader) (uint64, error) {
|
||||
firstByte, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// the first two bits of the first byte encode the length
|
||||
len := 1 << ((firstByte & 0xc0) >> 6)
|
||||
b1 := firstByte & (0xff - 0xc0)
|
||||
if len == 1 {
|
||||
return uint64(b1), nil
|
||||
}
|
||||
b2, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len == 2 {
|
||||
return uint64(b2) + uint64(b1)<<8, nil
|
||||
}
|
||||
b3, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b4, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len == 4 {
|
||||
return uint64(b4) + uint64(b3)<<8 + uint64(b2)<<16 + uint64(b1)<<24, nil
|
||||
}
|
||||
b5, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b6, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b7, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
b8, err := r.ReadByte()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(b8) + uint64(b7)<<8 + uint64(b6)<<16 + uint64(b5)<<24 + uint64(b4)<<32 + uint64(b3)<<40 + uint64(b2)<<48 + uint64(b1)<<56, nil
|
||||
}
|
||||
|
||||
// Append appends i in the QUIC varint format.
|
||||
func Append(b []byte, i uint64) []byte {
|
||||
if i <= maxVarInt1 {
|
||||
return append(b, uint8(i))
|
||||
}
|
||||
if i <= maxVarInt2 {
|
||||
return append(b, []byte{uint8(i>>8) | 0x40, uint8(i)}...)
|
||||
}
|
||||
if i <= maxVarInt4 {
|
||||
return append(b, []byte{uint8(i>>24) | 0x80, uint8(i >> 16), uint8(i >> 8), uint8(i)}...)
|
||||
}
|
||||
if i <= maxVarInt8 {
|
||||
return append(b, []byte{
|
||||
uint8(i>>56) | 0xc0, uint8(i >> 48), uint8(i >> 40), uint8(i >> 32),
|
||||
uint8(i >> 24), uint8(i >> 16), uint8(i >> 8), uint8(i),
|
||||
}...)
|
||||
}
|
||||
panic(fmt.Sprintf("%#x doesn't fit into 62 bits", i))
|
||||
}
|
||||
|
||||
// AppendWithLen append i in the QUIC varint format with the desired length.
|
||||
func AppendWithLen(b []byte, i uint64, length protocol.ByteCount) []byte {
|
||||
if length != 1 && length != 2 && length != 4 && length != 8 {
|
||||
panic("invalid varint length")
|
||||
}
|
||||
l := Len(i)
|
||||
if l == length {
|
||||
return Append(b, i)
|
||||
}
|
||||
if l > length {
|
||||
panic(fmt.Sprintf("cannot encode %d in %d bytes", i, length))
|
||||
}
|
||||
if length == 2 {
|
||||
b = append(b, 0b01000000)
|
||||
} else if length == 4 {
|
||||
b = append(b, 0b10000000)
|
||||
} else if length == 8 {
|
||||
b = append(b, 0b11000000)
|
||||
}
|
||||
for j := protocol.ByteCount(1); j < length-l; j++ {
|
||||
b = append(b, 0)
|
||||
}
|
||||
for j := protocol.ByteCount(0); j < l; j++ {
|
||||
b = append(b, uint8(i>>(8*(l-1-j))))
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Len determines the number of bytes that will be needed to write the number i.
|
||||
func Len(i uint64) protocol.ByteCount {
|
||||
if i <= maxVarInt1 {
|
||||
return 1
|
||||
}
|
||||
if i <= maxVarInt2 {
|
||||
return 2
|
||||
}
|
||||
if i <= maxVarInt4 {
|
||||
return 4
|
||||
}
|
||||
if i <= maxVarInt8 {
|
||||
return 8
|
||||
}
|
||||
// Don't use a fmt.Sprintf here to format the error message.
|
||||
// The function would then exceed the inlining budget.
|
||||
panic(struct {
|
||||
message string
|
||||
num uint64
|
||||
}{"value doesn't fit into 62 bits: ", i})
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tls12
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// PRF implements the TLS 1.2 pseudo-random function, as defined in RFC 5246,
|
||||
// Section 5 and allowed by SP 800-135, Revision 1, Section 4.2.2.
|
||||
func PRF(hash func() hash.Hash, secret []byte, label string, seed []byte, keyLen int) []byte {
|
||||
labelAndSeed := make([]byte, len(label)+len(seed))
|
||||
copy(labelAndSeed, label)
|
||||
copy(labelAndSeed[len(label):], seed)
|
||||
|
||||
result := make([]byte, keyLen)
|
||||
pHash(hash, result, secret, labelAndSeed)
|
||||
return result
|
||||
}
|
||||
|
||||
// pHash implements the P_hash function, as defined in RFC 5246, Section 5.
|
||||
func pHash(hash func() hash.Hash, result, secret, seed []byte) {
|
||||
h := hmac.New(hash, secret)
|
||||
h.Write(seed)
|
||||
a := h.Sum(nil)
|
||||
|
||||
for len(result) > 0 {
|
||||
h.Reset()
|
||||
h.Write(a)
|
||||
h.Write(seed)
|
||||
b := h.Sum(nil)
|
||||
n := copy(result, b)
|
||||
result = result[n:]
|
||||
|
||||
h.Reset()
|
||||
h.Write(a)
|
||||
a = h.Sum(nil)
|
||||
}
|
||||
}
|
||||
|
||||
const masterSecretLength = 48
|
||||
const extendedMasterSecretLabel = "extended master secret"
|
||||
|
||||
// MasterSecret implements the TLS 1.2 extended master secret derivation, as
|
||||
// defined in RFC 7627 and allowed by SP 800-135, Revision 1, Section 4.2.2.
|
||||
func MasterSecret(hash func() hash.Hash, preMasterSecret, transcript []byte) []byte {
|
||||
// [uTLS SECTION BEGIN]
|
||||
// "The TLS 1.2 KDF is an approved KDF when the following conditions are
|
||||
// satisfied: [...] (3) P_HASH uses either SHA-256, SHA-384 or SHA-512."
|
||||
// h := hash()
|
||||
// switch any(h).(type) {
|
||||
// case *sha256.Digest:
|
||||
// if h.Size() != 32 {
|
||||
// fips140.RecordNonApproved()
|
||||
// }
|
||||
// case *sha512.Digest:
|
||||
// if h.Size() != 46 && h.Size() != 64 {
|
||||
// fips140.RecordNonApproved()
|
||||
// }
|
||||
// default:
|
||||
// fips140.RecordNonApproved()
|
||||
// }
|
||||
// [uTLS SECTION END]
|
||||
|
||||
return PRF(hash, preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength)
|
||||
}
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tls13 implements the TLS 1.3 Key Schedule as specified in RFC 8446,
|
||||
// Section 7.1 and allowed by FIPS 140-3 IG 2.4.B Resolution 7.
|
||||
package tls13
|
||||
|
||||
import (
|
||||
fips140 "hash"
|
||||
|
||||
"github.com/refraction-networking/utls/internal/byteorder"
|
||||
"github.com/refraction-networking/utls/internal/hkdf"
|
||||
)
|
||||
|
||||
// We don't set the service indicator in this package but we delegate that to
|
||||
// the underlying functions because the TLS 1.3 KDF does not have a standard of
|
||||
// its own.
|
||||
|
||||
// ExpandLabel implements HKDF-Expand-Label from RFC 8446, Section 7.1.
|
||||
func ExpandLabel[H fips140.Hash](hash func() H, secret []byte, label string, context []byte, length int) []byte {
|
||||
if len("tls13 ")+len(label) > 255 || len(context) > 255 {
|
||||
// It should be impossible for this to panic: labels are fixed strings,
|
||||
// and context is either a fixed-length computed hash, or parsed from a
|
||||
// field which has the same length limitation.
|
||||
//
|
||||
// Another reasonable approach might be to return a randomized slice if
|
||||
// we encounter an error, which would break the connection, but avoid
|
||||
// panicking. This would perhaps be safer but significantly more
|
||||
// confusing to users.
|
||||
panic("tls13: label or context too long")
|
||||
}
|
||||
hkdfLabel := make([]byte, 0, 2+1+len("tls13 ")+len(label)+1+len(context))
|
||||
hkdfLabel = byteorder.BEAppendUint16(hkdfLabel, uint16(length))
|
||||
hkdfLabel = append(hkdfLabel, byte(len("tls13 ")+len(label)))
|
||||
hkdfLabel = append(hkdfLabel, "tls13 "...)
|
||||
hkdfLabel = append(hkdfLabel, label...)
|
||||
hkdfLabel = append(hkdfLabel, byte(len(context)))
|
||||
hkdfLabel = append(hkdfLabel, context...)
|
||||
return hkdf.Expand(hash, secret, string(hkdfLabel), length)
|
||||
}
|
||||
|
||||
func extract[H fips140.Hash](hash func() H, newSecret, currentSecret []byte) []byte {
|
||||
if newSecret == nil {
|
||||
newSecret = make([]byte, hash().Size())
|
||||
}
|
||||
return hkdf.Extract(hash, newSecret, currentSecret)
|
||||
}
|
||||
|
||||
func deriveSecret[H fips140.Hash](hash func() H, secret []byte, label string, transcript fips140.Hash) []byte {
|
||||
if transcript == nil {
|
||||
transcript = hash()
|
||||
}
|
||||
return ExpandLabel(hash, secret, label, transcript.Sum(nil), transcript.Size())
|
||||
}
|
||||
|
||||
const (
|
||||
resumptionBinderLabel = "res binder"
|
||||
clientEarlyTrafficLabel = "c e traffic"
|
||||
clientHandshakeTrafficLabel = "c hs traffic"
|
||||
serverHandshakeTrafficLabel = "s hs traffic"
|
||||
clientApplicationTrafficLabel = "c ap traffic"
|
||||
serverApplicationTrafficLabel = "s ap traffic"
|
||||
earlyExporterLabel = "e exp master"
|
||||
exporterLabel = "exp master"
|
||||
resumptionLabel = "res master"
|
||||
)
|
||||
|
||||
type EarlySecret struct {
|
||||
secret []byte
|
||||
hash func() fips140.Hash
|
||||
}
|
||||
|
||||
func NewEarlySecret[H fips140.Hash](hash func() H, psk []byte) *EarlySecret {
|
||||
return &EarlySecret{
|
||||
secret: extract(hash, psk, nil),
|
||||
hash: func() fips140.Hash { return hash() },
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EarlySecret) ResumptionBinderKey() []byte {
|
||||
return deriveSecret(s.hash, s.secret, resumptionBinderLabel, nil)
|
||||
}
|
||||
|
||||
// ClientEarlyTrafficSecret derives the client_early_traffic_secret from the
|
||||
// early secret and the transcript up to the ClientHello.
|
||||
func (s *EarlySecret) ClientEarlyTrafficSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, clientEarlyTrafficLabel, transcript)
|
||||
}
|
||||
|
||||
type HandshakeSecret struct {
|
||||
secret []byte
|
||||
hash func() fips140.Hash
|
||||
}
|
||||
|
||||
func (s *EarlySecret) HandshakeSecret(sharedSecret []byte) *HandshakeSecret {
|
||||
derived := deriveSecret(s.hash, s.secret, "derived", nil)
|
||||
return &HandshakeSecret{
|
||||
secret: extract(s.hash, sharedSecret, derived),
|
||||
hash: s.hash,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientHandshakeTrafficSecret derives the client_handshake_traffic_secret from
|
||||
// the handshake secret and the transcript up to the ServerHello.
|
||||
func (s *HandshakeSecret) ClientHandshakeTrafficSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, clientHandshakeTrafficLabel, transcript)
|
||||
}
|
||||
|
||||
// ServerHandshakeTrafficSecret derives the server_handshake_traffic_secret from
|
||||
// the handshake secret and the transcript up to the ServerHello.
|
||||
func (s *HandshakeSecret) ServerHandshakeTrafficSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, serverHandshakeTrafficLabel, transcript)
|
||||
}
|
||||
|
||||
type MasterSecret struct {
|
||||
secret []byte
|
||||
hash func() fips140.Hash
|
||||
}
|
||||
|
||||
func (s *HandshakeSecret) MasterSecret() *MasterSecret {
|
||||
derived := deriveSecret(s.hash, s.secret, "derived", nil)
|
||||
return &MasterSecret{
|
||||
secret: extract(s.hash, nil, derived),
|
||||
hash: s.hash,
|
||||
}
|
||||
}
|
||||
|
||||
// ClientApplicationTrafficSecret derives the client_application_traffic_secret_0
|
||||
// from the master secret and the transcript up to the server Finished.
|
||||
func (s *MasterSecret) ClientApplicationTrafficSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, clientApplicationTrafficLabel, transcript)
|
||||
}
|
||||
|
||||
// ServerApplicationTrafficSecret derives the server_application_traffic_secret_0
|
||||
// from the master secret and the transcript up to the server Finished.
|
||||
func (s *MasterSecret) ServerApplicationTrafficSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, serverApplicationTrafficLabel, transcript)
|
||||
}
|
||||
|
||||
// ResumptionMasterSecret derives the resumption_master_secret from the master secret
|
||||
// and the transcript up to the client Finished.
|
||||
func (s *MasterSecret) ResumptionMasterSecret(transcript fips140.Hash) []byte {
|
||||
return deriveSecret(s.hash, s.secret, resumptionLabel, transcript)
|
||||
}
|
||||
|
||||
type ExporterMasterSecret struct {
|
||||
secret []byte
|
||||
hash func() fips140.Hash
|
||||
}
|
||||
|
||||
// ExporterMasterSecret derives the exporter_master_secret from the master secret
|
||||
// and the transcript up to the server Finished.
|
||||
func (s *MasterSecret) ExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
|
||||
return &ExporterMasterSecret{
|
||||
secret: deriveSecret(s.hash, s.secret, exporterLabel, transcript),
|
||||
hash: s.hash,
|
||||
}
|
||||
}
|
||||
|
||||
// EarlyExporterMasterSecret derives the exporter_master_secret from the early secret
|
||||
// and the transcript up to the ClientHello.
|
||||
func (s *EarlySecret) EarlyExporterMasterSecret(transcript fips140.Hash) *ExporterMasterSecret {
|
||||
return &ExporterMasterSecret{
|
||||
secret: deriveSecret(s.hash, s.secret, earlyExporterLabel, transcript),
|
||||
hash: s.hash,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ExporterMasterSecret) Exporter(label string, context []byte, length int) []byte {
|
||||
secret := deriveSecret(s.hash, s.secret, label, nil)
|
||||
h := s.hash()
|
||||
h.Write(context)
|
||||
return ExpandLabel(s.hash, secret, "exporter", h.Sum(nil), length)
|
||||
}
|
||||
|
||||
func TestingOnlyExporterSecret(s *ExporterMasterSecret) []byte {
|
||||
return s.secret
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package tls13
|
||||
|
||||
import fips140 "hash"
|
||||
|
||||
func NewEarlySecretFromSecret[H fips140.Hash](hash func() H, secret []byte) *EarlySecret {
|
||||
return &EarlySecret{
|
||||
secret: secret,
|
||||
hash: func() fips140.Hash { return hash() },
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EarlySecret) Secret() []byte {
|
||||
if s != nil {
|
||||
return s.secret
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewMasterSecretFromSecret[H fips140.Hash](hash func() H, secret []byte) *MasterSecret {
|
||||
return &MasterSecret{
|
||||
secret: secret,
|
||||
hash: func() fips140.Hash { return hash() },
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MasterSecret) Secret() []byte {
|
||||
if s != nil {
|
||||
return s.secret
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user