diff --git a/auth.go b/auth.go index eff8def..680192f 100644 --- a/auth.go +++ b/auth.go @@ -291,9 +291,11 @@ func selectSignatureScheme(vers uint16, c *Certificate, peerAlgs []SignatureSche // 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 - } + // [uTLS] SECTION BEGIN + // if fips140tls.Required() && !isSupportedSignatureAlgorithm(preferredAlg, defaultSupportedSignatureAlgorithmsFIPS) { + // continue + // } + // [uTLS] SECTION END if isSupportedSignatureAlgorithm(preferredAlg, supportedAlgs) { return preferredAlg, nil } diff --git a/auth_test.go b/auth_test.go index 54cf15d..8ee7266 100644 --- a/auth_test.go +++ b/auth_test.go @@ -9,6 +9,7 @@ import ( "testing" circlPki "github.com/cloudflare/circl/pki" + "github.com/refraction-networking/utls/internal/fips140tls" ) func TestSignatureSelection(t *testing.T) { @@ -59,6 +60,11 @@ func TestSignatureSelection(t *testing.T) { } for testNo, test := range tests { + if fips140tls.Required() && (test.expectedHash == crypto.SHA1 || test.expectedSigAlg == Ed25519) { + t.Logf("skipping test[%d] - not compatible with TLS FIPS mode", testNo) + continue + } + sigAlg, err := selectSignatureScheme(test.tlsVersion, test.cert, test.peerSigAlgs) if err != nil { t.Errorf("test[%d]: unexpected selectSignatureScheme error: %v", testNo, err) diff --git a/cipher_suites.go b/cipher_suites.go index f6f21a4..5e01f7e 100644 --- a/cipher_suites.go +++ b/cipher_suites.go @@ -10,8 +10,6 @@ import ( "crypto/cipher" "crypto/des" "crypto/hmac" - - // "crypto/internal/boring" "crypto/rc4" "crypto/sha1" "crypto/sha256" @@ -236,7 +234,7 @@ var cipherSuitesTLS13 = []*cipherSuiteTLS13{ // TODO: replace with a map. // - 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 +// countermeasures. See https://www.isg.rhul.ac.uk/tls/Lucky13.html and // https://www.imperialviolet.org/2013/02/04/luckythirteen.html. // // - Anything else comes before 3DES @@ -368,15 +366,13 @@ var tdesCiphers = map[uint16]bool{ } var ( - hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ + // Keep in sync with crypto/internal/fips140/aes/gcm.supportsAESGCM. + hasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3 hasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL - // 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) + hasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH + hasGCMAsmPPC64 = runtime.GOARCH == "ppc64" || runtime.GOARCH == "ppc64le" - hasAESGCMHardwareSupport = runtime.GOARCH == "amd64" && hasGCMAsmAMD64 || - runtime.GOARCH == "arm64" && hasGCMAsmARM64 || - runtime.GOARCH == "s390x" && hasGCMAsmS390X + hasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64 ) var aesgcmCiphers = map[uint16]bool{ @@ -526,7 +522,10 @@ func aeadAESGCM(key, noncePrefix []byte) aead { aead, err = boring.NewGCMTLS(aes) } else { boring.Unreachable() + // [uTLS] SECTION BEGIN + // aead, err = gcm.NewGCMForTLS12(aes.(*fipsaes.Block)) aead, err = cipher.NewGCM(aes) + // [uTLS] SECTION END } if err != nil { panic(err) @@ -555,7 +554,16 @@ func aeadAESGCMTLS13(key, nonceMask []byte) aead { if err != nil { panic(err) } - aead, err := cipher.NewGCM(aes) + var aead cipher.AEAD + if boring.Enabled { + aead, err = boring.NewGCMTLS13(aes) + } else { + boring.Unreachable() + // [uTLS] SECTION BEGIN + // aead, err = gcm.NewGCMForTLS13(aes.(*fipsaes.Block)) + aead, err = cipher.NewGCM(aes) + // [uTLS] SECTION END + } if err != nil { panic(err) } diff --git a/common.go b/common.go index 46abb61..fea76bd 100644 --- a/common.go +++ b/common.go @@ -25,6 +25,8 @@ import ( "sync" "time" _ "unsafe" // for linkname + + "github.com/refraction-networking/utls/internal/fips140tls" ) const ( @@ -145,17 +147,21 @@ const ( type CurveID uint16 const ( - CurveP256 CurveID = 23 - CurveP384 CurveID = 24 - CurveP521 CurveID = 25 - X25519 CurveID = 29 - - // Experimental codepoint for X25519Kyber768Draft00, specified in - // draft-tls-westerbaan-xyber768d00-03. Not exported, as support might be - // removed in the future. - x25519Kyber768Draft00 CurveID = 0x6399 // X25519Kyber768Draft00 + CurveP256 CurveID = 23 + CurveP384 CurveID = 24 + CurveP521 CurveID = 25 + X25519 CurveID = 29 + X25519MLKEM768 CurveID = 4588 ) +func isTLS13OnlyKeyExchange(curve CurveID) bool { + return curve == X25519MLKEM768 +} + +func isPQKeyExchange(curve CurveID) bool { + return curve == X25519MLKEM768 +} + // TLS 1.3 Key Share. See RFC 8446, Section 4.2.8. type keyShare struct { group CurveID @@ -430,9 +436,12 @@ type ClientHelloInfo struct { // client is using SNI (see RFC 4366, Section 3.1). ServerName string - // SupportedCurves lists the elliptic curves supported by the client. - // SupportedCurves is set only if the Supported Elliptic Curves - // Extension is being used (see RFC 4492, Section 5.1.1). + // SupportedCurves lists the key exchange mechanisms supported by the + // client. It was renamed to "supported groups" in TLS 1.3, see RFC 8446, + // Section 4.2.7 and [CurveID]. + // + // SupportedCurves may be nil in TLS 1.2 and lower if the Supported Elliptic + // Curves Extension is not being used (see RFC 4492, Section 5.1.1). SupportedCurves []CurveID // SupportedPoints lists the point formats supported by the client. @@ -459,6 +468,10 @@ type ClientHelloInfo struct { // might be rejected if used. SupportedVersions []uint16 + // Extensions lists the IDs of the extensions presented by the client + // in the ClientHello. + Extensions []uint16 + // Conn is the underlying net.Conn for the connection. Do not read // from, or write to, this connection; that will cause the TLS // connection to fail. @@ -808,14 +821,15 @@ type Config struct { // which is currently TLS 1.3. MaxVersion uint16 - // CurvePreferences contains the elliptic curves that will be used in - // an ECDHE handshake, in preference order. If empty, the default will - // be used. The client will use the first preference as the type for - // its key share in TLS 1.3. This may change in the future. + // CurvePreferences contains a set of supported key exchange mechanisms. + // The name refers to elliptic curves for legacy reasons, see [CurveID]. + // The order of the list is ignored, and key exchange mechanisms are chosen + // from this list using an internal preference order. If empty, the default + // will be used. // - // From Go 1.23, the default includes the X25519Kyber768Draft00 hybrid + // From Go 1.24, the default includes the [X25519MLKEM768] hybrid // post-quantum key exchange. To disable it, set CurvePreferences explicitly - // or use the GODEBUG=tlskyber=0 environment variable. + // or use the GODEBUG=tlsmlkem=0 environment variable. CurvePreferences []CurveID // PQSignatureSchemesEnabled controls whether additional post-quantum @@ -843,8 +857,10 @@ type Config struct { // EncryptedClientHelloConfigList is a serialized ECHConfigList. If // provided, clients will attempt to connect to servers using Encrypted - // Client Hello (ECH) using one of the provided ECHConfigs. Servers - // currently ignore this field. + // Client Hello (ECH) using one of the provided ECHConfigs. + // + // Servers do not use this field. In order to configure ECH for servers, see + // the EncryptedClientHelloKeys field. // // If the list contains no valid ECH configs, the handshake will fail // and return an error. @@ -853,7 +869,7 @@ type Config struct { // be VersionTLS13. // // When EncryptedClientHelloConfigList is set, the handshake will only - // succeed if ECH is sucessfully negotiated. If the server rejects ECH, + // succeed if ECH is successfully negotiated. If the server rejects ECH, // an ECHRejectionError error will be returned, which may contain a new // ECHConfigList that the server suggests using. // @@ -862,9 +878,11 @@ type Config struct { EncryptedClientHelloConfigList []byte // EncryptedClientHelloRejectionVerify, if not nil, is called when ECH is - // rejected, in order to verify the ECH provider certificate in the outer - // Client Hello. If it returns a non-nil error, the handshake is aborted and - // that error results. + // rejected by the remote server, in order to verify the ECH provider + // certificate in the outer ClientHello. If it returns a non-nil error, the + // handshake is aborted and that error results. + // + // On the server side this field is not used. // // Unlike VerifyPeerCertificate and VerifyConnection, normal certificate // verification will not be performed before calling @@ -876,6 +894,20 @@ type Config struct { // when ECH is rejected, even if set, and InsecureSkipVerify is ignored. EncryptedClientHelloRejectionVerify func(ConnectionState) error + // EncryptedClientHelloKeys are the ECH keys to use when a client + // attempts ECH. + // + // If EncryptedClientHelloKeys is set, MinVersion, if set, must be + // VersionTLS13. + // + // If a client attempts ECH, but it is rejected by the server, the server + // will send a list of configs to retry based on the set of + // EncryptedClientHelloKeys which have the SendAsRetry field set. + // + // On the client side, this field is ignored. In order to configure ECH for + // clients, see the EncryptedClientHelloConfigList field. + EncryptedClientHelloKeys []EncryptedClientHelloKey + // mutex protects sessionTicketKeys and autoSessionTicketKeys. mutex sync.RWMutex // sessionTicketKeys contains zero or more ticket keys. If set, it means @@ -900,6 +932,24 @@ type Config struct { ECHConfigs []ECHConfig // [uTLS] } +// EncryptedClientHelloKey holds a private key that is associated +// with a specific ECH config known to a client. +type EncryptedClientHelloKey struct { + // Config should be a marshalled ECHConfig associated with PrivateKey. This + // must match the config provided to clients byte-for-byte. The config + // should only specify the DHKEM(X25519, HKDF-SHA256) KEM ID (0x0020), the + // HKDF-SHA256 KDF ID (0x0001), and a subset of the following AEAD IDs: + // AES-128-GCM (0x0000), AES-256-GCM (0x0001), ChaCha20Poly1305 (0x0002). + Config []byte + // PrivateKey should be a marshalled private key. Currently, we expect + // this to be the output of [ecdh.PrivateKey.Bytes]. + PrivateKey []byte + // SendAsRetry indicates if Config should be sent as part of the list of + // retry configs when ECH is requested by the client but rejected by the + // server. + SendAsRetry bool +} + const ( // ticketKeyLifetime is how long a ticket key remains valid and can be used to // resume a client connection. @@ -981,6 +1031,7 @@ func (c *Config) Clone() *Config { KeyLogWriter: c.KeyLogWriter, EncryptedClientHelloConfigList: c.EncryptedClientHelloConfigList, EncryptedClientHelloRejectionVerify: c.EncryptedClientHelloRejectionVerify, + EncryptedClientHelloKeys: c.EncryptedClientHelloKeys, sessionTicketKeys: c.sessionTicketKeys, autoSessionTicketKeys: c.autoSessionTicketKeys, @@ -1133,17 +1184,21 @@ func (c *Config) time() time.Time { func (c *Config) cipherSuites() []uint16 { if c.CipherSuites == nil { - if needFIPS() { - return defaultCipherSuitesFIPS - } + // [uTLS] SECTION BEGIN + // if fips140tls.Required() { + // return defaultCipherSuitesFIPS + // } + // [uTLS] SECTION END return defaultCipherSuites() } - if needFIPS() { - cipherSuites := slices.Clone(c.CipherSuites) - return slices.DeleteFunc(cipherSuites, func(id uint16) bool { - return !slices.Contains(defaultCipherSuitesFIPS, id) - }) - } + // [uTLS] SECTION BEGIN + // if fips140tls.Required() { + // cipherSuites := slices.Clone(c.CipherSuites) + // return slices.DeleteFunc(cipherSuites, func(id uint16) bool { + // return !slices.Contains(defaultCipherSuitesFIPS, id) + // }) + // } + // [uTLS] SECTION END return c.CipherSuites } @@ -1164,9 +1219,11 @@ const roleServer = false func (c *Config) supportedVersions(isClient bool) []uint16 { versions := make([]uint16, 0, len(supportedVersions)) for _, v := range supportedVersions { - if needFIPS() && !slices.Contains(defaultSupportedVersionsFIPS, v) { - continue - } + // [uTLS] SECTION BEGIN + // if fips140tls.Required() && !slices.Contains(defaultSupportedVersionsFIPS, v) { + // continue + // } + // [uTLS] SECTION END if (c == nil || c.MinVersion == 0) && v < VersionTLS12 { // [uTLS SECTION BEGIN] // Disable unsupported godebug package @@ -1216,22 +1273,20 @@ func supportedVersionsFromMax(maxVersion uint16) []uint16 { func (c *Config) curvePreferences(version uint16) []CurveID { var curvePreferences []CurveID + // [uTLS] SECTION BEGIN + // if fips140tls.Required() { + // curvePreferences = slices.Clone(defaultCurvePreferencesFIPS) + // } else { + curvePreferences = defaultCurvePreferences() + // } + // [uTLS] SECTION END if c != nil && len(c.CurvePreferences) != 0 { - curvePreferences = slices.Clone(c.CurvePreferences) - if needFIPS() { - return slices.DeleteFunc(curvePreferences, func(c CurveID) bool { - return !slices.Contains(defaultCurvePreferencesFIPS, c) - }) - } - } else if needFIPS() { - curvePreferences = slices.Clone(defaultCurvePreferencesFIPS) - } else { - curvePreferences = defaultCurvePreferences() + curvePreferences = slices.DeleteFunc(curvePreferences, func(x CurveID) bool { + return !slices.Contains(c.CurvePreferences, x) + }) } if version < VersionTLS13 { - return slices.DeleteFunc(curvePreferences, func(c CurveID) bool { - return c == x25519Kyber768Draft00 - }) + curvePreferences = slices.DeleteFunc(curvePreferences, isTLS13OnlyKeyExchange) } return curvePreferences } @@ -1695,10 +1750,12 @@ func unexpectedMessageError(wanted, got any) error { // supportedSignatureAlgorithms returns the supported signature algorithms. func supportedSignatureAlgorithms() []SignatureScheme { - if !needFIPS() { - return defaultSupportedSignatureAlgorithms - } - return defaultSupportedSignatureAlgorithmsFIPS + // [uTLS] SECTION BEGIN + // if !fips140tls.Required() { + return defaultSupportedSignatureAlgorithms + // } + // return defaultSupportedSignatureAlgorithmsFIPS + // [uTLS] SECTION END } func isSupportedSignatureAlgorithm(sigAlg SignatureScheme, supportedSignatureAlgorithms []SignatureScheme) bool { @@ -1724,3 +1781,56 @@ func (e *CertificateVerificationError) Error() string { func (e *CertificateVerificationError) Unwrap() error { return e.Err } + +// fipsAllowedChains returns chains that are allowed to be used in a TLS connection +// based on the current fips140tls enforcement setting. +// +// If fips140tls is not required, the chains are returned as-is with no processing. +// Otherwise, the returned chains are filtered to only those allowed by FIPS 140-3. +// If this results in no chains it returns an error. +func fipsAllowedChains(chains [][]*x509.Certificate) ([][]*x509.Certificate, error) { + if !fips140tls.Required() { + return chains, nil + } + + permittedChains := make([][]*x509.Certificate, 0, len(chains)) + for _, chain := range chains { + if fipsAllowChain(chain) { + permittedChains = append(permittedChains, chain) + } + } + + if len(permittedChains) == 0 { + return nil, errors.New("tls: no FIPS compatible certificate chains found") + } + + return permittedChains, nil +} + +func fipsAllowChain(chain []*x509.Certificate) bool { + if len(chain) == 0 { + return false + } + + for _, cert := range chain { + if !fipsAllowCert(cert) { + return false + } + } + + return true +} + +func fipsAllowCert(c *x509.Certificate) bool { + // The key must be RSA 2048, RSA 3072, RSA 4096, + // or ECDSA P-256, P-384, P-521. + switch k := c.PublicKey.(type) { + case *rsa.PublicKey: + size := k.N.BitLen() + return size == 2048 || size == 3072 || size == 4096 + case *ecdsa.PublicKey: + return k.Curve == elliptic.P256() || k.Curve == elliptic.P384() || k.Curve == elliptic.P521() + } + + return false +} diff --git a/common_string.go b/common_string.go index 1752f81..e15dd48 100644 --- a/common_string.go +++ b/common_string.go @@ -71,13 +71,13 @@ func _() { _ = x[CurveP384-24] _ = x[CurveP521-25] _ = x[X25519-29] - _ = x[x25519Kyber768Draft00-25497] + _ = x[X25519MLKEM768-4588] } const ( _CurveID_name_0 = "CurveP256CurveP384CurveP521" _CurveID_name_1 = "X25519" - _CurveID_name_2 = "X25519Kyber768Draft00" + _CurveID_name_2 = "X25519MLKEM768" ) var ( @@ -91,7 +91,7 @@ func (i CurveID) String() string { return _CurveID_name_0[_CurveID_index_0[i]:_CurveID_index_0[i+1]] case i == 29: return _CurveID_name_1 - case i == 25497: + case i == 4588: return _CurveID_name_2 default: return "CurveID(" + strconv.FormatInt(int64(i), 10) + ")" diff --git a/conn_test.go b/conn_test.go index 5e090a0..5fd48d6 100644 --- a/conn_test.go +++ b/conn_test.go @@ -230,6 +230,8 @@ func runDynamicRecordSizingTest(t *testing.T, config *Config) { } func TestDynamicRecordSizingWithStreamCipher(t *testing.T) { + skipFIPS(t) // No RC4 in FIPS mode. + config := testConfig.Clone() config.MaxVersion = VersionTLS12 config.CipherSuites = []uint16{TLS_RSA_WITH_RC4_128_SHA} @@ -237,6 +239,8 @@ func TestDynamicRecordSizingWithStreamCipher(t *testing.T) { } func TestDynamicRecordSizingWithCBC(t *testing.T) { + skipFIPS(t) // No CBC cipher suites in defaultCipherSuitesFIPS. + config := testConfig.Clone() config.MaxVersion = VersionTLS12 config.CipherSuites = []uint16{TLS_RSA_WITH_AES_256_CBC_SHA} diff --git a/defaults.go b/defaults.go index 3b2fafe..f36b6c4 100644 --- a/defaults.go +++ b/defaults.go @@ -12,17 +12,18 @@ import ( // Defaults are collected in this file to allow distributions to more easily patch // them to apply local policies. -// var tlskyber = godebug.New("tlskyber") [uTLS] +// var tlsmlkem = godebug.New("tlsmlkem") [uTLS] +// defaultCurvePreferences is the default set of supported key exchanges, as +// well as the preference order. func defaultCurvePreferences() []CurveID { // [uTLS section begins] - // if tlskyber.Value() == "0" { + // if tlsmlkem.Value() == "0" { // return []CurveID{X25519, CurveP256, CurveP384, CurveP521} // } // [uTLS section ends] - // For now, x25519Kyber768Draft00 must always be followed by X25519. - return []CurveID{x25519Kyber768Draft00, X25519, CurveP256, CurveP384, CurveP521} + return []CurveID{X25519MLKEM768, X25519, CurveP256, CurveP384, CurveP521} } // defaultSupportedSignatureAlgorithms contains the signature and hash algorithms that @@ -98,13 +99,18 @@ var defaultCipherSuitesTLS13NoAES = []uint16{ TLS_AES_256_GCM_SHA384, } +// The FIPS-only policies below match BoringSSL's +// ssl_compliance_policy_fips_202205, which is based on NIST SP 800-52r2. +// https://cs.opensource.google/boringssl/boringssl/+/master:ssl/ssl_lib.cc;l=3289;drc=ea7a88fa + var defaultSupportedVersionsFIPS = []uint16{ VersionTLS12, + VersionTLS13, } // defaultCurvePreferencesFIPS are the FIPS-allowed curves, // in preference order (most preferable first). -var defaultCurvePreferencesFIPS = []CurveID{CurveP256, CurveP384, CurveP521} +var defaultCurvePreferencesFIPS = []CurveID{CurveP256, CurveP384} // defaultSupportedSignatureAlgorithmsFIPS currently are a subset of // defaultSupportedSignatureAlgorithms without Ed25519 and SHA-1. @@ -117,7 +123,6 @@ var defaultSupportedSignatureAlgorithmsFIPS = []SignatureScheme{ PKCS1WithSHA384, ECDSAWithP384AndSHA384, PKCS1WithSHA512, - ECDSAWithP521AndSHA512, } // defaultCipherSuitesFIPS are the FIPS-allowed cipher suites. @@ -126,8 +131,6 @@ var defaultCipherSuitesFIPS = []uint16{ 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. diff --git a/ech.go b/ech.go index fa4f6fe..4f59e07 100644 --- a/ech.go +++ b/ech.go @@ -5,7 +5,10 @@ package tls import ( + "bytes" "errors" + "fmt" + "slices" "strings" "github.com/refraction-networking/utls/internal/hpke" @@ -13,6 +16,18 @@ import ( "golang.org/x/crypto/cryptobyte" ) +// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS. +// We need this so that when we insert them into ECHConfigs the ordering +// is stable. +var sortedSupportedAEADs []uint16 + +func init() { + for aeadID := range hpke.SupportedAEADs { + sortedSupportedAEADs = append(sortedSupportedAEADs, aeadID) + } + slices.Sort(sortedSupportedAEADs) +} + type echCipher struct { KDFID uint16 AEADID uint16 @@ -41,12 +56,77 @@ type echConfig struct { var errMalformedECHConfig = errors.New("tls: malformed ECHConfigList") +func parseECHConfig(enc []byte) (skip bool, ec echConfig, err error) { + s := cryptobyte.String(enc) + ec.raw = []byte(enc) + if !s.ReadUint16(&ec.Version) { + return false, echConfig{}, errMalformedECHConfig + } + if !s.ReadUint16(&ec.Length) { + return false, echConfig{}, errMalformedECHConfig + } + if len(ec.raw) < int(ec.Length)+4 { + return false, echConfig{}, errMalformedECHConfig + } + ec.raw = ec.raw[:ec.Length+4] + if ec.Version != extensionEncryptedClientHello { + s.Skip(int(ec.Length)) + return true, echConfig{}, nil + } + if !s.ReadUint8(&ec.ConfigID) { + return false, echConfig{}, errMalformedECHConfig + } + if !s.ReadUint16(&ec.KemID) { + return false, echConfig{}, errMalformedECHConfig + } + if !readUint16LengthPrefixed(&s, &ec.PublicKey) { + return false, echConfig{}, errMalformedECHConfig + } + var cipherSuites cryptobyte.String + if !s.ReadUint16LengthPrefixed(&cipherSuites) { + return false, echConfig{}, errMalformedECHConfig + } + for !cipherSuites.Empty() { + var c echCipher + if !cipherSuites.ReadUint16(&c.KDFID) { + return false, echConfig{}, errMalformedECHConfig + } + if !cipherSuites.ReadUint16(&c.AEADID) { + return false, echConfig{}, errMalformedECHConfig + } + ec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c) + } + if !s.ReadUint8(&ec.MaxNameLength) { + return false, echConfig{}, errMalformedECHConfig + } + var publicName cryptobyte.String + if !s.ReadUint8LengthPrefixed(&publicName) { + return false, echConfig{}, errMalformedECHConfig + } + ec.PublicName = publicName + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) { + return false, echConfig{}, errMalformedECHConfig + } + for !extensions.Empty() { + var e echExtension + if !extensions.ReadUint16(&e.Type) { + return false, echConfig{}, errMalformedECHConfig + } + if !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) { + return false, echConfig{}, errMalformedECHConfig + } + ec.Extensions = append(ec.Extensions, e) + } + + return false, ec, nil +} + // parseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a // slice of parsed ECHConfigs, in the same order they were parsed, or an error // if the list is malformed. func parseECHConfigList(data []byte) ([]echConfig, error) { s := cryptobyte.String(data) - // Skip the length prefix var length uint16 if !s.ReadUint16(&length) { return nil, errMalformedECHConfig @@ -56,69 +136,18 @@ func parseECHConfigList(data []byte) ([]echConfig, error) { } var configs []echConfig for len(s) > 0 { - var ec echConfig - ec.raw = []byte(s) - if !s.ReadUint16(&ec.Version) { - return nil, errMalformedECHConfig + if len(s) < 4 { + return nil, errors.New("tls: malformed ECHConfig") } - if !s.ReadUint16(&ec.Length) { - return nil, errMalformedECHConfig + configLen := uint16(s[2])<<8 | uint16(s[3]) + skip, ec, err := parseECHConfig(s) + if err != nil { + return nil, err } - if len(ec.raw) < int(ec.Length)+4 { - return nil, errMalformedECHConfig + s = s[configLen+4:] + if !skip { + configs = append(configs, ec) } - 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 } @@ -196,6 +225,175 @@ func encodeInnerClientHello(inner *clientHelloMsg, maxNameLength int) ([]byte, e return append(h, make([]byte, paddingLen)...), nil } +func skipUint8LengthPrefixed(s *cryptobyte.String) bool { + var skip uint8 + if !s.ReadUint8(&skip) { + return false + } + return s.Skip(int(skip)) +} + +func skipUint16LengthPrefixed(s *cryptobyte.String) bool { + var skip uint16 + if !s.ReadUint16(&skip) { + return false + } + return s.Skip(int(skip)) +} + +type rawExtension struct { + extType uint16 + data []byte +} + +func extractRawExtensions(hello *clientHelloMsg) ([]rawExtension, error) { + s := cryptobyte.String(hello.original) + if !s.Skip(4+2+32) || // header, version, random + !skipUint8LengthPrefixed(&s) || // session ID + !skipUint16LengthPrefixed(&s) || // cipher suites + !skipUint8LengthPrefixed(&s) { // compression methods + return nil, errors.New("tls: malformed outer client hello") + } + var rawExtensions []rawExtension + var extensions cryptobyte.String + if !s.ReadUint16LengthPrefixed(&extensions) { + return nil, errors.New("tls: malformed outer client hello") + } + + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + return nil, errors.New("tls: invalid inner client hello") + } + rawExtensions = append(rawExtensions, rawExtension{extension, extData}) + } + return rawExtensions, nil +} + +func decodeInnerClientHello(outer *clientHelloMsg, encoded []byte) (*clientHelloMsg, error) { + // Reconstructing the inner client hello from its encoded form is somewhat + // complicated. It is missing its header (message type and length), session + // ID, and the extensions may be compressed. Since we need to put the + // extensions back in the same order as they were in the raw outer hello, + // and since we don't store the raw extensions, or the order we parsed them + // in, we need to reparse the raw extensions from the outer hello in order + // to properly insert them into the inner hello. This _should_ result in raw + // bytes which match the hello as it was generated by the client. + innerReader := cryptobyte.String(encoded) + var versionAndRandom, sessionID, cipherSuites, compressionMethods []byte + var extensions cryptobyte.String + if !innerReader.ReadBytes(&versionAndRandom, 2+32) || + !readUint8LengthPrefixed(&innerReader, &sessionID) || + len(sessionID) != 0 || + !readUint16LengthPrefixed(&innerReader, &cipherSuites) || + !readUint8LengthPrefixed(&innerReader, &compressionMethods) || + !innerReader.ReadUint16LengthPrefixed(&extensions) { + return nil, errors.New("tls: invalid inner client hello") + } + + // The specification says we must verify that the trailing padding is all + // zeros. This is kind of weird for TLS messages, where we generally just + // throw away any trailing garbage. + for _, p := range innerReader { + if p != 0 { + return nil, errors.New("tls: invalid inner client hello") + } + } + + rawOuterExts, err := extractRawExtensions(outer) + if err != nil { + return nil, err + } + + recon := cryptobyte.NewBuilder(nil) + recon.AddUint8(typeClientHello) + recon.AddUint24LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(versionAndRandom) + recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(outer.sessionId) + }) + recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(cipherSuites) + }) + recon.AddUint8LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(compressionMethods) + }) + recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) { + for !extensions.Empty() { + var extension uint16 + var extData cryptobyte.String + if !extensions.ReadUint16(&extension) || + !extensions.ReadUint16LengthPrefixed(&extData) { + recon.SetError(errors.New("tls: invalid inner client hello")) + return + } + if extension == extensionECHOuterExtensions { + if !extData.ReadUint8LengthPrefixed(&extData) { + recon.SetError(errors.New("tls: invalid inner client hello")) + return + } + var i int + for !extData.Empty() { + var extType uint16 + if !extData.ReadUint16(&extType) { + recon.SetError(errors.New("tls: invalid inner client hello")) + return + } + if extType == extensionEncryptedClientHello { + recon.SetError(errors.New("tls: invalid outer extensions")) + return + } + for ; i <= len(rawOuterExts); i++ { + if i == len(rawOuterExts) { + recon.SetError(errors.New("tls: invalid outer extensions")) + return + } + if rawOuterExts[i].extType == extType { + break + } + } + recon.AddUint16(rawOuterExts[i].extType) + recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(rawOuterExts[i].data) + }) + } + } else { + recon.AddUint16(extension) + recon.AddUint16LengthPrefixed(func(recon *cryptobyte.Builder) { + recon.AddBytes(extData) + }) + } + } + }) + }) + + reconBytes, err := recon.Bytes() + if err != nil { + return nil, err + } + inner := &clientHelloMsg{} + if !inner.unmarshal(reconBytes) { + return nil, errors.New("tls: invalid reconstructed inner client hello") + } + + if !bytes.Equal(inner.encryptedClientHello, []byte{uint8(innerECHExt)}) { + return nil, errInvalidECHExt + } + + if len(inner.supportedVersions) != 1 || (len(inner.supportedVersions) >= 1 && inner.supportedVersions[0] != VersionTLS13) { + return nil, errors.New("tls: client sent encrypted_client_hello extension and offered incompatible versions") + } + + return inner, nil +} + +func decryptECHPayload(context *hpke.Receipient, hello, payload []byte) ([]byte, error) { + outerAAD := bytes.Replace(hello[4:], payload, make([]byte, len(payload)), 1) + return context.Open(outerAAD, payload) +} + func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payload []byte) ([]byte, error) { var b cryptobyte.Builder b.AddUint8(0) // outer @@ -207,7 +405,7 @@ func generateOuterECHExt(id uint8, kdfID, aeadID uint16, encodedKey []byte, payl return b.Bytes() } -func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echContext, useKey bool) error { +func computeAndUpdateOuterECHExtension(outer, inner *clientHelloMsg, ech *echClientContext, useKey bool) error { var encapKey []byte if useKey { encapKey = ech.encapsulatedKey @@ -282,3 +480,159 @@ type ECHRejectionError struct { func (e *ECHRejectionError) Error() string { return "tls: server rejected ECH" } + +var errMalformedECHExt = errors.New("tls: malformed encrypted_client_hello extension") +var errInvalidECHExt = errors.New("tls: client sent invalid encrypted_client_hello extension") + +type echExtType uint8 + +const ( + innerECHExt echExtType = 1 + outerECHExt echExtType = 0 +) + +func parseECHExt(ext []byte) (echType echExtType, cs echCipher, configID uint8, encap []byte, payload []byte, err error) { + data := make([]byte, len(ext)) + copy(data, ext) + s := cryptobyte.String(data) + var echInt uint8 + if !s.ReadUint8(&echInt) { + err = errMalformedECHExt + return + } + echType = echExtType(echInt) + if echType == innerECHExt { + if !s.Empty() { + err = errMalformedECHExt + return + } + return echType, cs, 0, nil, nil, nil + } + if echType != outerECHExt { + err = errInvalidECHExt + return + } + if !s.ReadUint16(&cs.KDFID) { + err = errMalformedECHExt + return + } + if !s.ReadUint16(&cs.AEADID) { + err = errMalformedECHExt + return + } + if !s.ReadUint8(&configID) { + err = errMalformedECHExt + return + } + if !readUint16LengthPrefixed(&s, &encap) { + err = errMalformedECHExt + return + } + if !readUint16LengthPrefixed(&s, &payload) { + err = errMalformedECHExt + return + } + + // NOTE: clone encap and payload so that mutating them does not mutate the + // raw extension bytes. + return echType, cs, configID, bytes.Clone(encap), bytes.Clone(payload), nil +} + +func marshalEncryptedClientHelloConfigList(configs []EncryptedClientHelloKey) ([]byte, error) { + builder := cryptobyte.NewBuilder(nil) + builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { + for _, c := range configs { + builder.AddBytes(c.Config) + } + }) + return builder.Bytes() +} + +func (c *Conn) processECHClientHello(outer *clientHelloMsg) (*clientHelloMsg, *echServerContext, error) { + echType, echCiphersuite, configID, encap, payload, err := parseECHExt(outer.encryptedClientHello) + if err != nil { + if errors.Is(err, errInvalidECHExt) { + c.sendAlert(alertIllegalParameter) + } else { + c.sendAlert(alertDecodeError) + } + + return nil, nil, errInvalidECHExt + } + + if echType == innerECHExt { + return outer, &echServerContext{inner: true}, nil + } + + if len(c.config.EncryptedClientHelloKeys) == 0 { + return outer, nil, nil + } + + for _, echKey := range c.config.EncryptedClientHelloKeys { + skip, config, err := parseECHConfig(echKey.Config) + if err != nil || skip { + c.sendAlert(alertInternalError) + return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys Config: %s", err) + } + if skip { + continue + } + echPriv, err := hpke.ParseHPKEPrivateKey(config.KemID, echKey.PrivateKey) + if err != nil { + c.sendAlert(alertInternalError) + return nil, nil, fmt.Errorf("tls: invalid EncryptedClientHelloKeys PrivateKey: %s", err) + } + info := append([]byte("tls ech\x00"), echKey.Config...) + hpkeContext, err := hpke.SetupReceipient(hpke.DHKEM_X25519_HKDF_SHA256, echCiphersuite.KDFID, echCiphersuite.AEADID, echPriv, info, encap) + if err != nil { + // attempt next trial decryption + continue + } + + encodedInner, err := decryptECHPayload(hpkeContext, outer.original, payload) + if err != nil { + // attempt next trial decryption + continue + } + + // NOTE: we do not enforce that the sent server_name matches the ECH + // configs PublicName, since this is not particularly important, and + // the client already had to know what it was in order to properly + // encrypt the payload. This is only a MAY in the spec, so we're not + // doing anything revolutionary. + + echInner, err := decodeInnerClientHello(outer, encodedInner) + if err != nil { + c.sendAlert(alertIllegalParameter) + return nil, nil, errInvalidECHExt + } + + c.echAccepted = true + + return echInner, &echServerContext{ + hpkeContext: hpkeContext, + configID: configID, + ciphersuite: echCiphersuite, + }, nil + } + + return outer, nil, nil +} + +func buildRetryConfigList(keys []EncryptedClientHelloKey) ([]byte, error) { + var atLeastOneRetryConfig bool + var retryBuilder cryptobyte.Builder + retryBuilder.AddUint16LengthPrefixed(func(b *cryptobyte.Builder) { + for _, c := range keys { + if !c.SendAsRetry { + continue + } + atLeastOneRetryConfig = true + b.AddBytes(c.Config) + } + }) + if !atLeastOneRetryConfig { + return nil, nil + } + return retryBuilder.Bytes() +} diff --git a/fipsonly/fipsonly.go b/fipsonly/fipsonly.go index e5e4783..e702f44 100644 --- a/fipsonly/fipsonly.go +++ b/fipsonly/fipsonly.go @@ -19,11 +19,11 @@ package fipsonly // new source file and not modifying any existing source files. import ( - "crypto/internal/boring/fipstls" "crypto/internal/boring/sig" + "crypto/tls/internal/fips140tls" ) func init() { - fipstls.Force() + fips140tls.Force() sig.FIPSOnly() } diff --git a/fipsonly/fipsonly_test.go b/fipsonly/fipsonly_test.go index f8485dc..027bc22 100644 --- a/fipsonly/fipsonly_test.go +++ b/fipsonly/fipsonly_test.go @@ -7,12 +7,12 @@ package fipsonly import ( - "crypto/internal/boring/fipstls" + "crypto/tls/internal/fips140tls" "testing" ) func Test(t *testing.T) { - if !fipstls.Required() { - t.Fatal("fipstls.Required() = false, must be true") + if !fips140tls.Required() { + t.Fatal("fips140tls.Required() = false, must be true") } } diff --git a/handshake_client.go b/handshake_client.go index b72d503..f523c6a 100644 --- a/handshake_client.go +++ b/handshake_client.go @@ -10,6 +10,7 @@ import ( "crypto" "crypto/ecdsa" "crypto/ed25519" + "crypto/mlkem" "crypto/rsa" "crypto/subtle" "crypto/x509" @@ -18,14 +19,14 @@ import ( "hash" "io" "net" + "slices" "strings" "time" - "github.com/refraction-networking/utls/internal/hpke" - "github.com/refraction-networking/utls/internal/byteorder" - - "github.com/refraction-networking/utls/internal/mlkem768" + "github.com/refraction-networking/utls/internal/fips140tls" + "github.com/refraction-networking/utls/internal/hpke" + "github.com/refraction-networking/utls/internal/tls13" circlSign "github.com/cloudflare/circl/sign" ) @@ -46,7 +47,7 @@ type clientHandshakeState struct { var testingOnlyForceClientHelloSignatureAlgorithms []SignatureScheme -func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echContext, error) { +func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) { config := c.config // [UTLS SECTION START] @@ -149,35 +150,44 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCon if len(hello.supportedVersions) == 1 { hello.cipherSuites = nil } - if hasAESGCMHardwareSupport { + if fips140tls.Required() { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13FIPS...) + } else if hasAESGCMHardwareSupport { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...) } else { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...) } - curveID := config.curvePreferences(maxVersion)[0] + if len(hello.supportedCurves) == 0 { + return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE") + } + curveID := hello.supportedCurves[0] keyShareKeys = &keySharePrivateKeys{curveID: curveID} - if curveID == x25519Kyber768Draft00 { + // Note that if X25519MLKEM768 is supported, it will be first because + // the preference order is fixed. + if curveID == X25519MLKEM768 { keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519) if err != nil { return nil, nil, nil, err } - seed := make([]byte, mlkem768.SeedSize) + seed := make([]byte, mlkem.SeedSize) if _, err := io.ReadFull(config.rand(), seed); err != nil { return nil, nil, nil, err } - keyShareKeys.kyber, err = mlkem768.NewKeyFromSeed(seed) + keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed) if err != nil { return nil, nil, nil, err } - // For draft-tls-westerbaan-xyber768d00-03, we send both a hybrid - // and a standard X25519 key share, since most servers will only - // support the latter. We reuse the same X25519 ephemeral key for - // both, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. + mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes() + x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes() hello.keyShares = []keyShare{ - {group: x25519Kyber768Draft00, data: append(keyShareKeys.ecdhe.PublicKey().Bytes(), - keyShareKeys.kyber.EncapsulationKey()...)}, - {group: X25519, data: keyShareKeys.ecdhe.PublicKey().Bytes()}, + {group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)}, + } + // If both X25519MLKEM768 and X25519 are supported, we send both key + // shares (as a fallback) and we reuse the same X25519 ephemeral + // key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. + if slices.Contains(hello.supportedCurves, X25519) { + hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey}) } } else { if _, ok := curveForCurveID(curveID); !ok { @@ -202,7 +212,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCon hello.quicTransportParameters = p } - var ech *echContext + var ech *echClientContext if c.config.EncryptedClientHelloConfigList != nil { if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 { return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated") @@ -218,7 +228,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCon if echConfig == nil { return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs") } - ech = &echContext{config: echConfig} + ech = &echClientContext{config: echConfig} hello.encryptedClientHello = []byte{1} // indicate inner hello // We need to explicitly set these 1.2 fields to nil, as we do not // marshal them when encoding the inner hello, otherwise transcripts @@ -247,7 +257,7 @@ func (c *Conn) makeClientHello() (*clientHelloMsg, *keySharePrivateKeys, *echCon return hello, keyShareKeys, ech, nil } -type echContext struct { +type echClientContext struct { config *echConfig hpkeContext *hpke.Sender encapsulatedKey []byte @@ -256,6 +266,7 @@ type echContext struct { kdfID uint16 aeadID uint16 echRejected bool + retryConfigs []byte } func (c *Conn) clientHandshake(ctx context.Context) (err error) { @@ -327,7 +338,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { if err := transcriptMsg(hello, transcript); err != nil { return err } - earlyTrafficSecret := suite.deriveSecret(earlySecret, clientEarlyTrafficLabel, transcript) + earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript) c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret) } @@ -387,7 +398,7 @@ func (c *Conn) clientHandshake(ctx context.Context) (err error) { } func (c *Conn) loadSession(hello *clientHelloMsg) ( - session *SessionState, earlySecret, binderKey []byte, err error) { + session *SessionState, earlySecret *tls13.EarlySecret, binderKey []byte, err error) { // [UTLS SECTION START] if c.utls.sessionController != nil { c.utls.sessionController.onEnterLoadSessionCheck() @@ -534,8 +545,8 @@ func (c *Conn) loadSession(hello *clientHelloMsg) ( hello.pskBinders = [][]byte{make([]byte, cipherSuite.hash.Size())} // Compute the PSK binders. See RFC 8446, Section 4.2.11.2. - earlySecret = cipherSuite.extract(session.secret, nil) - binderKey = cipherSuite.deriveSecret(earlySecret, resumptionBinderLabel, nil) + earlySecret = tls13.NewEarlySecret(cipherSuite.hash.New, session.secret) + binderKey = earlySecret.ResumptionBinderKey() // [UTLS SECTION START] if c.utls.sessionController != nil && !c.utls.sessionController.shouldLoadSessionWriteBinders() { return @@ -662,11 +673,11 @@ func (hs *clientHandshakeState) pickCipherSuite() error { } // [UTLS SECTION START] - // if hs.c.config.CipherSuites == nil && !needFIPS() && rsaKexCiphers[hs.suite.id] { + // if hs.c.config.CipherSuites == nil && !fips140tls.Required() && rsaKexCiphers[hs.suite.id] { // tlsrsakex.Value() // ensure godebug is initialized // tlsrsakex.IncNonDefault() // } - // if hs.c.config.CipherSuites == nil && !needFIPS() && tdesCiphers[hs.suite.id] { + // if hs.c.config.CipherSuites == nil && !fips140tls.Required() && tdesCiphers[hs.suite.id] { // tls3des.Value() // ensure godebug is initialized // tls3des.IncNonDefault() // } @@ -741,11 +752,11 @@ func (hs *clientHandshakeState) doFullHandshake() error { if ok { err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) if err != nil { - c.sendAlert(alertUnexpectedMessage) + c.sendAlert(alertIllegalParameter) return err } if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ { - c.curveID = CurveID(byteorder.BeUint16(skx.key[1:])) + c.curveID = CurveID(byteorder.BEUint16(skx.key[1:])) } msg, err = c.readHandshake(&hs.finishedHash) @@ -969,7 +980,7 @@ func (hs *clientHandshakeState) processServerHello() (bool, error) { } // checkALPN ensure that the server's choice of ALPN protocol is compatible with -// the protocols that we advertised in the Client Hello. +// the protocols that we advertised in the ClientHello. func checkALPN(clientProtos []string, serverProto string, quic bool) error { if serverProto == "" { if quic && len(clientProtos) > 0 { @@ -1162,8 +1173,13 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { for _, cert := range certs[1:] { opts.Intermediates.AddCert(cert) } - var err error - c.verifiedChains, err = certs[0].Verify(opts) + chains, err := certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} + } + + c.verifiedChains, err = fipsAllowedChains(chains) if err != nil { c.sendAlert(alertBadCertificate) return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} @@ -1192,8 +1208,13 @@ func (c *Conn) verifyServerCertificate(certificates [][]byte) error { for _, cert := range certs[1:] { opts.Intermediates.AddCert(cert) } - var err error - c.verifiedChains, err = certs[0].Verify(opts) + chains, err := certs[0].Verify(opts) + if err != nil { + c.sendAlert(alertBadCertificate) + return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} + } + + c.verifiedChains, err = fipsAllowedChains(chains) if err != nil { c.sendAlert(alertBadCertificate) return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} diff --git a/handshake_client_test.go b/handshake_client_test.go index 9c81748..5cb8617 100644 --- a/handshake_client_test.go +++ b/handshake_client_test.go @@ -32,6 +32,7 @@ import ( "time" "github.com/refraction-networking/utls/internal/byteorder" + "github.com/refraction-networking/utls/internal/fips140tls" ) // Note: see comment in handshake_test.go for details of how the reference @@ -208,7 +209,7 @@ func (test *clientTest) connFromCommand() (conn *recordingConn, child *exec.Cmd, var serverInfo bytes.Buffer for _, ext := range test.extensions { pem.Encode(&serverInfo, &pem.Block{ - Type: fmt.Sprintf("SERVERINFO FOR EXTENSION %d", byteorder.BeUint16(ext)), + Type: fmt.Sprintf("SERVERINFO FOR EXTENSION %d", byteorder.BEUint16(ext)), Bytes: ext, }) } @@ -434,7 +435,7 @@ func (test *clientTest) run(t *testing.T, write bool) { } if write { - clientConn.Close() + client.Close() path := test.dataPath() out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) if err != nil { @@ -849,14 +850,17 @@ func testResumption(t *testing.T, version uint16) { if testing.Short() { t.Skip("skipping in -short mode") } + + // Note: using RSA 2048 test certificates because they are compatible with FIPS mode. + testCertificates := []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} serverConfig := &Config{ MaxVersion: version, - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - Certificates: testConfig.Certificates, - Time: testTime, // [uTLS] + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, + Certificates: testCertificates, + Time: testTime, } - issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) if err != nil { panic(err) } @@ -866,11 +870,11 @@ func testResumption(t *testing.T, version uint16) { clientConfig := &Config{ MaxVersion: version, - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, ClientSessionCache: NewLRUClientSessionCache(32), RootCAs: rootCAs, ServerName: "example.golang", - Time: testTime, // [uTLS] + Time: testTime, } testResumeState := func(test string, didResume bool) { @@ -917,7 +921,7 @@ func testResumption(t *testing.T, version uint16) { // An old session ticket is replaced with a ticket encrypted with a fresh key. ticket = getTicket() - serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } // [uTLS] + serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } testResumeState("ResumeWithOldTicket", true) if bytes.Equal(ticket, getTicket()) { t.Fatal("old first ticket matches the fresh one") @@ -925,13 +929,13 @@ func testResumption(t *testing.T, version uint16) { // Once the session master secret is expired, a full handshake should occur. ticket = getTicket() - serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + time.Minute) } // [uTLS] + serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + time.Minute) } testResumeState("ResumeWithExpiredTicket", false) if bytes.Equal(ticket, getTicket()) { t.Fatal("expired first ticket matches the fresh one") } - serverConfig.Time = testTime // reset the time back // [uTLS] + serverConfig.Time = testTime // reset the time back key1 := randomKey() serverConfig.SetSessionTicketKeys([][32]byte{key1}) @@ -948,11 +952,11 @@ func testResumption(t *testing.T, version uint16) { testResumeState("KeyChangeFinish", true) // Age the session ticket a bit, but not yet expired. - serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } // [uTLS] + serverConfig.Time = func() time.Time { return testTime().Add(24*time.Hour + time.Minute) } testResumeState("OldSessionTicket", true) ticket = getTicket() // Expire the session ticket, which would force a full handshake. - serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + 2*time.Minute) } // [uTLS] + serverConfig.Time = func() time.Time { return testTime().Add(24*8*time.Hour + 2*time.Minute) } testResumeState("ExpiredSessionTicket", false) if bytes.Equal(ticket, getTicket()) { t.Fatal("new ticket wasn't provided after old ticket expired") @@ -960,7 +964,7 @@ func testResumption(t *testing.T, version uint16) { // Age the session ticket a bit at a time, but don't expire it. d := 0 * time.Hour - serverConfig.Time = func() time.Time { return testTime().Add(d) } // [uTLS] + serverConfig.Time = func() time.Time { return testTime().Add(d) } deleteTicket() testResumeState("GetFreshSessionTicket", false) for i := 0; i < 13; i++ { @@ -971,7 +975,7 @@ func testResumption(t *testing.T, version uint16) { // handshake occurs for TLS 1.2. Resumption should still occur for // TLS 1.3 since the client should be using a fresh ticket sent over // by the server. - d += 12*time.Hour + time.Minute // [uTLS] + d += 12*time.Hour + time.Minute if version == VersionTLS13 { testResumeState("ExpiredSessionTicket", true) } else { @@ -985,9 +989,9 @@ func testResumption(t *testing.T, version uint16) { // before the serverConfig is used works. serverConfig = &Config{ MaxVersion: version, - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - Certificates: testConfig.Certificates, - Time: testTime, // [uTLS] + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, + Certificates: testCertificates, + Time: testTime, } serverConfig.SetSessionTicketKeys([][32]byte{key2}) @@ -996,7 +1000,7 @@ func testResumption(t *testing.T, version uint16) { // In TLS 1.3, cross-cipher suite resumption is allowed as long as the KDF // hash matches. Also, Config.CipherSuites does not apply to TLS 1.3. if version != VersionTLS13 { - clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_RC4_128_SHA} + clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384} testResumeState("DifferentCipherSuite", false) testResumeState("DifferentCipherSuiteRecovers", true) } @@ -1012,8 +1016,8 @@ func testResumption(t *testing.T, version uint16) { // Use a different curve than the client to force a HelloRetryRequest. CurvePreferences: []CurveID{CurveP521, CurveP384, CurveP256}, MaxVersion: version, - Certificates: testConfig.Certificates, - Time: testTime, // [uTLS] + Certificates: testCertificates, + Time: testTime, } testResumeState("InitialHandshake", false) testResumeState("WithHelloRetryRequest", true) @@ -1021,9 +1025,9 @@ func testResumption(t *testing.T, version uint16) { // Reset serverConfig back. serverConfig = &Config{ MaxVersion: version, - CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA}, - Certificates: testConfig.Certificates, - Time: testTime, // [uTLS] + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384}, + Certificates: testCertificates, + Time: testTime, } } @@ -1278,7 +1282,7 @@ func TestServerSelectingUnconfiguredApplicationProtocol(t *testing.T) { go func() { client := Client(c, &Config{ ServerName: "foo", - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256}, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, NextProtos: []string{"http", "something-else"}, }) errChan <- client.Handshake() @@ -1298,7 +1302,7 @@ func TestServerSelectingUnconfiguredApplicationProtocol(t *testing.T) { serverHello := &serverHelloMsg{ vers: VersionTLS12, random: make([]byte, 32), - cipherSuite: TLS_RSA_WITH_AES_128_GCM_SHA256, + cipherSuite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, alpnProtocol: "how-about-this", } serverHelloBytes := mustMarshal(t, serverHello) @@ -1314,7 +1318,7 @@ func TestServerSelectingUnconfiguredApplicationProtocol(t *testing.T) { s.Close() if err := <-errChan; !strings.Contains(err.Error(), "server selected unadvertised ALPN protocol") { - t.Fatalf("Expected error about unconfigured cipher suite but got %q", err) + t.Fatalf("Expected error about unconfigured ALPN protocol but got %q", err) } } @@ -1730,7 +1734,10 @@ func testVerifyConnection(t *testing.T, version uint16) { }, } for _, test := range tests { - issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + // Note: using RSA 2048 test certificates because they are compatible with FIPS mode. + testCertificates := []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} + + issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) if err != nil { panic(err) } @@ -1741,8 +1748,8 @@ func testVerifyConnection(t *testing.T, version uint16) { serverConfig := &Config{ MaxVersion: version, - Certificates: []Certificate{testConfig.Certificates[0]}, - Time: testTime, // [uTLS] + Certificates: testCertificates, + Time: testTime, ClientCAs: rootCAs, NextProtos: []string{"protocol1"}, } @@ -1755,8 +1762,8 @@ func testVerifyConnection(t *testing.T, version uint16) { ClientSessionCache: NewLRUClientSessionCache(32), RootCAs: rootCAs, ServerName: "example.golang", - Certificates: []Certificate{testConfig.Certificates[0]}, - Time: testTime, // [uTLS] + Certificates: testCertificates, + Time: testTime, NextProtos: []string{"protocol1"}, } test.configureClient(clientConfig, &clientCalled) @@ -1791,7 +1798,8 @@ func TestVerifyPeerCertificate(t *testing.T) { } func testVerifyPeerCertificate(t *testing.T, version uint16) { - issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + // Note: using RSA 2048 test certificates because they are compatible with FIPS mode. + issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) if err != nil { panic(err) } @@ -1799,8 +1807,6 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { rootCAs := x509.NewCertPool() rootCAs.AddCert(issuer) - now := func() time.Time { return time.Unix(1476984729, 0) } - sentinelErr := errors.New("TestVerifyPeerCertificate") verifyPeerCertificateCallback := func(called *bool, rawCerts [][]byte, validatedChains [][]*x509.Certificate) error { @@ -2046,11 +2052,11 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { config.ServerName = "example.golang" config.ClientAuth = RequireAndVerifyClientCert config.ClientCAs = rootCAs - config.Time = now + config.Time = testTime config.MaxVersion = version config.Certificates = make([]Certificate, 1) - config.Certificates[0].Certificate = [][]byte{testRSACertificate} - config.Certificates[0].PrivateKey = testRSAPrivateKey + config.Certificates[0].Certificate = [][]byte{testRSA2048Certificate} + config.Certificates[0].PrivateKey = testRSA2048PrivateKey config.Certificates[0].SignedCertificateTimestamps = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")} config.Certificates[0].OCSPStaple = []byte("dummy ocsp") test.configureServer(config, &serverCalled) @@ -2061,9 +2067,10 @@ func testVerifyPeerCertificate(t *testing.T, version uint16) { }() config := testConfig.Clone() + config.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} config.ServerName = "example.golang" config.RootCAs = rootCAs - config.Time = now + config.Time = testTime config.MaxVersion = version test.configureClient(config, &clientCalled) clientErr := Client(c, config).Handshake() @@ -2344,8 +2351,8 @@ var getClientCertificateTests = []struct { panic("empty AcceptableCAs") } cert := &Certificate{ - Certificate: [][]byte{testRSACertificate}, - PrivateKey: testRSAPrivateKey, + Certificate: [][]byte{testRSA2048Certificate}, + PrivateKey: testRSA2048PrivateKey, } return cert, nil } @@ -2365,25 +2372,34 @@ func TestGetClientCertificate(t *testing.T) { } func testGetClientCertificate(t *testing.T, version uint16) { - issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + // Note: using RSA 2048 test certificates because they are compatible with FIPS mode. + issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) if err != nil { panic(err) } for i, test := range getClientCertificateTests { serverConfig := testConfig.Clone() + serverConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} serverConfig.ClientAuth = VerifyClientCertIfGiven serverConfig.RootCAs = x509.NewCertPool() serverConfig.RootCAs.AddCert(issuer) serverConfig.ClientCAs = serverConfig.RootCAs - serverConfig.Time = func() time.Time { return time.Unix(1476984729, 0) } + serverConfig.Time = testTime serverConfig.MaxVersion = version clientConfig := testConfig.Clone() + clientConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} clientConfig.MaxVersion = version test.setup(clientConfig, serverConfig) + // TLS 1.1 isn't available for FIPS required + if fips140tls.Required() && clientConfig.MaxVersion == VersionTLS11 { + t.Logf("skipping test %d for FIPS mode", i) + continue + } + type serverResult struct { cs ConnectionState err error @@ -2522,11 +2538,15 @@ func TestDowngradeCanary(t *testing.T) { if err := testDowngradeCanary(t, VersionTLS12, VersionTLS12); err != nil { t.Errorf("client didn't ignore expected TLS 1.2 canary") } - if err := testDowngradeCanary(t, VersionTLS11, VersionTLS11); err != nil { - t.Errorf("client unexpectedly reacted to a canary in TLS 1.1") - } - if err := testDowngradeCanary(t, VersionTLS10, VersionTLS10); err != nil { - t.Errorf("client unexpectedly reacted to a canary in TLS 1.0") + if !fips140tls.Required() { + if err := testDowngradeCanary(t, VersionTLS11, VersionTLS11); err != nil { + t.Errorf("client unexpectedly reacted to a canary in TLS 1.1") + } + if err := testDowngradeCanary(t, VersionTLS10, VersionTLS10); err != nil { + t.Errorf("client unexpectedly reacted to a canary in TLS 1.0") + } + } else { + t.Logf("skiping TLS 1.1 and TLS 1.0 downgrade canary checks in FIPS mode") } } @@ -2536,7 +2556,8 @@ func TestResumptionKeepsOCSPAndSCT(t *testing.T) { } func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) { - issuer, err := x509.ParseCertificate(testRSACertificateIssuer) + // Note: using RSA 2048 test certificates because they are compatible with FIPS mode. + issuer, err := x509.ParseCertificate(testRSA2048CertificateIssuer) if err != nil { t.Fatalf("failed to parse test issuer") } @@ -2547,9 +2568,10 @@ func testResumptionKeepsOCSPAndSCT(t *testing.T, ver uint16) { ClientSessionCache: NewLRUClientSessionCache(32), ServerName: "example.golang", RootCAs: roots, - Time: testTime, // [uTLS] + Time: testTime, } serverConfig := testConfig.Clone() + serverConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} serverConfig.MaxVersion = ver serverConfig.Certificates[0].OCSPStaple = []byte{1, 2, 3} serverConfig.Certificates[0].SignedCertificateTimestamps = [][]byte{{4, 5, 6}} @@ -2684,11 +2706,15 @@ func testTLS13OnlyClientHelloCipherSuite(t *testing.T, ciphers []uint16) { serverConfig := &Config{ Certificates: testConfig.Certificates, GetConfigForClient: func(chi *ClientHelloInfo) (*Config, error) { - if len(chi.CipherSuites) != len(defaultCipherSuitesTLS13NoAES) { + expectedCiphersuites := defaultCipherSuitesTLS13NoAES + if fips140tls.Required() { + expectedCiphersuites = defaultCipherSuitesTLS13FIPS + } + if len(chi.CipherSuites) != len(expectedCiphersuites) { t.Errorf("only TLS 1.3 suites should be advertised, got=%x", chi.CipherSuites) } else { - for i := range defaultCipherSuitesTLS13NoAES { - if want, got := defaultCipherSuitesTLS13NoAES[i], chi.CipherSuites[i]; want != got { + for i := range expectedCiphersuites { + if want, got := expectedCiphersuites[i], chi.CipherSuites[i]; want != got { t.Errorf("cipher at index %d does not match, want=%x, got=%x", i, want, got) } } diff --git a/handshake_client_tls13.go b/handshake_client_tls13.go index ffe71f1..53b5eba 100644 --- a/handshake_client_tls13.go +++ b/handshake_client_tls13.go @@ -10,6 +10,7 @@ import ( "crypto" "crypto/ecdh" "crypto/hmac" + "crypto/mlkem" "crypto/rsa" "crypto/subtle" "errors" @@ -18,9 +19,9 @@ import ( "slices" "time" - "github.com/refraction-networking/utls/internal/mlkem768" - "github.com/cloudflare/circl/kem" + "github.com/refraction-networking/utls/internal/hkdf" + "github.com/refraction-networking/utls/internal/tls13" ) // [uTLS SECTION START] @@ -61,10 +62,10 @@ func (ksp *KeySharesParameters) GetEcdhePubkey(curveID CurveID) (params *ecdh.Pu } func (ksp *KeySharesParameters) AddKemKeypair(curveID CurveID, kemKey kem.PrivateKey, kemPubKey kem.PublicKey) { - if curveID == x25519Kyber768Draft00 { // only store for x25519Kyber768Draft00 - ksp.kemPrivKeymap[curveID] = kemKey - ksp.kemPubKeymap[curveID] = kemPubKey - } + // if curveID == x25519Kyber768Draft00 { // only store for x25519Kyber768Draft00 + // ksp.kemPrivKeymap[curveID] = kemKey + // ksp.kemPubKeymap[curveID] = kemPubKey + // } } func (ksp *KeySharesParameters) GetKemKey(curveID CurveID) (kemKey kem.PrivateKey, ok bool) { @@ -88,7 +89,7 @@ type clientHandshakeStateTLS13 struct { keySharesParams *KeySharesParameters // [uTLS] support both ecdhe and kem session *SessionState - earlySecret []byte + earlySecret *tls13.EarlySecret binderKey []byte certReq *certificateRequestMsgTLS13 @@ -96,10 +97,10 @@ type clientHandshakeStateTLS13 struct { sentDummyCCS bool suite *cipherSuiteTLS13 transcript hash.Hash - masterSecret []byte + masterSecret *tls13.MasterSecret trafficSecret []byte // client_application_traffic_secret_0 - echContext *echContext + echContext *echClientContext uconn *UConn // [uTLS] } @@ -109,10 +110,6 @@ type clientHandshakeStateTLS13 struct { 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 { @@ -121,21 +118,21 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } // [uTLS SECTION START] - if hs.keyShareKeys == nil { - // set echdheParams to what we received from server - if ecdheKey, ok := hs.keySharesParams.GetEcdheKey(hs.serverHello.serverShare.group); ok { - hs.keyShareKeys.ecdhe = ecdheKey - hs.keyShareKeys.curveID = hs.serverHello.serverShare.group - } - // set kemParams to what we received from server - if kemKey, ok := hs.keySharesParams.GetKemKey(hs.serverHello.serverShare.group); ok { - if kyberKey, ecdhKey, err := mlkemCirclToGo(kemKey); err == nil { - hs.keyShareKeys.kyber = kyberKey - hs.keyShareKeys.ecdhe = ecdhKey - hs.keyShareKeys.curveID = hs.serverHello.serverShare.group - } - } - } + // if hs.keyShareKeys == nil { + // // set echdheParams to what we received from server + // if ecdheKey, ok := hs.keySharesParams.GetEcdheKey(hs.serverHello.serverShare.group); ok { + // hs.keyShareKeys.ecdhe = ecdheKey + // hs.keyShareKeys.curveID = hs.serverHello.serverShare.group + // } + // // set kemParams to what we received from server + // if kemKey, ok := hs.keySharesParams.GetKemKey(hs.serverHello.serverShare.group); ok { + // if kyberKey, ecdhKey, err := mlkemCirclToGo(kemKey); err == nil { + // hs.keyShareKeys.kyber = kyberKey + // hs.keyShareKeys.ecdhe = ecdhKey + // hs.keyShareKeys.curveID = hs.serverHello.serverShare.group + // } + // } + // } // [uTLS SECTION END] // Consistency check on the presence of a keyShare and its parameters. @@ -169,14 +166,13 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } } - 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), + acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New, + hkdf.Extract(hs.suite.hash.New, hs.echContext.innerHello.random, nil), "ech accept confirmation", confTranscript.Sum(nil), 8, @@ -189,7 +185,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if hs.serverHello.encryptedClientHello != nil { c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: unexpected encrypted_client_hello extension in server hello despite ECH being accepted") + return errors.New("tls: unexpected encrypted client hello extension in server hello despite ECH being accepted") } if hs.hello.serverName == "" && hs.serverHello.serverNameAck { @@ -198,9 +194,6 @@ func (hs *clientHandshakeStateTLS13) handshake() error { } } 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 } } @@ -244,7 +237,7 @@ func (hs *clientHandshakeStateTLS13) handshake() error { if hs.echContext != nil && hs.echContext.echRejected { c.sendAlert(alertECHRequired) - return &ECHRejectionError{echRetryConfigList} + return &ECHRejectionError{hs.echContext.retryConfigs} } c.isHandshakeComplete.Store(true) @@ -357,8 +350,8 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { 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), + acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New, + hkdf.Extract(hs.suite.hash.New, hs.echContext.innerHello.random, nil), "hrr ech accept confirmation", confTranscript.Sum(nil), 8, @@ -377,7 +370,7 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { } else if hs.serverHello.encryptedClientHello != nil { // Unsolicited ECH extension should be rejected c.sendAlert(alertUnsupportedExtension) - return errors.New("tls: unexpected ECH extension in serverHello") + return errors.New("tls: unexpected encrypted client hello extension in serverHello") } // The only HelloRetryRequest extensions we support are key_share and @@ -411,12 +404,11 @@ func (hs *clientHandshakeStateTLS13) processHelloRetryRequest() error { 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. + // Note: we don't support selecting X25519MLKEM768 in a HRR, because it + // is currently first in preference order, so if it's enabled we'll + // always send a key share for it. // - // This will have to change once we support selecting hybrid KEMs - // without sending key shares for them. + // This will have to change once we support multiple hybrid KEMs. if _, ok := curveForCurveID(curveID); !ok { c.sendAlert(alertInternalError) return errors.New("tls: CurvePreferences includes unsupported curve") @@ -630,12 +622,12 @@ 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 { + if hs.serverHello.serverShare.group == X25519MLKEM768 { + if len(ecdhePeerData) != mlkem.CiphertextSize768+x25519PublicKeySize { c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid server key share") + return errors.New("tls: invalid server X25519MLKEM768 key share") } - ecdhePeerData = hs.serverHello.serverShare.data[:x25519PublicKeySize] + ecdhePeerData = hs.serverHello.serverShare.data[mlkem.CiphertextSize768:] } peerKey, err := hs.keyShareKeys.ecdhe.Curve().NewPublicKey(ecdhePeerData) if err != nil { @@ -647,33 +639,30 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid server key share") } - if hs.serverHello.serverShare.group == x25519Kyber768Draft00 { - if hs.keyShareKeys.kyber == nil { + if hs.serverHello.serverShare.group == X25519MLKEM768 { + if hs.keyShareKeys.mlkem == nil { return c.sendAlert(alertInternalError) } - ciphertext := hs.serverHello.serverShare.data[x25519PublicKeySize:] - kyberShared, err := kyberDecapsulate(hs.keyShareKeys.kyber, ciphertext) + ciphertext := hs.serverHello.serverShare.data[:mlkem.CiphertextSize768] + mlkemShared, err := hs.keyShareKeys.mlkem.Decapsulate(ciphertext) if err != nil { c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid Kyber server key share") + return errors.New("tls: invalid X25519MLKEM768 server key share") } - sharedKey = append(sharedKey, kyberShared...) + sharedKey = append(mlkemShared, sharedKey...) } c.curveID = hs.serverHello.serverShare.group earlySecret := hs.earlySecret if !hs.usingPSK { - earlySecret = hs.suite.extract(nil, nil) + earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, nil) } - handshakeSecret := hs.suite.extract(sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) + handshakeSecret := earlySecret.HandshakeSecret(sharedKey) - clientSecret := hs.suite.deriveSecret(handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) + clientSecret := handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript) c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret) - serverSecret := hs.suite.deriveSecret(handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) + serverSecret := handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript) c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret) if c.quic != nil { @@ -695,8 +684,7 @@ func (hs *clientHandshakeStateTLS13) establishHandshakeKeys() error { return err } - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(handshakeSecret, "derived", nil)) + hs.masterSecret = handshakeSecret.MasterSecret() return nil } @@ -766,9 +754,13 @@ func (hs *clientHandshakeStateTLS13) readServerParameters() error { 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") + if hs.echContext != nil { + if hs.echContext.echRejected { + hs.echContext.retryConfigs = encryptedExtensions.echRetryConfigs + } else if encryptedExtensions.echRetryConfigs != nil { + c.sendAlert(alertUnsupportedExtension) + return errors.New("tls: server sent encrypted client hello retry configs after accepting encrypted client hello") + } } return nil @@ -920,10 +912,8 @@ func (hs *clientHandshakeStateTLS13) readServerFinished() error { // 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) + hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript) + serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript) c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret) err = c.config.writeKeyLog(keyLogLabelClientTraffic, hs.hello.random, hs.trafficSecret) @@ -1030,8 +1020,7 @@ func (hs *clientHandshakeStateTLS13) sendClientFinished() error { 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) + c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript) } if c.quic != nil { @@ -1075,7 +1064,7 @@ func (c *Conn) handleNewSessionTicket(msg *newSessionTicketMsgTLS13) error { return c.sendAlert(alertInternalError) } - psk := cipherSuite.expandLabel(c.resumptionSecret, "resumption", + psk := tls13.ExpandLabel(cipherSuite.hash.New, c.resumptionSecret, "resumption", msg.nonce, cipherSuite.hash.Size()) session := c.sessionState() diff --git a/handshake_messages.go b/handshake_messages.go index da74d1e..34f70d6 100644 --- a/handshake_messages.go +++ b/handshake_messages.go @@ -98,6 +98,8 @@ type clientHelloMsg struct { pskBinders [][]byte quicTransportParameters []byte encryptedClientHello []byte + // extensions are only populated on the server-side of a handshake + extensions []uint16 // [uTLS] nextProtoNeg bool @@ -477,6 +479,7 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { return false } seenExts[extension] = true + m.extensions = append(m.extensions, extension) switch extension { case extensionServerName: @@ -669,6 +672,10 @@ func (m *clientHelloMsg) unmarshal(data []byte) bool { } m.pskBinders = append(m.pskBinders, binder) } + case extensionEncryptedClientHello: + if !extData.ReadBytes(&m.encryptedClientHello, len(extData)) { + return false + } default: // Ignore unknown extensions. continue diff --git a/handshake_messages_test.go b/handshake_messages_test.go index 5167e8d..04d5410 100644 --- a/handshake_messages_test.go +++ b/handshake_messages_test.go @@ -76,6 +76,18 @@ func TestMarshalUnmarshal(t *testing.T) { m.activeCertHandles = nil } + if ch, ok := m.(*clientHelloMsg); ok { + // extensions is special cased, as it is only populated by the + // server-side of a handshake and is not expected to roundtrip + // through marshal + unmarshal. m ends up with the list of + // extensions necessary to serialize the other fields of + // clientHelloMsg, so check that it is non-empty, then clear it. + if len(ch.extensions) == 0 { + t.Errorf("expected ch.extensions to be populated on unmarshal") + } + ch.extensions = nil + } + // clientHelloMsg and serverHelloMsg, when unmarshalled, store // their original representation, for later use in the handshake // transcript. In order to prevent DeepEqual from failing since @@ -220,6 +232,9 @@ func (*clientHelloMsg) Generate(rand *rand.Rand, size int) reflect.Value { if rand.Intn(10) > 5 { m.earlyData = true } + if rand.Intn(10) > 5 { + m.encryptedClientHello = randomBytes(rand.Intn(50)+1, rand) + } return reflect.ValueOf(m) } diff --git a/handshake_server.go b/handshake_server.go index bf3c0d2..70ba714 100644 --- a/handshake_server.go +++ b/handshake_server.go @@ -43,7 +43,7 @@ type serverHandshakeState struct { // serverHandshake performs a TLS handshake as a server. func (c *Conn) serverHandshake(ctx context.Context) error { - clientHello, err := c.readClientHello(ctx) + clientHello, ech, err := c.readClientHello(ctx) if err != nil { return err } @@ -53,6 +53,7 @@ func (c *Conn) serverHandshake(ctx context.Context) error { c: c, ctx: ctx, clientHello: clientHello, + echContext: ech, } return hs.handshake() } @@ -134,17 +135,27 @@ func (hs *serverHandshakeState) handshake() error { } // readClientHello reads a ClientHello message and selects the protocol version. -func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { +func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, *echServerContext, 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 + return nil, nil, err } clientHello, ok := msg.(*clientHelloMsg) if !ok { c.sendAlert(alertUnexpectedMessage) - return nil, unexpectedMessageError(clientHello, msg) + return nil, nil, unexpectedMessageError(clientHello, msg) + } + + // ECH processing has to be done before we do any other negotiation based on + // the contents of the client hello, since we may swap it out completely. + var ech *echServerContext + if len(clientHello.encryptedClientHello) != 0 { + clientHello, ech, err = c.processECHClientHello(clientHello) + if err != nil { + return nil, nil, err + } } var configForClient *Config @@ -153,7 +164,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { chi := clientHelloInfo(ctx, c, clientHello) if configForClient, err = c.config.GetConfigForClient(chi); err != nil { c.sendAlert(alertInternalError) - return nil, err + return nil, nil, err } else if configForClient != nil { c.config = configForClient } @@ -167,12 +178,24 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { 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) + return nil, nil, fmt.Errorf("tls: client offered only unsupported versions: %x", clientVersions) } c.haveVers = true c.in.version = c.vers c.out.version = c.vers + // This check reflects some odd specification implied behavior. Client-facing servers + // are supposed to reject hellos with outer ECH and inner ECH that offers 1.2, but + // backend servers are allowed to accept hellos with inner ECH that offer 1.2, since + // they cannot expect client-facing servers to behave properly. Since we act as both + // a client-facing and backend server, we only enforce 1.3 being negotiated if we + // saw a hello with outer ECH first. The spec probably should've made this an error, + // but it didn't, and this matches the boringssl behavior. + if c.vers != VersionTLS13 && (ech != nil && !ech.inner) { + c.sendAlert(alertIllegalParameter) + return nil, nil, errors.New("tls: Encrypted Client Hello cannot be used pre-TLS 1.3") + } + // [UTLS SECTION BEGIN] // Disable unsupported godebug package // if c.config.MinVersion == 0 && c.vers < VersionTLS12 { @@ -181,7 +204,7 @@ func (c *Conn) readClientHello(ctx context.Context) (*clientHelloMsg, error) { // } // [UTLS SECTION END] - return clientHello, nil + return clientHello, ech, nil } func (hs *serverHandshakeState) processClientHello() error { @@ -381,11 +404,11 @@ func (hs *serverHandshakeState) pickCipherSuite() error { // [UTLS SECTION BEGIN] // Disable unsupported godebug package - // if c.config.CipherSuites == nil && !needFIPS() && rsaKexCiphers[hs.suite.id] { + // if c.config.CipherSuites == nil && !fips140tls.Required() && rsaKexCiphers[hs.suite.id] { // tlsrsakex.Value() // ensure godebug is initialized // tlsrsakex.IncNonDefault() // } - // if c.config.CipherSuites == nil && !needFIPS() && tdesCiphers[hs.suite.id] { + // if c.config.CipherSuites == nil && !fips140tls.Required() && tdesCiphers[hs.suite.id] { // tls3des.Value() // ensure godebug is initialized // tls3des.IncNonDefault() // } @@ -603,7 +626,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { } if skx != nil { if len(skx.key) >= 3 && skx.key[0] == 3 /* named curve */ { - c.curveID = CurveID(byteorder.BeUint16(skx.key[1:])) + c.curveID = CurveID(byteorder.BEUint16(skx.key[1:])) } if _, err := hs.c.writeHandshakeRecord(skx, &hs.finishedHash); err != nil { return err @@ -691,7 +714,7 @@ func (hs *serverHandshakeState) doFullHandshake() error { preMasterSecret, err := keyAgreement.processClientKeyExchange(c.config, hs.cert, ckx, c.vers) if err != nil { - c.sendAlert(alertHandshakeFailure) + c.sendAlert(alertIllegalParameter) return err } if hs.hello.extendedMasterSecret { @@ -936,7 +959,11 @@ func (c *Conn) processCertsFromClient(certificate Certificate) error { return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} } - c.verifiedChains = chains + c.verifiedChains, err = fipsAllowedChains(chains) + if err != nil { + c.sendAlert(alertBadCertificate) + return &CertificateVerificationError{UnverifiedCertificates: certs, Err: err} + } } c.peerCertificates = certs @@ -976,6 +1003,7 @@ func clientHelloInfo(ctx context.Context, c *Conn, clientHello *clientHelloMsg) SignatureSchemes: clientHello.supportedSignatureAlgorithms, SupportedProtos: clientHello.alpnProtocols, SupportedVersions: supportedVersions, + Extensions: clientHello.extensions, Conn: c.conn, config: c.config, ctx: ctx, diff --git a/handshake_server_test.go b/handshake_server_test.go index f24821d..525ee13 100644 --- a/handshake_server_test.go +++ b/handshake_server_test.go @@ -23,11 +23,15 @@ import ( "runtime" "slices" "strings" + "sync/atomic" "testing" "time" + + "github.com/refraction-networking/utls/internal/fips140tls" ) func testClientHello(t *testing.T, serverConfig *Config, m handshakeMessage) { + t.Helper() testClientHelloFailure(t, serverConfig, m, "") } @@ -52,12 +56,13 @@ func testClientHelloFailure(t *testing.T, serverConfig *Config, m handshakeMessa }() ctx := context.Background() conn := Server(s, serverConfig) - ch, err := conn.readClientHello(ctx) + ch, ech, err := conn.readClientHello(ctx) if conn.vers == VersionTLS13 { hs := serverHandshakeStateTLS13{ c: conn, ctx: ctx, clientHello: ch, + echContext: ech, } if err == nil { err = hs.processClientHello() @@ -116,7 +121,7 @@ func TestRejectBadProtocolVersion(t *testing.T) { func TestNoSuiteOverlap(t *testing.T) { clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), cipherSuites: []uint16{0xff00}, compressionMethods: []uint8{compressionNone}, @@ -126,9 +131,9 @@ func TestNoSuiteOverlap(t *testing.T) { func TestNoCompressionOverlap(t *testing.T) { clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{0xff}, } testClientHelloFailure(t, testConfig, clientHello, "client does not support uncompressed connections") @@ -136,7 +141,7 @@ func TestNoCompressionOverlap(t *testing.T) { func TestNoRC4ByDefault(t *testing.T) { clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, compressionMethods: []uint8{compressionNone}, @@ -160,9 +165,9 @@ func TestDontSelectECDSAWithRSAKey(t *testing.T) { // Test that, even when both sides support an ECDSA cipher suite, it // won't be selected if the server's private key doesn't support it. clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + cipherSuites: []uint16{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384}, compressionMethods: []uint8{compressionNone}, supportedCurves: []CurveID{CurveP256}, supportedPoints: []uint8{pointFormatUncompressed}, @@ -186,9 +191,9 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { // Test that, even when both sides support an RSA cipher suite, it // won't be selected if the server's private key doesn't support it. clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, supportedCurves: []CurveID{CurveP256}, supportedPoints: []uint8{pointFormatUncompressed}, @@ -208,6 +213,8 @@ func TestDontSelectRSAWithECDSAKey(t *testing.T) { } func TestRenegotiationExtension(t *testing.T) { + skipFIPS(t) // #70505 + clientHello := &clientHelloMsg{ vers: VersionTLS12, compressionMethods: []uint8{compressionNone}, @@ -260,6 +267,8 @@ func TestRenegotiationExtension(t *testing.T) { } func TestTLS12OnlyCipherSuites(t *testing.T) { + skipFIPS(t) // No TLS 1.1 in FIPS mode. + // Test that a Server doesn't select a TLS 1.2-only cipher suite when // the client negotiates TLS 1.1. clientHello := &clientHelloMsg{ @@ -321,13 +330,18 @@ func TestTLSPointFormats(t *testing.T) { supportedPoints []uint8 wantSupportedPoints bool }{ - {"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{pointFormatUncompressed}, true}, - {"ECC without ec_point_format", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, nil, false}, - {"ECC with extra values", []uint16{TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, []CurveID{CurveP256}, []uint8{13, 37, pointFormatUncompressed, 42}, true}, + {"ECC", []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, []CurveID{CurveP256}, []uint8{pointFormatUncompressed}, true}, + {"ECC without ec_point_format", []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, []CurveID{CurveP256}, nil, false}, + {"ECC with extra values", []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, []CurveID{CurveP256}, []uint8{13, 37, pointFormatUncompressed, 42}, true}, {"RSA", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, nil, false}, {"RSA with ec_point_format", []uint16{TLS_RSA_WITH_AES_256_GCM_SHA384}, nil, []uint8{pointFormatUncompressed}, false}, } for _, tt := range tests { + // The RSA subtests should be enabled for FIPS 140 required mode: #70505 + if strings.HasPrefix(tt.name, "RSA") && fips140tls.Required() { + t.Logf("skipping in FIPS mode.") + continue + } t.Run(tt.name, func(t *testing.T) { clientHello := &clientHelloMsg{ vers: VersionTLS12, @@ -341,7 +355,9 @@ func TestTLSPointFormats(t *testing.T) { c, s := localPipe(t) replyChan := make(chan any) go func() { - cli := Client(c, testConfig) + clientConfig := testConfig.Clone() + clientConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} + cli := Client(c, clientConfig) cli.vers = clientHello.vers if _, err := cli.writeHandshakeRecord(clientHello, nil); err != nil { testFatal(t, err) @@ -354,9 +370,10 @@ func TestTLSPointFormats(t *testing.T) { replyChan <- reply } }() - config := testConfig.Clone() - config.CipherSuites = clientHello.cipherSuites - Server(s, config).Handshake() + serverConfig := testConfig.Clone() + serverConfig.Certificates = []Certificate{{Certificate: [][]byte{testRSA2048Certificate}, PrivateKey: testRSA2048PrivateKey}} + serverConfig.CipherSuites = clientHello.cipherSuites + Server(s, serverConfig).Handshake() s.Close() reply := <-replyChan if err, ok := reply.(error); ok { @@ -431,6 +448,8 @@ func TestVersion(t *testing.T) { } func TestCipherSuitePreference(t *testing.T) { + skipFIPS(t) // No RC4 or CHACHA20_POLY1305 in FIPS mode. + serverConfig := &Config{ CipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA, TLS_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256}, @@ -499,16 +518,17 @@ func TestCrossVersionResume(t *testing.T) { func testCrossVersionResume(t *testing.T, version uint16) { serverConfig := &Config{ - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, Certificates: testConfig.Certificates, + Time: testTime, } clientConfig := &Config{ - CipherSuites: []uint16{TLS_RSA_WITH_AES_128_CBC_SHA}, + CipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, InsecureSkipVerify: true, ClientSessionCache: NewLRUClientSessionCache(1), ServerName: "servername", MinVersion: VersionTLS12, - Time: testTime, // [uTLS] + Time: testTime, } // Establish a session at TLS 1.3. @@ -922,22 +942,6 @@ func TestHandshakeServerKeySharePreference(t *testing.T) { runServerTestTLS13(t, test) } -// TestHandshakeServerUnsupportedKeyShare tests a client that sends a key share -// that's not in the supported groups list. -func TestHandshakeServerUnsupportedKeyShare(t *testing.T) { - pk, _ := ecdh.X25519().GenerateKey(rand.Reader) - clientHello := &clientHelloMsg{ - vers: VersionTLS12, - random: make([]byte, 32), - supportedVersions: []uint16{VersionTLS13}, - cipherSuites: []uint16{TLS_CHACHA20_POLY1305_SHA256}, - compressionMethods: []uint8{compressionNone}, - keyShares: []keyShare{{group: X25519, data: pk.PublicKey().Bytes()}}, - supportedCurves: []CurveID{CurveP256}, - } - testClientHelloFailure(t, testConfig, clientHello, "client sent key share for group it does not support") -} - func TestHandshakeServerALPN(t *testing.T) { config := testConfig.Clone() config.NextProtos = []string{"proto1", "proto2"} @@ -1067,6 +1071,65 @@ func TestHandshakeServerSNIGetCertificateNotFound(t *testing.T) { runServerTestTLS12(t, test) } +// TestHandshakeServerGetCertificateExtensions tests to make sure that the +// Extensions passed to GetCertificate match what we expect based on the +// clientHelloMsg +func TestHandshakeServerGetCertificateExtensions(t *testing.T) { + const errMsg = "TestHandshakeServerGetCertificateExtensions error" + // ensure the test condition inside our GetCertificate callback + // is actually invoked + var called atomic.Int32 + + testVersions := []uint16{VersionTLS12, VersionTLS13} + for _, vers := range testVersions { + t.Run(fmt.Sprintf("TLS version %04x", vers), func(t *testing.T) { + pk, _ := ecdh.P256().GenerateKey(rand.Reader) + clientHello := &clientHelloMsg{ + vers: vers, + random: make([]byte, 32), + cipherSuites: []uint16{TLS_AES_128_GCM_SHA256}, + compressionMethods: []uint8{compressionNone}, + serverName: "test", + keyShares: []keyShare{{group: CurveP256, data: pk.PublicKey().Bytes()}}, + supportedCurves: []CurveID{CurveP256}, + supportedSignatureAlgorithms: []SignatureScheme{ECDSAWithP256AndSHA256}, + } + + // the clientHelloMsg initialized just above is serialized with + // two extensions: server_name(0) and application_layer_protocol_negotiation(16) + expectedExtensions := []uint16{ + extensionServerName, + extensionSupportedCurves, + extensionSignatureAlgorithms, + extensionKeyShare, + } + + if vers == VersionTLS13 { + clientHello.supportedVersions = []uint16{VersionTLS13} + expectedExtensions = append(expectedExtensions, extensionSupportedVersions) + } + + // Go's TLS client presents extensions in the ClientHello sorted by extension ID + slices.Sort(expectedExtensions) + + serverConfig := testConfig.Clone() + serverConfig.GetCertificate = func(clientHello *ClientHelloInfo) (*Certificate, error) { + if !slices.Equal(expectedExtensions, clientHello.Extensions) { + t.Errorf("expected extensions on ClientHelloInfo (%v) to match clientHelloMsg (%v)", expectedExtensions, clientHello.Extensions) + } + called.Add(1) + + return nil, errors.New(errMsg) + } + testClientHelloFailure(t, serverConfig, clientHello, errMsg) + }) + } + + if int(called.Load()) != len(testVersions) { + t.Error("expected our GetCertificate test to be called twice") + } +} + // TestHandshakeServerSNIGetCertificateError tests to make sure that errors in // GetCertificate result in a tls alert. func TestHandshakeServerSNIGetCertificateError(t *testing.T) { @@ -1078,9 +1141,9 @@ func TestHandshakeServerSNIGetCertificateError(t *testing.T) { } clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, serverName: "test", } @@ -1099,9 +1162,9 @@ func TestHandshakeServerEmptyCertificates(t *testing.T) { serverConfig.Certificates = nil clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, } testClientHelloFailure(t, serverConfig, clientHello, errMsg) @@ -1111,9 +1174,9 @@ func TestHandshakeServerEmptyCertificates(t *testing.T) { serverConfig.GetCertificate = nil clientHello = &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, } testClientHelloFailure(t, serverConfig, clientHello, "no certificates") @@ -1436,9 +1499,9 @@ func TestSNIGivenOnFailure(t *testing.T) { const expectedServerName = "test.testing" clientHello := &clientHelloMsg{ - vers: VersionTLS10, + vers: VersionTLS12, random: make([]byte, 32), - cipherSuites: []uint16{TLS_RSA_WITH_RC4_128_SHA}, + cipherSuites: []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256}, compressionMethods: []uint8{compressionNone}, serverName: expectedServerName, } @@ -1458,7 +1521,7 @@ func TestSNIGivenOnFailure(t *testing.T) { }() conn := Server(s, serverConfig) ctx := context.Background() - ch, err := conn.readClientHello(ctx) + ch, _, err := conn.readClientHello(ctx) hs := serverHandshakeState{ c: conn, ctx: ctx, @@ -1694,7 +1757,7 @@ T+E0J8wlH24pgwQHzy7Ko2qLwn1b5PW8ecrlvP1g func TestMultipleCertificates(t *testing.T) { clientConfig := testConfig.Clone() - clientConfig.CipherSuites = []uint16{TLS_RSA_WITH_AES_128_GCM_SHA256} + clientConfig.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256} clientConfig.MaxVersion = VersionTLS12 serverConfig := testConfig.Clone() @@ -1716,6 +1779,8 @@ func TestMultipleCertificates(t *testing.T) { } func TestAESCipherReordering(t *testing.T) { + skipFIPS(t) // No CHACHA20_POLY1305 for FIPS. + currentAESSupport := hasAESGCMHardwareSupport defer func() { hasAESGCMHardwareSupport = currentAESSupport }() @@ -1859,6 +1924,8 @@ func TestAESCipherReordering(t *testing.T) { } func TestAESCipherReorderingTLS13(t *testing.T) { + skipFIPS(t) // No CHACHA20_POLY1305 for FIPS. + currentAESSupport := hasAESGCMHardwareSupport defer func() { hasAESGCMHardwareSupport = currentAESSupport }() diff --git a/handshake_server_tls13.go b/handshake_server_tls13.go index 432fbdc..b05f142 100644 --- a/handshake_server_tls13.go +++ b/handshake_server_tls13.go @@ -9,15 +9,20 @@ import ( "context" "crypto" "crypto/hmac" + "crypto/mlkem" "crypto/rsa" "errors" "hash" "io" "slices" + "sort" "time" "github.com/refraction-networking/utls/internal/byteorder" - "github.com/refraction-networking/utls/internal/mlkem768" + "github.com/refraction-networking/utls/internal/fips140tls" + "github.com/refraction-networking/utls/internal/hkdf" + "github.com/refraction-networking/utls/internal/hpke" + "github.com/refraction-networking/utls/internal/tls13" ) // maxClientPSKIdentities is the number of client PSK identities the server will @@ -25,6 +30,18 @@ import ( // messages cause too much work in session ticket decryption attempts. const maxClientPSKIdentities = 5 +type echServerContext struct { + hpkeContext *hpke.Receipient + configID uint8 + ciphersuite echCipher + transcript hash.Hash + // inner indicates that the initial client_hello we recieved contained an + // encrypted_client_hello extension that indicated it was an "inner" hello. + // We don't do any additional processing of the hello in this case, so all + // fields above are unset. + inner bool +} + type serverHandshakeStateTLS13 struct { c *Conn ctx context.Context @@ -36,23 +53,19 @@ type serverHandshakeStateTLS13 struct { suite *cipherSuiteTLS13 cert *Certificate sigAlg SignatureScheme - selectedGroup CurveID - earlySecret []byte + earlySecret *tls13.EarlySecret sharedKey []byte - handshakeSecret []byte - masterSecret []byte + handshakeSecret *tls13.HandshakeSecret + masterSecret *tls13.MasterSecret trafficSecret []byte // client_application_traffic_secret_0 transcript hash.Hash clientFinished []byte + echContext *echServerContext } func (hs *serverHandshakeStateTLS13) handshake() error { c := hs.c - if needFIPS() { - return errors.New("tls: internal error: TLS 1.3 reached in FIPS mode") - } - // For an overview of the TLS 1.3 handshake, see RFC 8446, Section 2. if err := hs.processClientHello(); err != nil { return err @@ -167,6 +180,9 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { if !hasAESGCMHardwareSupport || !aesgcmPreferred(hs.clientHello.cipherSuites) { preferenceList = defaultCipherSuitesTLS13NoAES } + if fips140tls.Required() { + preferenceList = defaultCipherSuitesTLS13FIPS + } for _, suiteID := range preferenceList { hs.suite = mutualCipherSuiteTLS13(hs.clientHello.cipherSuites, suiteID) if hs.suite != nil { @@ -181,37 +197,45 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { hs.hello.cipherSuite = hs.suite.id hs.transcript = hs.suite.hash.New() - // Pick the key exchange method in server preference order, but give - // priority to key shares, to avoid a HelloRetryRequest round-trip. - var selectedGroup CurveID - var clientKeyShare *keyShare + // First, if a post-quantum key exchange is available, use one. See + // draft-ietf-tls-key-share-prediction-01, Section 4 for why this must be + // first. + // + // Second, if the client sent a key share for a group we support, use that, + // to avoid a HelloRetryRequest round-trip. + // + // Finally, pick in our fixed preference order. preferredGroups := c.config.curvePreferences(c.vers) - for _, preferredGroup := range preferredGroups { - ki := slices.IndexFunc(hs.clientHello.keyShares, func(ks keyShare) bool { - return ks.group == preferredGroup - }) - if ki != -1 { - clientKeyShare = &hs.clientHello.keyShares[ki] - selectedGroup = clientKeyShare.group - if !slices.Contains(hs.clientHello.supportedCurves, selectedGroup) { - c.sendAlert(alertIllegalParameter) - return errors.New("tls: client sent key share for group it does not support") + preferredGroups = slices.DeleteFunc(preferredGroups, func(group CurveID) bool { + return !slices.Contains(hs.clientHello.supportedCurves, group) + }) + if len(preferredGroups) == 0 { + c.sendAlert(alertHandshakeFailure) + return errors.New("tls: no key exchanges supported by both client and server") + } + hasKeyShare := func(group CurveID) bool { + for _, ks := range hs.clientHello.keyShares { + if ks.group == group { + return true } + } + return false + } + sort.SliceStable(preferredGroups, func(i, j int) bool { + return hasKeyShare(preferredGroups[i]) && !hasKeyShare(preferredGroups[j]) + }) + sort.SliceStable(preferredGroups, func(i, j int) bool { + return isPQKeyExchange(preferredGroups[i]) && !isPQKeyExchange(preferredGroups[j]) + }) + selectedGroup := preferredGroups[0] + + var clientKeyShare *keyShare + for _, ks := range hs.clientHello.keyShares { + if ks.group == selectedGroup { + clientKeyShare = &ks break } } - if selectedGroup == 0 { - for _, preferredGroup := range preferredGroups { - if slices.Contains(hs.clientHello.supportedCurves, preferredGroup) { - selectedGroup = preferredGroup - break - } - } - } - if selectedGroup == 0 { - c.sendAlert(alertHandshakeFailure) - return errors.New("tls: no ECDHE curve supported by both client and server") - } if clientKeyShare == nil { ks, err := hs.doHelloRetryRequest(selectedGroup) if err != nil { @@ -223,13 +247,13 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { ecdhGroup := selectedGroup ecdhData := clientKeyShare.data - if selectedGroup == x25519Kyber768Draft00 { + if selectedGroup == X25519MLKEM768 { ecdhGroup = X25519 - if len(ecdhData) != x25519PublicKeySize+mlkem768.EncapsulationKeySize { + if len(ecdhData) != mlkem.EncapsulationKeySize768+x25519PublicKeySize { c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid Kyber client key share") + return errors.New("tls: invalid X25519MLKEM768 client key share") } - ecdhData = ecdhData[:x25519PublicKeySize] + ecdhData = ecdhData[mlkem.EncapsulationKeySize768:] } if _, ok := curveForCurveID(ecdhGroup); !ok { c.sendAlert(alertInternalError) @@ -251,14 +275,24 @@ func (hs *serverHandshakeStateTLS13) processClientHello() error { c.sendAlert(alertIllegalParameter) return errors.New("tls: invalid client key share") } - if selectedGroup == x25519Kyber768Draft00 { - ciphertext, kyberShared, err := kyberEncapsulate(clientKeyShare.data[x25519PublicKeySize:]) + if selectedGroup == X25519MLKEM768 { + k, err := mlkem.NewEncapsulationKey768(clientKeyShare.data[:mlkem.EncapsulationKeySize768]) if err != nil { c.sendAlert(alertIllegalParameter) - return errors.New("tls: invalid Kyber client key share") + return errors.New("tls: invalid X25519MLKEM768 client key share") } - hs.sharedKey = append(hs.sharedKey, kyberShared...) - hs.hello.serverShare.data = append(hs.hello.serverShare.data, ciphertext...) + mlkemSharedSecret, ciphertext := k.Encapsulate() + // draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.3: "For + // X25519MLKEM768, the shared secret is the concatenation of the ML-KEM + // shared secret and the X25519 shared secret. The shared secret is 64 + // bytes (32 bytes for each part)." + hs.sharedKey = append(mlkemSharedSecret, hs.sharedKey...) + // draft-kwiatkowski-tls-ecdhe-mlkem-02, Section 3.1.2: "When the + // X25519MLKEM768 group is negotiated, the server's key exchange value + // is the concatenation of an ML-KEM ciphertext returned from + // encapsulation to the client's encapsulation key, and the server's + // ephemeral X25519 share." + hs.hello.serverShare.data = append(ciphertext, hs.hello.serverShare.data...) } selectedProto, err := negotiateALPN(c.config.NextProtos, hs.clientHello.alpnProtocols, c.quic != nil) @@ -385,8 +419,8 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { } } - hs.earlySecret = hs.suite.extract(sessionState.secret, nil) - binderKey := hs.suite.deriveSecret(hs.earlySecret, resumptionBinderLabel, nil) + hs.earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, sessionState.secret) + binderKey := hs.earlySecret.ResumptionBinderKey() // Clone the transcript in case a HelloRetryRequest was recorded. transcript := cloneHash(hs.transcript, hs.suite.hash) if transcript == nil { @@ -414,7 +448,7 @@ func (hs *serverHandshakeStateTLS13) checkForResumption() error { if err := transcriptMsg(hs.clientHello, transcript); err != nil { return err } - earlyTrafficSecret := hs.suite.deriveSecret(hs.earlySecret, clientEarlyTrafficLabel, transcript) + earlyTrafficSecret := hs.earlySecret.ClientEarlyTrafficSecret(transcript) c.quicSetReadSecret(QUICEncryptionLevelEarly, hs.suite.id, earlyTrafficSecret) } @@ -532,6 +566,22 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) selectedGroup: selectedGroup, } + if hs.echContext != nil { + // Compute the acceptance message. + helloRetryRequest.encryptedClientHello = make([]byte, 8) + confTranscript := cloneHash(hs.transcript, hs.suite.hash) + if err := transcriptMsg(helloRetryRequest, confTranscript); err != nil { + return nil, err + } + acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New, + hkdf.Extract(hs.suite.hash.New, hs.clientHello.random, nil), + "hrr ech accept confirmation", + confTranscript.Sum(nil), + 8, + ) + helloRetryRequest.encryptedClientHello = acceptConfirmation + } + if _, err := hs.c.writeHandshakeRecord(helloRetryRequest, hs.transcript); err != nil { return nil, err } @@ -552,6 +602,45 @@ func (hs *serverHandshakeStateTLS13) doHelloRetryRequest(selectedGroup CurveID) return nil, unexpectedMessageError(clientHello, msg) } + if hs.echContext != nil { + if len(clientHello.encryptedClientHello) == 0 { + c.sendAlert(alertMissingExtension) + return nil, errors.New("tls: second client hello missing encrypted client hello extension") + } + + echType, echCiphersuite, configID, encap, payload, err := parseECHExt(clientHello.encryptedClientHello) + if err != nil { + c.sendAlert(alertDecodeError) + return nil, errors.New("tls: client sent invalid encrypted client hello extension") + } + + if echType == outerECHExt && hs.echContext.inner || echType == innerECHExt && !hs.echContext.inner { + c.sendAlert(alertDecodeError) + return nil, errors.New("tls: unexpected switch in encrypted client hello extension type") + } + + if echType == outerECHExt { + if echCiphersuite != hs.echContext.ciphersuite || configID != hs.echContext.configID || len(encap) != 0 { + c.sendAlert(alertIllegalParameter) + return nil, errors.New("tls: second client hello encrypted client hello extension does not match") + } + + encodedInner, err := decryptECHPayload(hs.echContext.hpkeContext, clientHello.original, payload) + if err != nil { + c.sendAlert(alertDecryptError) + return nil, errors.New("tls: failed to decrypt second client hello encrypted client hello extension payload") + } + + echInner, err := decodeInnerClientHello(clientHello, encodedInner) + if err != nil { + c.sendAlert(alertIllegalParameter) + return nil, errors.New("tls: client sent invalid encrypted client hello extension") + } + + clientHello = echInner + } + } + if len(clientHello.keyShares) != 1 { c.sendAlert(alertIllegalParameter) return nil, errors.New("tls: client didn't send one key share in second ClientHello") @@ -639,9 +728,27 @@ func illegalClientHelloChange(ch, ch1 *clientHelloMsg) bool { func (hs *serverHandshakeStateTLS13) sendServerParameters() error { c := hs.c + if hs.echContext != nil { + copy(hs.hello.random[32-8:], make([]byte, 8)) + echTranscript := cloneHash(hs.transcript, hs.suite.hash) + echTranscript.Write(hs.clientHello.original) + if err := transcriptMsg(hs.hello, echTranscript); err != nil { + return err + } + // compute the acceptance message + acceptConfirmation := tls13.ExpandLabel(hs.suite.hash.New, + hkdf.Extract(hs.suite.hash.New, hs.clientHello.random, nil), + "ech accept confirmation", + echTranscript.Sum(nil), + 8, + ) + copy(hs.hello.random[32-8:], acceptConfirmation) + } + if err := transcriptMsg(hs.clientHello, hs.transcript); err != nil { return err } + if _, err := hs.c.writeHandshakeRecord(hs.hello, hs.transcript); err != nil { return err } @@ -652,16 +759,13 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { earlySecret := hs.earlySecret if earlySecret == nil { - earlySecret = hs.suite.extract(nil, nil) + earlySecret = tls13.NewEarlySecret(hs.suite.hash.New, nil) } - hs.handshakeSecret = hs.suite.extract(hs.sharedKey, - hs.suite.deriveSecret(earlySecret, "derived", nil)) + hs.handshakeSecret = earlySecret.HandshakeSecret(hs.sharedKey) - clientSecret := hs.suite.deriveSecret(hs.handshakeSecret, - clientHandshakeTrafficLabel, hs.transcript) + clientSecret := hs.handshakeSecret.ClientHandshakeTrafficSecret(hs.transcript) c.in.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, clientSecret) - serverSecret := hs.suite.deriveSecret(hs.handshakeSecret, - serverHandshakeTrafficLabel, hs.transcript) + serverSecret := hs.handshakeSecret.ServerHandshakeTrafficSecret(hs.transcript) c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelHandshake, serverSecret) if c.quic != nil { @@ -695,6 +799,16 @@ func (hs *serverHandshakeStateTLS13) sendServerParameters() error { encryptedExtensions.earlyData = hs.earlyData } + // If client sent ECH extension, but we didn't accept it, + // send retry configs, if available. + if len(hs.c.config.EncryptedClientHelloKeys) > 0 && len(hs.clientHello.encryptedClientHello) > 0 && hs.echContext == nil { + encryptedExtensions.echRetryConfigs, err = buildRetryConfigList(hs.c.config.EncryptedClientHelloKeys) + if err != nil { + c.sendAlert(alertInternalError) + return err + } + } + if _, err := hs.c.writeHandshakeRecord(encryptedExtensions, hs.transcript); err != nil { return err } @@ -786,13 +900,10 @@ func (hs *serverHandshakeStateTLS13) sendServerFinished() error { // Derive secrets that take context through the server Finished. - hs.masterSecret = hs.suite.extract(nil, - hs.suite.deriveSecret(hs.handshakeSecret, "derived", nil)) + hs.masterSecret = hs.handshakeSecret.MasterSecret() - hs.trafficSecret = hs.suite.deriveSecret(hs.masterSecret, - clientApplicationTrafficLabel, hs.transcript) - serverSecret := hs.suite.deriveSecret(hs.masterSecret, - serverApplicationTrafficLabel, hs.transcript) + hs.trafficSecret = hs.masterSecret.ClientApplicationTrafficSecret(hs.transcript) + serverSecret := hs.masterSecret.ServerApplicationTrafficSecret(hs.transcript) c.out.setTrafficSecret(hs.suite, QUICEncryptionLevelApplication, serverSecret) if c.quic != nil { @@ -858,8 +969,7 @@ func (hs *serverHandshakeStateTLS13) sendSessionTickets() error { return err } - c.resumptionSecret = hs.suite.deriveSecret(hs.masterSecret, - resumptionLabel, hs.transcript) + c.resumptionSecret = hs.masterSecret.ResumptionMasterSecret(hs.transcript) if !hs.shouldSendSessionTickets() { return nil @@ -874,7 +984,7 @@ func (c *Conn) sendSessionTicket(earlyData bool, extra [][]byte) error { } // ticket_nonce, which must be unique per connection, is always left at // zero because we only ever send one ticket per connection. - psk := suite.expandLabel(c.resumptionSecret, "resumption", + psk := tls13.ExpandLabel(suite.hash.New, c.resumptionSecret, "resumption", nil, suite.hash.Size()) m := new(newSessionTicketMsgTLS13) @@ -909,7 +1019,7 @@ func (c *Conn) sendSessionTicket(earlyData bool, extra [][]byte) error { if _, err := c.config.rand().Read(ageAdd); err != nil { return err } - m.ageAdd = byteorder.LeUint32(ageAdd) + m.ageAdd = byteorder.LEUint32(ageAdd) if earlyData { // RFC 9001, Section 4.6.1 diff --git a/handshake_test.go b/handshake_test.go index 1221462..8f62403 100644 --- a/handshake_test.go +++ b/handshake_test.go @@ -50,6 +50,9 @@ var ( ) func runTestAndUpdateIfNeeded(t *testing.T, name string, run func(t *testing.T, update bool), wait bool) { + // FIPS mode is non-deterministic and so isn't suited for testing against static test transcripts. + skipFIPS(t) + success := t.Run(name, func(t *testing.T) { if !*update && !wait { t.Parallel() @@ -521,19 +524,21 @@ func fromHex(s string) []byte { return b } -// [uTLS] SECTION BEGIN -// from go1.24 // testTime is 2016-10-20T17:32:09.000Z, which is within the validity period of // [testRSACertificate], [testRSACertificateIssuer], [testRSA2048Certificate], // [testRSA2048CertificateIssuer], and [testECDSACertificate]. var testTime = func() time.Time { return time.Unix(1476984729, 0) } -// [uTLS] SECTION END - var testRSACertificate = fromHex("3082024b308201b4a003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30819f300d06092a864886f70d010101050003818d0030818902818100db467d932e12270648bc062821ab7ec4b6a25dfe1e5245887a3647a5080d92425bc281c0be97799840fb4f6d14fd2b138bc2a52e67d8d4099ed62238b74a0b74732bc234f1d193e596d9747bf3589f6c613cc0b041d4d92b2b2423775b1c3bbd755dce2054cfa163871d1e24c4f31d1a508baab61443ed97a77562f414c852d70203010001a38193308190300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d0e041204109f91161f43433e49a6de6db680d79f60301b0603551d230414301280104813494d137e1631bba301d5acab6e7b30190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b0500038181009d30cc402b5b50a061cbbae55358e1ed8328a9581aa938a495a1ac315a1a84663d43d32dd90bf297dfd320643892243a00bccf9c7db74020015faad3166109a276fd13c3cce10c5ceeb18782f16c04ed73bbb343778d0c1cf10fa1d8408361c94c722b9daedb4606064df4c1b33ec0d1bd42d4dbfe3d1360845c21d33be9fae7") var testRSACertificateIssuer = fromHex("3082021930820182a003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430819f300d06092a864886f70d010101050003818d0030818902818100d667b378bb22f34143b6cd2008236abefaf2852adf3ab05e01329e2c14834f5105df3f3073f99dab5442d45ee5f8f57b0111c8cb682fbb719a86944eebfffef3406206d898b8c1b1887797c9c5006547bb8f00e694b7a063f10839f269f2c34fff7a1f4b21fbcd6bfdfb13ac792d1d11f277b5c5b48600992203059f2a8f8cc50203010001a35d305b300e0603551d0f0101ff040403020204301d0603551d250416301406082b0601050507030106082b06010505070302300f0603551d130101ff040530030101ff30190603551d0e041204104813494d137e1631bba301d5acab6e7b300d06092a864886f70d01010b050003818100c1154b4bab5266221f293766ae4138899bd4c5e36b13cee670ceeaa4cbdf4f6679017e2fe649765af545749fe4249418a56bd38a04b81e261f5ce86b8d5c65413156a50d12449554748c59a30c515bc36a59d38bddf51173e899820b282e40aa78c806526fd184fb6b4cf186ec728edffa585440d2b3225325f7ab580e87dd76") +var testRSA2048Certificate = fromHex("30820316308201fea003020102020900e8f09d3fe25beaa6300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3338303130313030303030305a301a310b3009060355040a1302476f310b300906035504031302476f30820122300d06092a864886f70d01010105000382010f003082010a0282010100e0ac47db9ba1b7f98a996c62dc1d248d4ee570544136fe4e911e22fccc0fe2b20982f3c4cdd8f4065c5068c873ca0a768b80dc915edc66541a5f26cdea44e56e411221e2f9927bf4e009fee76dbe0e118dcc13392efd6f42d8eb2fd5bc8f63ac77800c84d3be90c20c321273254b9137ef61f825dad1ec2c5e75aa4be6d3104899bd5ac400da7ab942b4227a3870ae5bb97870aa09a1082fb8e78b944cd7fd1b0c6fb1cce03b5430b12ef9ce2d95e01821766e998df0cc99202a57cf030577bd2dc0ec85a49f203511bb6f0e9f43398ead0958f8d7534c61e81daf4501faaa68d9cbc725b58401900fa48a3e2333b15c88cf0c5cc8f33fb9464f9d5f5768b8f10203010001a35a3058300e0603551d0f0101ff0404030205a0301d0603551d250416301406082b0601050507030106082b06010505070302300c0603551d130101ff0402300030190603551d1104123010820e6578616d706c652e676f6c616e67300d06092a864886f70d01010b050003820101009e83f835e2da08204ee6f8bdca793cf83c7aec175349c1642dfbe9f4d0dcfb1aedb4d0122e16c2ad92e63dd31cce10ca5dd04be48cded0fdc8fea49e891d9d93e778a67d54b619ac167ce7bb0f6000ca00c5677d09df3eb10080134ba32bfe4132d33954dc479cb266288d53d3f43af9c78c0ca59d396498bdc56d4966dc6b7e49081f7f2ae1d704bb9f9effed93c57d3b738da02edff3999e3f1a5dce2b093951947d233d9c6b6a12b4b1611826aa02544980089eebbcf22a1a96bd35a3ddf638578989334a93d5081fab442b4383ba6213b7cdd74110582244a2abd937828b311d8dd69178756db7874293b9810c5c2e833f91d49d283a62caaf359141997f") + +var testRSA2048CertificateIssuer = fromHex("308203223082020aa003020102020900ca5e4e811a965964300d06092a864886f70d01010b0500301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f74301e170d3136303130313030303030305a170d3235303130313030303030305a301f310b3009060355040a1302476f3110300e06035504031307476f20526f6f7430820122300d06092a864886f70d01010105000382010f003082010a0282010100b308c1720c7054abe66e1be6f8a11246808215a810e8936e47601f7ec1afeb02ad69a5000959d4e08ebc4455ef90b39616f380b8ff2e76f29942d7e009cf010824fe56f69140ac39b761595255ec2aa35155ca2eea884f57b25f8a52f41f56f65b0197cb6c637f9adfa97d8ac27565449f64e67f8b918646ffd630601b0badd8d38aea421fe413ee94f10ea5874c2fd6d8c1b9febaa5ca0ce759993a232c9c48e52230bbf58777b0c30e07e9e0914133730d844b9887b950d5a17c779ac69de2d9c65d26f1ea46c7dd7ac636af6d77df7c9218f78c7b5f08b025867f343ac66cd43a657ac44bfd7e9d07e95a22ff9a0babf72dcffc66eba0a1d90731f67e3bbd0203010001a361305f300e0603551d0f0101ff040403020204301d0603551d250416301406082b0601050507030106082b06010505070302300f0603551d130101ff040530030101ff301d0603551d0e0416041460145a6ce2e8a15b1b68db9a4752ce8684d6ba2d300d06092a864886f70d01010b050003820101001d342fe0b50a25d57a8b13bc14d0abb1eea7431ee752aa423e1306654183e44e9d48bbf592cd32ce77310fdc4e8bbcd724fc43d2723f454bfe605ff90d38d8c6fe60b36c6f4d2d7e4e79bceeb2484f0565274b0d0c4a8562370677624a4c133e332a9e63d4b47544c14e4908ee8685dd0760ae6f4ab089ede2b0cdc595ecefbee7d8be80d57b2d4e4510b6ceda54d1a5980540214191d81cc89a983da43d4043f8efe97a2e231c5153bded520acce87ec8c64a3408f0eb4c742c4a877e8b5b7b7f72497734a41a95994a7a103262ea6d598d03fd5cb0579ed4702424da8893334c58215bc655d49656aedcd02d18676f45d6b9469ae04b89abe9b358391cce99") + +var testRSA2048PrivateKey, _ = x509.ParsePKCS1PrivateKey(fromHex("308204a40201000282010100e0ac47db9ba1b7f98a996c62dc1d248d4ee570544136fe4e911e22fccc0fe2b20982f3c4cdd8f4065c5068c873ca0a768b80dc915edc66541a5f26cdea44e56e411221e2f9927bf4e009fee76dbe0e118dcc13392efd6f42d8eb2fd5bc8f63ac77800c84d3be90c20c321273254b9137ef61f825dad1ec2c5e75aa4be6d3104899bd5ac400da7ab942b4227a3870ae5bb97870aa09a1082fb8e78b944cd7fd1b0c6fb1cce03b5430b12ef9ce2d95e01821766e998df0cc99202a57cf030577bd2dc0ec85a49f203511bb6f0e9f43398ead0958f8d7534c61e81daf4501faaa68d9cbc725b58401900fa48a3e2333b15c88cf0c5cc8f33fb9464f9d5f5768b8f10203010001028201007aac96efca229b199e1bf79a63256677e1c455792bc2a348b2e409a68ea57dda486740430d4290bb885c3f5a741eb567d4f41f7b2098a726f4df4f88cf899edc7c9b31f584dffedece15a7212642c7dbbdd8d806392a183e1fc30af36169c9bab9e528f0bdcd27ad4c8b6a97849da6452c6809de61848db80c3ba3289e785042cdfd46fbfee5f78adcba2927fcd8cbe9dcaa97190457eaa45d77adbe0db820aff0c8511d837ab5b307bad5f85afd2cc70d9659ec58045d97ced1eb7950670ac559449c0305fddefda1bac88d36629a177f65abad182c6470830b39e7f6dbdef4df813ccaef01d5a42d37213b2b9647e2ff56a63e6b6a4b6e8a1567bbfd77042102818100eb66f205e8507c78f7167dbef3ddf02fde6a67bd15152609e9296576e28c79678177145ae98e0a2fee58fdb3d626fb6beae3e0ae0b76bc47d16fcdeb16f0caca8a0902779979382609705ae84514de480c2fb2ddda3049347cc1bde9f1a359747079ef3dce020a3c186c90e63bc20b5489a40d768b1c1c35c679edc5662e18c702818100f454ffff95b126b55cb13b68a3841600fc0bc69ff4064f7ceb122495fa972fdb05ca2fa1c6e2e84432f81c96875ab12226e8ce92ba808c4f6325f27ce058791f05db96e623687d3cfc198e748a07521a8c7ee9e7e8faf95b0985be82b867a49f7d5d50fac3881d2c39dedfdbca3ebe847b859c9864cf7a543e4688f5a60118870281806cee737ac65950704daeebbb8c701c709a54d4f28baa00b33f6137a1bf0e5033d4963d2620c3e8f4eb2fe51eee2f95d3079c31e1784e96ac093fdaa33a376d3032961ebd27990fa192669abab715041385082196461c6813d0d37ac5a25afbcf452937cb7ae438c63c6b28d651bae6b1550c446aa1cefd42e9388d0df6cdc80b02818100cac172c33504923bb494fad8e5c0a9c5dd63244bfe63f238969632b82700a95cd71c2694d887d9f92656d0da75ae640a1441e392cda3f94bb3da7cb4f6335527d2639c809467946e34423cfe26c0d6786398ba20922d1b1a59f79bd5bc937d8040b75c890c13fb298548977a3c05ff71cf535c54f66b5a77684a7e4363a3cb2702818100a4d782f35d5a07f9c1f8f9c378564b220387d1e481cc856b631de7637d8bb77c851db070122050ac230dc6e45edf4523471c717c1cb86a36b2fd3358fae349d51be54d71d7dbeaa6af668323e2b51933f0b8488aa12723e0f32207068b4aa64ed54bcef4acbbbe35b92802faba7ed45ae52bef8313d9ef4393ccc5cf868ddbf8")) + // testRSAPSSCertificate has signatureAlgorithm rsassaPss, but subjectPublicKeyInfo // algorithm rsaEncryption, for use with the rsa_pss_rsae_* SignatureSchemes. // See also TestRSAPSSKeyError. testRSAPSSCertificate is self-signed. diff --git a/internal/byteorder/byteorder.go b/internal/byteorder/byteorder.go index ba37856..01500a8 100644 --- a/internal/byteorder/byteorder.go +++ b/internal/byteorder/byteorder.go @@ -6,30 +6,30 @@ // little and big endian integer types from/to byte slices. package byteorder -func LeUint16(b []byte) uint16 { +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) { +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 { +func LEAppendUint16(b []byte, v uint16) []byte { return append(b, byte(v), byte(v>>8), ) } -func LeUint32(b []byte) uint32 { +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) { +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) @@ -37,7 +37,7 @@ func LePutUint32(b []byte, v uint32) { b[3] = byte(v >> 24) } -func LeAppendUint32(b []byte, v uint32) []byte { +func LEAppendUint32(b []byte, v uint32) []byte { return append(b, byte(v), byte(v>>8), @@ -46,13 +46,13 @@ func LeAppendUint32(b []byte, v uint32) []byte { ) } -func LeUint64(b []byte) uint64 { +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) { +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) @@ -64,7 +64,7 @@ func LePutUint64(b []byte, v uint64) { b[7] = byte(v >> 56) } -func LeAppendUint64(b []byte, v uint64) []byte { +func LEAppendUint64(b []byte, v uint64) []byte { return append(b, byte(v), byte(v>>8), @@ -77,30 +77,30 @@ func LeAppendUint64(b []byte, v uint64) []byte { ) } -func BeUint16(b []byte) uint16 { +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) { +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 { +func BEAppendUint16(b []byte, v uint16) []byte { return append(b, byte(v>>8), byte(v), ) } -func BeUint32(b []byte) uint32 { +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) { +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) @@ -108,7 +108,7 @@ func BePutUint32(b []byte, v uint32) { b[3] = byte(v) } -func BeAppendUint32(b []byte, v uint32) []byte { +func BEAppendUint32(b []byte, v uint32) []byte { return append(b, byte(v>>24), byte(v>>16), @@ -117,13 +117,13 @@ func BeAppendUint32(b []byte, v uint32) []byte { ) } -func BeUint64(b []byte) uint64 { +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) { +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) @@ -135,7 +135,7 @@ func BePutUint64(b []byte, v uint64) { b[7] = byte(v) } -func BeAppendUint64(b []byte, v uint64) []byte { +func BEAppendUint64(b []byte, v uint64) []byte { return append(b, byte(v>>56), byte(v>>48), diff --git a/internal/fips140tls/fipstls.go b/internal/fips140tls/fipstls.go new file mode 100644 index 0000000..df03808 --- /dev/null +++ b/internal/fips140tls/fipstls.go @@ -0,0 +1,5 @@ +package fips140tls + +func Required() bool { + return false +} diff --git a/internal/hkdf/hkdf.go b/internal/hkdf/hkdf.go new file mode 100644 index 0000000..184ef7b --- /dev/null +++ b/internal/hkdf/hkdf.go @@ -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 +} diff --git a/internal/hpke/hpke.go b/internal/hpke/hpke.go index 611c89a..6887661 100644 --- a/internal/hpke/hpke.go +++ b/internal/hpke/hpke.go @@ -10,12 +10,12 @@ import ( "crypto/cipher" "crypto/ecdh" "crypto/rand" - "encoding/binary" "errors" "math/bits" + "github.com/refraction-networking/utls/internal/byteorder" + "github.com/refraction-networking/utls/internal/hkdf" "golang.org/x/crypto/chacha20poly1305" - "golang.org/x/crypto/hkdf" ) // testingOnlyGenerateKey is only used during testing, to provide @@ -26,10 +26,10 @@ 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)) +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, suiteID...) + labeledIKM = append(labeledIKM, sid...) labeledIKM = append(labeledIKM, label...) labeledIKM = append(labeledIKM, inputKey...) return hkdf.Extract(kdf.hash.New, labeledIKM, salt) @@ -37,17 +37,12 @@ func (kdf *hkdfKDF) LabeledExtract(suiteID []byte, salt []byte, label string, in 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 = byteorder.BEAppendUint16(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 + return hkdf.Expand(kdf.hash.New, randomKey, string(labeledInfo), int(length)) } // dhKEM implements the KEM specified in RFC 9180, Section 4.1. @@ -59,13 +54,17 @@ type dhKEM struct { 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 - 0x0020: {ecdh.X25519(), crypto.SHA256, 32}, + DHKEM_X25519_HKDF_SHA256: {ecdh.X25519(), crypto.SHA256, 32}, } func newDHKem(kemID uint16) (*dhKEM, error) { @@ -76,7 +75,7 @@ func newDHKem(kemID uint16) (*dhKEM, error) { return &dhKEM{ dh: suite.curve, kdf: hkdfKDF{suite.hash}, - suiteID: binary.BigEndian.AppendUint16([]byte("KEM"), kemID), + suiteID: byteorder.BEAppendUint16([]byte("KEM"), kemID), nSecret: suite.nSecret, }, nil } @@ -108,9 +107,22 @@ func (dh *dhKEM) Encap(pubRecipient *ecdh.PublicKey) (sharedSecret []byte, encap return dh.ExtractAndExpand(dhVal, kemContext), encPubEph, nil } -type Sender struct { +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 - kem *dhKEM sharedSecret []byte @@ -123,6 +135,14 @@ type Sender struct { 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 { @@ -131,102 +151,148 @@ var aesGCMNew = func(key []byte) (cipher.AEAD, error) { 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 - 0x0001: {keySize: 16, nonceSize: 12, aead: aesGCMNew}, - 0x0002: {keySize: 32, nonceSize: 12, aead: aesGCMNew}, - 0x0003: {keySize: chacha20poly1305.KeySize, nonceSize: chacha20poly1305.NonceSize, aead: chacha20poly1305.New}, + 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 - 0x0001: func() *hkdfKDF { return &hkdfKDF{crypto.SHA256} }, + KDF_HKDF_SHA256: 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 - } +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, nil, errors.New("unsupported KDF id") + return nil, errors.New("unsupported KDF id") } kdf := kdfInit() aeadInfo, ok := SupportedAEADs[aeadID] if !ok { - return nil, nil, errors.New("unsupported AEAD id") + return nil, errors.New("unsupported AEAD id") } - pskIDHash := kdf.LabeledExtract(suiteID, nil, "psk_id_hash", nil) - infoHash := kdf.LabeledExtract(suiteID, nil, "info_hash", info) + 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(suiteID, sharedSecret, "secret", nil) + secret := kdf.LabeledExtract(sid, 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*/) + 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, nil, err + return nil, err } - return encapsulatedKey, &Sender{ - kem: kem, + return &context{ aead: aead, sharedSecret: sharedSecret, - suiteID: suiteID, + suiteID: sid, 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] +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 } - // 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") + 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] } - s.seqNum = s.seqNum.addOne() return nonce } -func (s *Sender) Seal(aad, plaintext []byte) ([]byte, error) { +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 SuiteID(kemID, kdfID, aeadID uint16) []byte { +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 = binary.BigEndian.AppendUint16(suiteID, kemID) - suiteID = binary.BigEndian.AppendUint16(suiteID, kdfID) - suiteID = binary.BigEndian.AppendUint16(suiteID, aeadID) + suiteID = byteorder.BEAppendUint16(suiteID, kemID) + suiteID = byteorder.BEAppendUint16(suiteID, kdfID) + suiteID = byteorder.BEAppendUint16(suiteID, aeadID) return suiteID } @@ -238,6 +304,14 @@ func ParseHPKEPublicKey(kemID uint16, bytes []byte) (*ecdh.PublicKey, error) { 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 } @@ -253,7 +327,7 @@ func (u uint128) bitLen() int { func (u uint128) bytes() []byte { b := make([]byte, 16) - binary.BigEndian.PutUint64(b[0:], u.hi) - binary.BigEndian.PutUint64(b[8:], u.lo) + byteorder.BEPutUint64(b[0:], u.hi) + byteorder.BEPutUint64(b[8:], u.lo) return b } diff --git a/internal/mlkem768/mlkem768.go b/internal/mlkem768/mlkem768.go deleted file mode 100644 index b9bb74e..0000000 --- a/internal/mlkem768/mlkem768.go +++ /dev/null @@ -1,886 +0,0 @@ -// 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/refraction-networking/utls/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 -} diff --git a/internal/mlkem768/mlkem768_test.go b/internal/mlkem768/mlkem768_test.go deleted file mode 100644 index b91b42a..0000000 --- a/internal/mlkem768/mlkem768_test.go +++ /dev/null @@ -1,467 +0,0 @@ -// 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 - -import ( - "bytes" - "crypto/rand" - _ "embed" - "encoding/hex" - "errors" - "flag" - "math/big" - "strconv" - "testing" - - "golang.org/x/crypto/sha3" -) - -func TestFieldReduce(t *testing.T) { - for a := uint32(0); a < 2*q*q; a++ { - got := fieldReduce(a) - exp := fieldElement(a % q) - if got != exp { - t.Fatalf("reduce(%d) = %d, expected %d", a, got, exp) - } - } -} - -func TestFieldAdd(t *testing.T) { - for a := fieldElement(0); a < q; a++ { - for b := fieldElement(0); b < q; b++ { - got := fieldAdd(a, b) - exp := (a + b) % q - if got != exp { - t.Fatalf("%d + %d = %d, expected %d", a, b, got, exp) - } - } - } -} - -func TestFieldSub(t *testing.T) { - for a := fieldElement(0); a < q; a++ { - for b := fieldElement(0); b < q; b++ { - got := fieldSub(a, b) - exp := (a - b + q) % q - if got != exp { - t.Fatalf("%d - %d = %d, expected %d", a, b, got, exp) - } - } - } -} - -func TestFieldMul(t *testing.T) { - for a := fieldElement(0); a < q; a++ { - for b := fieldElement(0); b < q; b++ { - got := fieldMul(a, b) - exp := fieldElement((uint32(a) * uint32(b)) % q) - if got != exp { - t.Fatalf("%d * %d = %d, expected %d", a, b, got, exp) - } - } - } -} - -func TestDecompressCompress(t *testing.T) { - for _, bits := range []uint8{1, 4, 10} { - for a := uint16(0); a < 1<= q { - t.Fatalf("decompress(%d, %d) = %d >= q", a, bits, f) - } - got := compress(f, bits) - if got != a { - t.Fatalf("compress(decompress(%d, %d), %d) = %d", a, bits, bits, got) - } - } - - for a := fieldElement(0); a < q; a++ { - c := compress(a, bits) - if c >= 1<= 2^bits", a, bits, c) - } - got := decompress(c, bits) - diff := min(a-got, got-a, a-got+q, got-a+q) - ceil := q / (1 << bits) - if diff > fieldElement(ceil) { - t.Fatalf("decompress(compress(%d, %d), %d) = %d (diff %d, max diff %d)", - a, bits, bits, got, diff, ceil) - } - } - } -} - -func CompressRat(x fieldElement, d uint8) uint16 { - if x >= q { - panic("x out of range") - } - if d <= 0 || d >= 12 { - panic("d out of range") - } - - precise := big.NewRat((1<= 1<= 12 { - panic("d out of range") - } - - precise := big.NewRat(q*int64(y), 1<>7 != 0 { - panic("not 7 bits") - } - var r uint8 - r |= n >> 6 & 0b0000_0001 - r |= n >> 4 & 0b0000_0010 - r |= n >> 2 & 0b0000_0100 - r |= n /**/ & 0b0000_1000 - r |= n << 2 & 0b0001_0000 - r |= n << 4 & 0b0010_0000 - r |= n << 6 & 0b0100_0000 - return r -} - -func TestZetas(t *testing.T) { - ζ := big.NewInt(17) - q := big.NewInt(q) - for k, zeta := range zetas { - // ζ^BitRev7(k) mod q - exp := new(big.Int).Exp(ζ, big.NewInt(int64(BitRev7(uint8(k)))), q) - if big.NewInt(int64(zeta)).Cmp(exp) != 0 { - t.Errorf("zetas[%d] = %v, expected %v", k, zeta, exp) - } - } -} - -func TestGammas(t *testing.T) { - ζ := big.NewInt(17) - q := big.NewInt(q) - for k, gamma := range gammas { - // ζ^2BitRev7(i)+1 - exp := new(big.Int).Exp(ζ, big.NewInt(int64(BitRev7(uint8(k)))*2+1), q) - if big.NewInt(int64(gamma)).Cmp(exp) != 0 { - t.Errorf("gammas[%d] = %v, expected %v", k, gamma, exp) - } - } -} - -func TestRoundTrip(t *testing.T) { - dk, err := GenerateKey() - if err != nil { - t.Fatal(err) - } - c, Ke, err := Encapsulate(dk.EncapsulationKey()) - if err != nil { - t.Fatal(err) - } - Kd, err := Decapsulate(dk, c) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(Ke, Kd) { - t.Fail() - } - - dk1, err := GenerateKey() - if err != nil { - t.Fatal(err) - } - if bytes.Equal(dk.EncapsulationKey(), dk1.EncapsulationKey()) { - t.Fail() - } - if bytes.Equal(dk.Bytes(), dk1.Bytes()) { - t.Fail() - } - if bytes.Equal(dk.Bytes()[EncapsulationKeySize-32:], dk1.Bytes()[EncapsulationKeySize-32:]) { - t.Fail() - } - - c1, Ke1, err := Encapsulate(dk.EncapsulationKey()) - if err != nil { - t.Fatal(err) - } - if bytes.Equal(c, c1) { - t.Fail() - } - if bytes.Equal(Ke, Ke1) { - t.Fail() - } -} - -func TestBadLengths(t *testing.T) { - dk, err := GenerateKey() - if err != nil { - t.Fatal(err) - } - ek := dk.EncapsulationKey() - - for i := 0; i < len(ek)-1; i++ { - if _, _, err := Encapsulate(ek[:i]); err == nil { - t.Errorf("expected error for ek length %d", i) - } - } - ekLong := ek - for i := 0; i < 100; i++ { - ekLong = append(ekLong, 0) - if _, _, err := Encapsulate(ekLong); err == nil { - t.Errorf("expected error for ek length %d", len(ekLong)) - } - } - - c, _, err := Encapsulate(ek) - if err != nil { - t.Fatal(err) - } - - for i := 0; i < len(dk.Bytes())-1; i++ { - if _, err := NewKeyFromExtendedEncoding(dk.Bytes()[:i]); err == nil { - t.Errorf("expected error for dk length %d", i) - } - } - dkLong := dk.Bytes() - for i := 0; i < 100; i++ { - dkLong = append(dkLong, 0) - if _, err := NewKeyFromExtendedEncoding(dkLong); err == nil { - t.Errorf("expected error for dk length %d", len(dkLong)) - } - } - - for i := 0; i < len(c)-1; i++ { - if _, err := Decapsulate(dk, c[:i]); err == nil { - t.Errorf("expected error for c length %d", i) - } - } - cLong := c - for i := 0; i < 100; i++ { - cLong = append(cLong, 0) - if _, err := Decapsulate(dk, cLong); err == nil { - t.Errorf("expected error for c length %d", len(cLong)) - } - } -} - -func EncapsulateDerand(ek, m []byte) (c, K []byte, err error) { - if len(m) != messageSize { - return nil, nil, errors.New("bad message length") - } - return kemEncaps(nil, ek, (*[messageSize]byte)(m)) -} - -func DecapsulateFromBytes(dkBytes []byte, c []byte) ([]byte, error) { - dk, err := NewKeyFromExtendedEncoding(dkBytes) - if err != nil { - return nil, err - } - return Decapsulate(dk, c) -} - -func GenerateKeyDerand(t testing.TB, d, z []byte) ([]byte, *DecapsulationKey) { - if len(d) != 32 || len(z) != 32 { - t.Fatal("bad length") - } - dk := kemKeyGen(nil, (*[32]byte)(d), (*[32]byte)(z)) - return dk.EncapsulationKey(), dk -} - -var millionFlag = flag.Bool("million", false, "run the million vector test") - -// TestPQCrystalsAccumulated accumulates the 10k vectors generated by the -// reference implementation and checks the hash of the result, to avoid checking -// in 150MB of test vectors. -func TestPQCrystalsAccumulated(t *testing.T) { - n := 10000 - expected := "f7db260e1137a742e05fe0db9525012812b004d29040a5b606aad3d134b548d3" - if testing.Short() { - n = 100 - expected = "8d0c478ead6037897a0da6be21e5399545babf5fc6dd10c061c99b7dee2bf0dc" - } - if *millionFlag { - n = 1000000 - expected = "70090cc5842aad0ec43d5042c783fae9bc320c047b5dafcb6e134821db02384d" - } - - s := sha3.NewShake128() - o := sha3.NewShake128() - d := make([]byte, 32) - z := make([]byte, 32) - msg := make([]byte, 32) - ct1 := make([]byte, CiphertextSize) - - for i := 0; i < n; i++ { - s.Read(d) - s.Read(z) - ek, dk := GenerateKeyDerand(t, d, z) - o.Write(ek) - o.Write(dk.Bytes()) - - s.Read(msg) - ct, k, err := EncapsulateDerand(ek, msg) - if err != nil { - t.Fatal(err) - } - o.Write(ct) - o.Write(k) - - kk, err := Decapsulate(dk, ct) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(kk, k) { - t.Errorf("k: got %x, expected %x", kk, k) - } - - s.Read(ct1) - k1, err := Decapsulate(dk, ct1) - if err != nil { - t.Fatal(err) - } - o.Write(k1) - } - - got := hex.EncodeToString(o.Sum(nil)) - if got != expected { - t.Errorf("got %s, expected %s", got, expected) - } -} - -var sink byte - -func BenchmarkKeyGen(b *testing.B) { - var dk DecapsulationKey - var d, z [32]byte - rand.Read(d[:]) - rand.Read(z[:]) - b.ResetTimer() - for i := 0; i < b.N; i++ { - dk := kemKeyGen(&dk, &d, &z) - sink ^= dk.EncapsulationKey()[0] - } -} - -func BenchmarkEncaps(b *testing.B) { - d := make([]byte, 32) - rand.Read(d) - z := make([]byte, 32) - rand.Read(z) - var m [messageSize]byte - rand.Read(m[:]) - ek, _ := GenerateKeyDerand(b, d, z) - var c [CiphertextSize]byte - b.ResetTimer() - for i := 0; i < b.N; i++ { - c, K, err := kemEncaps(&c, ek, &m) - if err != nil { - b.Fatal(err) - } - sink ^= c[0] ^ K[0] - } -} - -func BenchmarkDecaps(b *testing.B) { - d := make([]byte, 32) - rand.Read(d) - z := make([]byte, 32) - rand.Read(z) - m := make([]byte, 32) - rand.Read(m) - ek, dk := GenerateKeyDerand(b, d, z) - c, _, err := EncapsulateDerand(ek, m) - if err != nil { - b.Fatal(err) - } - b.ResetTimer() - for i := 0; i < b.N; i++ { - K := kemDecaps(dk, (*[CiphertextSize]byte)(c)) - sink ^= K[0] - } -} - -func BenchmarkRoundTrip(b *testing.B) { - dk, err := GenerateKey() - if err != nil { - b.Fatal(err) - } - ek := dk.EncapsulationKey() - c, _, err := Encapsulate(ek) - if err != nil { - b.Fatal(err) - } - b.Run("Alice", func(b *testing.B) { - for i := 0; i < b.N; i++ { - dkS, err := GenerateKey() - if err != nil { - b.Fatal(err) - } - ekS := dkS.EncapsulationKey() - sink ^= ekS[0] - - Ks, err := Decapsulate(dk, c) - if err != nil { - b.Fatal(err) - } - sink ^= Ks[0] - } - }) - b.Run("Bob", func(b *testing.B) { - for i := 0; i < b.N; i++ { - cS, Ks, err := Encapsulate(ek) - if err != nil { - b.Fatal(err) - } - sink ^= cS[0] ^ Ks[0] - } - }) -} diff --git a/internal/tls12/tls12.go b/internal/tls12/tls12.go new file mode 100644 index 0000000..b456ed1 --- /dev/null +++ b/internal/tls12/tls12.go @@ -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) +} diff --git a/internal/tls13/tls13.go b/internal/tls13/tls13.go new file mode 100644 index 0000000..6bd5d4d --- /dev/null +++ b/internal/tls13/tls13.go @@ -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 +} diff --git a/internal/tls13/u_tls13.go b/internal/tls13/u_tls13.go new file mode 100644 index 0000000..56b3c17 --- /dev/null +++ b/internal/tls13/u_tls13.go @@ -0,0 +1,26 @@ +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 { return s.secret } diff --git a/key_schedule.go b/key_schedule.go index faffc8a..27e11b6 100644 --- a/key_schedule.go +++ b/key_schedule.go @@ -7,93 +7,27 @@ package tls import ( "crypto/ecdh" "crypto/hmac" + "crypto/mlkem" "errors" - "fmt" "hash" "io" - "github.com/refraction-networking/utls/internal/mlkem768" - "golang.org/x/crypto/cryptobyte" - "golang.org/x/crypto/hkdf" - "golang.org/x/crypto/sha3" + "github.com/refraction-networking/utls/internal/tls13" ) // This file contains the functions necessary to compute the TLS 1.3 key // schedule. See RFC 8446, Section 7. -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()) + return tls13.ExpandLabel(c.hash.New, trafficSecret, "traffic upd", nil, c.hash.Size()) } // trafficKey generates traffic keys according to RFC 8446, Section 7.3. func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { - key = c.expandLabel(trafficSecret, "key", nil, c.keyLen) - iv = c.expandLabel(trafficSecret, "iv", nil, aeadNonceLength) + key = tls13.ExpandLabel(c.hash.New, trafficSecret, "key", nil, c.keyLen) + iv = tls13.ExpandLabel(c.hash.New, trafficSecret, "iv", nil, aeadNonceLength) return } @@ -101,7 +35,7 @@ func (c *cipherSuiteTLS13) trafficKey(trafficSecret []byte) (key, iv []byte) { // 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()) + finishedKey := tls13.ExpandLabel(c.hash.New, baseKey, "finished", nil, c.hash.Size()) verifyData := hmac.New(c.hash.New, finishedKey) verifyData.Write(transcript.Sum(nil)) return verifyData.Sum(nil) @@ -109,51 +43,17 @@ func (c *cipherSuiteTLS13) finishedHash(baseKey []byte, transcript hash.Hash) [] // 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) +func (c *cipherSuiteTLS13) exportKeyingMaterial(s *tls13.MasterSecret, transcript hash.Hash) func(string, []byte, int) ([]byte, error) { + expMasterSecret := s.ExporterMasterSecret(transcript) return func(label string, context []byte, length int) ([]byte, error) { - secret := c.deriveSecret(expMasterSecret, label, nil) - h := c.hash.New() - h.Write(context) - return c.expandLabel(secret, "exporter", h.Sum(nil), length), nil + return expMasterSecret.Exporter(label, context, 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 + mlkem *mlkem.DecapsulationKey768 } const x25519PublicKeySize = 32 diff --git a/key_schedule_test.go b/key_schedule_test.go index 63516fc..0c57944 100644 --- a/key_schedule_test.go +++ b/key_schedule_test.go @@ -6,15 +6,81 @@ package tls import ( "bytes" + "crypto/sha256" "encoding/hex" - "hash" "strings" "testing" "unicode" - "github.com/refraction-networking/utls/internal/mlkem768" + "github.com/refraction-networking/utls/internal/tls13" ) +func TestACVPVectors(t *testing.T) { + // https://github.com/usnistgov/ACVP-Server/blob/3a7333f63/gen-val/json-files/TLS-v1.3-KDF-RFC8446/prompt.json#L428-L436 + psk := fromHex("56288B726C73829F7A3E47B103837C8139ACF552E7530C7A710B35ED41191698") + dhe := fromHex("EFFE9EC26AA29FD750DFA6A10B944D74071595B27EE88887D5E11C84590B5CC3") + helloClientRandom := fromHex("E9137679E582BA7C1DB41CF725F86C6D09C8C05F297BAD9A65B552EAF524FDE4") + helloServerRandom := fromHex("23ECCFD030790748C8F8D8A656FD98D717F1B62AF3712F97211D2070B499F98A") + finishedClientRandom := fromHex("62A62FA75563ED4FDCAA0BC16567B314871C304ACF06B0FFC3F08C1797594D43") + finishedServerRandom := fromHex("C750EDA6696CD101B142BD79E00E6AC8C5F2C0ABC78DD64F4D991326659E9299") + + // https://github.com/usnistgov/ACVP-Server/blob/3a7333f63/gen-val/json-files/TLS-v1.3-KDF-RFC8446/expectedResults.json#L571-L581 + clientEarlyTrafficSecret := fromHex("3272189698C3594D18F58EFA3F12B638A249515099BE7A2FA9836BABE74F0111") + earlyExporterMasterSecret := fromHex("88E078F562CDC930219F6A5E98A1CE8C6E5F3DAC5AC516459A96F2EF8F114C66") + clientHandshakeTrafficSecret := fromHex("B32306C3CE9932C460A1FE6C0F060593974842036B96FA45049B7352E71C2AD2") + serverHandshakeTrafficSecret := fromHex("22787F8CA269D34BC549AC8BA19F2040938A3AA370D7CC9D60F720882B88D01B") + clientApplicationTrafficSecret := fromHex("47D7EA08397B5871154B0FE85584BCC30A87C69E84D69B56007C5B21F76493BA") + serverApplicationTrafficSecret := fromHex("EFBDB0C873C0480DA57307083839A8984BE25B9A8545E4FCA029940FE2800565") + exporterMasterSecret := fromHex("8A43D787EE3804EAD4A2A5B32972F9896B696295645D7222E1FD081DDD939834") + resumptionMasterSecret := fromHex("5F4C961329C91044011ACBECB0B289282E0E3FED045CB3EA924DFFE5FE654B3D") + + // The "Random" values are undocumented, but they are meant to be written to + // the hash in sequence to develop the transcript. + transcript := sha256.New() + + es := tls13.NewEarlySecret(sha256.New, psk) + + transcript.Write(helloClientRandom) + + if got := es.ClientEarlyTrafficSecret(transcript); !bytes.Equal(got, clientEarlyTrafficSecret) { + t.Errorf("clientEarlyTrafficSecret = %x, want %x", got, clientEarlyTrafficSecret) + } + if got := tls13.TestingOnlyExporterSecret(es.EarlyExporterMasterSecret(transcript)); !bytes.Equal(got, earlyExporterMasterSecret) { + t.Errorf("earlyExporterMasterSecret = %x, want %x", got, earlyExporterMasterSecret) + } + + hs := es.HandshakeSecret(dhe) + + transcript.Write(helloServerRandom) + + if got := hs.ClientHandshakeTrafficSecret(transcript); !bytes.Equal(got, clientHandshakeTrafficSecret) { + t.Errorf("clientHandshakeTrafficSecret = %x, want %x", got, clientHandshakeTrafficSecret) + } + if got := hs.ServerHandshakeTrafficSecret(transcript); !bytes.Equal(got, serverHandshakeTrafficSecret) { + t.Errorf("serverHandshakeTrafficSecret = %x, want %x", got, serverHandshakeTrafficSecret) + } + + ms := hs.MasterSecret() + + transcript.Write(finishedServerRandom) + + if got := ms.ClientApplicationTrafficSecret(transcript); !bytes.Equal(got, clientApplicationTrafficSecret) { + t.Errorf("clientApplicationTrafficSecret = %x, want %x", got, clientApplicationTrafficSecret) + } + if got := ms.ServerApplicationTrafficSecret(transcript); !bytes.Equal(got, serverApplicationTrafficSecret) { + t.Errorf("serverApplicationTrafficSecret = %x, want %x", got, serverApplicationTrafficSecret) + } + if got := tls13.TestingOnlyExporterSecret(ms.ExporterMasterSecret(transcript)); !bytes.Equal(got, exporterMasterSecret) { + t.Errorf("exporterMasterSecret = %x, want %x", got, exporterMasterSecret) + } + + transcript.Write(finishedClientRandom) + + if got := ms.ResumptionMasterSecret(transcript); !bytes.Equal(got, resumptionMasterSecret) { + t.Errorf("resumptionMasterSecret = %x, want %x", got, resumptionMasterSecret) + } +} + // This file contains tests derived from draft-ietf-tls-tls13-vectors-07. func parseVector(v string) []byte { @@ -33,78 +99,6 @@ func parseVector(v string) []byte { return res } -func TestDeriveSecret(t *testing.T) { - chTranscript := cipherSuitesTLS13[0].hash.New() - chTranscript.Write(parseVector(` - payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff - 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 - 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 - 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 - 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 - 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 - 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 - 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 - 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 - 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 - 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 - ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 - 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 - 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 - 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 - 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 - ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d - e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa - cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d - ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d`)) - - type args struct { - secret []byte - label string - transcript hash.Hash - } - tests := []struct { - name string - args args - want []byte - }{ - { - `derive secret for handshake "tls13 derived"`, - args{ - parseVector(`PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 - 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a`), - "derived", - nil, - }, - parseVector(`expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba - b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba`), - }, - { - `derive secret "tls13 c e traffic"`, - args{ - parseVector(`PRK (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 bb - 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c`), - "c e traffic", - chTranscript, - }, - parseVector(`expanded (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e - ff 7e aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62`), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := cipherSuitesTLS13[0] - if got := c.deriveSecret(tt.args.secret, tt.args.label, tt.args.transcript); !bytes.Equal(got, tt.want) { - t.Errorf("cipherSuiteTLS13.deriveSecret() = % x, want % x", got, tt.want) - } - }) - } -} - func TestTrafficKey(t *testing.T) { trafficSecret := parseVector( `PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 @@ -124,90 +118,3 @@ func TestTrafficKey(t *testing.T) { t.Errorf("cipherSuiteTLS13.trafficKey() gotIV = % x, want % x", gotIV, wantIV) } } - -func TestExtract(t *testing.T) { - type args struct { - newSecret []byte - currentSecret []byte - } - tests := []struct { - name string - args args - want []byte - }{ - { - `extract secret "early"`, - args{ - nil, - nil, - }, - parseVector(`secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c - e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a`), - }, - { - `extract secret "master"`, - args{ - nil, - parseVector(`salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 - 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4`), - }, - parseVector(`secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a - 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19`), - }, - { - `extract secret "handshake"`, - args{ - parseVector(`IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d - 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d`), - parseVector(`salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 - 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba`), - }, - parseVector(`secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b - 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac`), - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - c := cipherSuitesTLS13[0] - if got := c.extract(tt.args.newSecret, tt.args.currentSecret); !bytes.Equal(got, tt.want) { - t.Errorf("cipherSuiteTLS13.extract() = % x, want % x", got, tt.want) - } - }) - } -} - -func TestKyberDecapsulate(t *testing.T) { - // From https://pq-crystals.org/kyber/data/kyber-submission-nist-round3.zip - dkBytes, _ := hex.DecodeString("07638FB69868F3D320E5862BD96933FEB311B362093C9B5D50170BCED43F1B536D9A204BB1F22695950BA1F2A9E8EB828B284488760B3FC84FABA04275D5628E39C5B2471374283C503299C0AB49B66B8BBB56A4186624F919A2BA59BB08D8551880C2BEFC4F87F25F59AB587A79C327D792D54C974A69262FF8A78938289E9A87B688B083E0595FE218B6BB1505941CE2E81A5A64C5AAC60417256985349EE47A52420A5F97477B7236AC76BC70E8288729287EE3E34A3DBC3683C0B7B10029FC203418537E7466BA6385A8FF301EE12708F82AAA1E380FC7A88F8F205AB7E88D7E95952A55BA20D09B79A47141D62BF6EB7DD307B08ECA13A5BC5F6B68581C6865B27BBCDDAB142F4B2CBFF488C8A22705FAA98A2B9EEA3530C76662335CC7EA3A00777725EBCCCD2A4636B2D9122FF3AB77123CE0883C1911115E50C9E8A94194E48DD0D09CFFB3ADCD2C1E92430903D07ADBF00532031575AA7F9E7B5A1F3362DEC936D4043C05F2476C07578BC9CBAF2AB4E382727AD41686A96B2548820BB03B32F11B2811AD62F489E951632ABA0D1DF89680CC8A8B53B481D92A68D70B4EA1C3A6A561C0692882B5CA8CC942A8D495AFCB06DE89498FB935B775908FE7A03E324D54CC19D4E1AABD3593B38B19EE1388FE492B43127E5A504253786A0D69AD32601C28E2C88504A5BA599706023A61363E17C6B9BB59BDC697452CD059451983D738CA3FD034E3F5988854CA05031DB09611498988197C6B30D258DFE26265541C89A4B31D6864E9389B03CB74F7EC4323FB9421A4B9790A26D17B0398A26767350909F84D57B6694DF830664CA8B3C3C03ED2AE67B89006868A68527CCD666459AB7F056671000C6164D3A7F266A14D97CBD7004D6C92CACA770B844A4FA9B182E7B18CA885082AC5646FCB4A14E1685FEB0C9CE3372AB95365C04FD83084F80A23FF10A05BF15F7FA5ACC6C0CB462C33CA524FA6B8BB359043BA68609EAA2536E81D08463B19653B5435BA946C9ADDEB202B04B031CC960DCC12E4518D428B32B257A4FC7313D3A7980D80082E934F9D95C32B0A0191A23604384DD9E079BBBAA266D14C3F756B9F2133107433A4E83FA7187282A809203A4FAF841851833D121AC383843A5E55BC2381425E16C7DB4CC9AB5C1B0D91A47E2B8DE0E582C86B6B0D907BB360B97F40AB5D038F6B75C814B27D9B968D419832BC8C2BEE605EF6E5059D33100D90485D378450014221736C07407CAC260408AA64926619788B8601C2A752D1A6CBF820D7C7A04716203225B3895B9342D147A8185CFC1BB65BA06B4142339903C0AC4651385B45D98A8B19D28CD6BAB088787F7EE1B12461766B43CBCCB96434427D93C065550688F6948ED1B5475A425F1B85209D061C08B56C1CC069F6C0A7C6F29358CAB911087732A649D27C9B98F9A48879387D9B00C25959A71654D6F6A946164513E47A75D005986C2363C09F6B537ECA78B9303A5FA457608A586A653A347DB04DFCC19175B3A301172536062A658A95277570C8852CA8973F4AE123A334047DD711C8927A634A03388A527B034BF7A8170FA702C1F7C23EC32D18A2374890BE9C787A9409C82D192C4BB705A2F996CE405DA72C2D9C843EE9F8313ECC7F86D6294D59159D9A879A542E260922ADF999051CC45200C9FFDB60449C49465979272367C083A7D6267A3ED7A7FD47957C219327F7CA73A4007E1627F00B11CC80573C15AEE6640FB8562DFA6B240CA0AD351AC4AC155B96C14C8AB13DD262CDFD51C4BB5572FD616553D17BDD430ACBEA3E95F0B698D66990AB51E5D03783A8B3D278A5720454CF9695CFDCA08485BA099C51CD92A7EA7587C1D15C28E609A81852601B0604010679AA482D51261EC36E36B8719676217FD74C54786488F4B4969C05A8BA27CA3A77CCE73B965923CA554E422B9B61F4754641608AC16C9B8587A32C1C5DD788F88B36B717A46965635DEB67F45B129B99070909C93EB80B42C2B3F3F70343A7CF37E8520E7BCFC416ACA4F18C7981262BA2BFC756AE03278F0EC66DC2057696824BA6769865A601D7148EF6F54E5AF5686AA2906F994CE38A5E0B938F239007003022C03392DF3401B1E4A3A7EBC6161449F73374C8B0140369343D9295FDF511845C4A46EBAAB6CA5492F6800B98C0CC803653A4B1D6E6AAED1932BACC5FEFAA818BA502859BA5494C5F5402C8536A9C4C1888150617F80098F6B2A99C39BC5DC7CF3B5900A21329AB59053ABAA64ED163E859A8B3B3CA3359B750CCC3E710C7AC43C8191CB5D68870C06391C0CB8AEC72B897AC6BE7FBAACC676ED66314C83630E89448C88A1DF04ACEB23ABF2E409EF333C622289C18A2134E650C45257E47475FA33AA537A5A8F7680214716C50D470E3284963CA64F54677AEC54B5272162BF52BC8142E1D4183FC017454A6B5A496831759064024745978CBD51A6CEDC8955DE4CC6D363670A47466E82BE5C23603A17BF22ACDB7CC984AF08C87E14E27753CF587A8EC3447E62C649E887A67C36C9CE98721B697213275646B194F36758673A8ED11284455AFC7A8529F69C97A3C2D7B8C636C0BA55614B768E624E712930F776169B01715725351BC74B47395ED52B25A1313C95164814C34C979CBDFAB85954662CAB485E75087A98CC74BB82CA2D1B5BF2803238480638C40E90B43C7460E7AA917F010151FAB1169987B372ABB59271F7006C24E60236B84B9DDD600623704254617FB498D89E58B0368BCB2103E79353EB587860C1422E476162E425BC2381DB82C6592737E1DD602864B0167A71EC1F223305C02FE25052AF2B3B5A55A0D7A2022D9A798DC0C5874A98702AAF4054C5D80338A5248B5B7BD09C53B5E2A084B047D277A861B1A73BB51488DE04EF573C85230A0470B73175C9FA50594F66A5F50B4150054C93B68186F8B5CBC49316C8548A642B2B36A1D454C7489AC33B2D2CE6668096782A2C1E0866D21A65E16B585E7AF8618BDF3184C1986878508917277B93E10706B1614972B2A94C7310FE9C708C231A1A8AC8D9314A529A97F469BF64962D820648443099A076D55D4CEA824A58304844F99497C10A25148618A315D72CA857D1B04D575B94F85C01D19BEF211BF0AA3362E7041FD16596D808E867B44C4C00D1CDA3418967717F147D0EB21B42AAEE74AC35D0B92414B958531AADF463EC6305AE5ECAF79174002F26DDECC813BF32672E8529D95A4E730A7AB4A3E8F8A8AF979A665EAFD465FC64A0C5F8F3F9003489415899D59A543D8208C54A3166529B53922D4EC143B50F01423B177895EDEE22BB739F647ECF85F50BC25EF7B5A725DEE868626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F") - dk, err := mlkem768.NewKeyFromExtendedEncoding(dkBytes) - if err != nil { - t.Fatal(err) - } - ct, _ := hex.DecodeString("B52C56B92A4B7CE9E4CB7C5B1B163167A8A1675B2FDEF84A5B67CA15DB694C9F11BD027C30AE22EC921A1D911599AF0585E48D20DA70DF9F39E32EF95D4C8F44BFEFDAA5DA64F1054631D04D6D3CFD0A540DD7BA3886E4B5F13E878788604C95C096EAB3919F427521419A946C26CC041475D7124CDC01D0373E5B09C7A70603CFDB4FB3405023F2264DC3F983C4FC02A2D1B268F2208A1F6E2A6209BFF12F6F465F0B069C3A7F84F606D8A94064003D6EC114C8E808D3053884C1D5A142FBF20112EB360FDA3F0F28B172AE50F5E7D83801FB3F0064B687187074BD7FE30EDDAA334CF8FC04FA8CED899CEADE4B4F28B68372BAF98FF482A415B731155B75CEB976BE0EA0285BA01A27F1857A8FB377A3AE0C23B2AA9A079BFABFF0D5B2F1CD9B718BEA03C42F343A39B4F142D01AD8ACBB50E38853CF9A50C8B44C3CF671A4A9043B26DDBB24959AD6715C08521855C79A23B9C3D6471749C40725BDD5C2776D43AED20204BAA141EFB3304917474B7F9F7A4B08B1A93DAED98C67495359D37D67F7438BEE5E43585634B26C6B3810D7CDCBC0F6EB877A6087E68ACB8480D3A8CF6900447E49B417F15A53B607A0E216B855970D37406870B4568722DA77A4084703816784E2F16BED18996532C5D8B7F5D214464E5F3F6E905867B0CE119E252A66713253544685D208E1723908A0CE97834652E08AE7BDC881A131B73C71E84D20D68FDEFF4F5D70CD1AF57B78E3491A9865942321800A203C05ED1FEEB5A28E584E19F6535E7F84E4A24F84A72DCAF5648B4A4235DD664464482F03176E888C28BFC6C1CB238CFFA35A321E71791D9EA8ED0878C61121BF8D2A4AB2C1A5E120BC40ABB1892D1715090A0EE48252CA297A99AA0E510CF26B1ADD06CA543E1C5D6BDCD3B9C585C8538045DB5C252EC3C8C3C954D9BE5907094A894E60EAB43538CFEE82E8FFC0791B0D0F43AC1627830A61D56DAD96C62958B0DE780B78BD47A604550DAB83FFF227C324049471F35248CFB849B25724FF704D5277AA352D550958BE3B237DFF473EC2ADBAEA48CA2658AEFCC77BBD4264AB374D70EAE5B964416CE8226A7E3255A0F8D7E2ADCA062BCD6D78D60D1B32E11405BE54B66EF0FDDD567702A3BCCFEDE3C584701269ED14809F06F8968356BB9267FE86E514252E88BB5C30A7ECB3D0E621021EE0FBF7871B09342BF84F55C97EAF86C48189C7FF4DF389F077E2806E5FA73B3E9458A16C7E275F4F602275580EB7B7135FB537FA0CD95D6EA58C108CD8943D70C1643111F4F01CA8A8276A902666ED81B78D168B006F16AAA3D8E4CE4F4D0FB0997E41AEFFB5B3DAA838732F357349447F387776C793C0479DE9E99498CC356FDB0075A703F23C55D47B550EC89B02ADE89329086A50843456FEDC3788AC8D97233C54560467EE1D0F024B18428F0D73B30E19F5C63B9ABF11415BEA4D0170130BAABD33C05E6524E5FB5581B22B0433342248266D0F1053B245CC2462DC44D34965102482A8ED9E4E964D5683E5D45D0C8269") - ss, err := kyberDecapsulate(dk, ct) - if err != nil { - t.Fatal(err) - } - exp, _ := hex.DecodeString("914CB67FE5C38E73BF74181C0AC50428DEDF7750A98058F7D536708774535B29") - if !bytes.Equal(ss, exp) { - t.Fatalf("got %x, want %x", ss, exp) - } -} - -func TestKyberEncapsulate(t *testing.T) { - dk, err := mlkem768.GenerateKey() - if err != nil { - t.Fatal(err) - } - ct, ss, err := kyberEncapsulate(dk.EncapsulationKey()) - if err != nil { - t.Fatal(err) - } - dkSS, err := kyberDecapsulate(dk, ct) - if err != nil { - t.Fatal(err) - } - if !bytes.Equal(ss, dkSS) { - t.Fatalf("got %x, want %x", ss, dkSS) - } -} diff --git a/notboring.go b/notboring.go deleted file mode 100644 index 63d70c5..0000000 --- a/notboring.go +++ /dev/null @@ -1,6 +0,0 @@ -// 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 - -func needFIPS() bool { return false } diff --git a/prf.go b/prf.go index c525fac..f99411f 100644 --- a/prf.go +++ b/prf.go @@ -14,8 +14,12 @@ import ( "errors" "fmt" "hash" + + "github.com/refraction-networking/utls/internal/tls12" ) +type prfFunc func(secret []byte, label string, seed []byte, keyLen int) []byte + // Split a premaster secret in two as specified in RFC 4346, Section 5. func splitPreMasterSecret(secret []byte) (s1, s2 []byte) { s1 = secret[0 : (len(secret)+1)/2] @@ -45,7 +49,8 @@ func pHash(result, secret, seed []byte, hash func() hash.Hash) { } // prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5. -func prf10(result, secret, label, seed []byte) { +func prf10(secret []byte, label string, seed []byte, keyLen int) []byte { + result := make([]byte, keyLen) hashSHA1 := sha1.New hashMD5 := md5.New @@ -61,16 +66,14 @@ func prf10(result, secret, label, seed []byte) { for i, b := range result2 { result[i] ^= b } + + return result } // prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5. -func prf12(hashFunc func() hash.Hash) 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) +func prf12(hashFunc func() hash.Hash) prfFunc { + return func(secret []byte, label string, seed []byte, keyLen int) []byte { + return tls12.PRF(hashFunc, secret, label, seed, keyLen) } } @@ -79,13 +82,13 @@ const ( finishedVerifyLength = 12 // Length of verify_data in a Finished message. ) -var masterSecretLabel = []byte("master secret") -var extendedMasterSecretLabel = []byte("extended master secret") -var keyExpansionLabel = []byte("key expansion") -var clientFinishedLabel = []byte("client finished") -var serverFinishedLabel = []byte("server finished") +const masterSecretLabel = "master secret" +const extendedMasterSecretLabel = "extended master secret" +const keyExpansionLabel = "key expansion" +const clientFinishedLabel = "client finished" +const serverFinishedLabel = "server finished" -func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secret, label, seed []byte), crypto.Hash) { +func prfAndHashForVersion(version uint16, suite *cipherSuite) (prfFunc, crypto.Hash) { switch version { case VersionTLS10, VersionTLS11: return prf10, crypto.Hash(0) @@ -99,7 +102,7 @@ func prfAndHashForVersion(version uint16, suite *cipherSuite) (func(result, secr } } -func prfForVersion(version uint16, suite *cipherSuite) func(result, secret, label, seed []byte) { +func prfForVersion(version uint16, suite *cipherSuite) prfFunc { prf, _ := prfAndHashForVersion(version, suite) return prf } @@ -111,17 +114,19 @@ func masterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecr seed = append(seed, clientRandom...) seed = append(seed, serverRandom...) - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, masterSecretLabel, seed) - return masterSecret + return prfForVersion(version, suite)(preMasterSecret, masterSecretLabel, seed, masterSecretLength) } // extMasterFromPreMasterSecret generates the extended master secret from the // pre-master secret. See RFC 7627. func extMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret, transcript []byte) []byte { - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, transcript) - return masterSecret + prf, hash := prfAndHashForVersion(version, suite) + if version == VersionTLS12 { + // Use the FIPS 140-3 module only for TLS 1.2 with EMS, which is the + // only TLS 1.0-1.2 approved mode per IG D.Q. + return tls12.MasterSecret(hash.New, preMasterSecret, transcript) + } + return prf(preMasterSecret, extendedMasterSecretLabel, transcript, masterSecretLength) } // keysFromMasterSecret generates the connection keys from the master @@ -133,8 +138,7 @@ func keysFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clie seed = append(seed, clientRandom...) n := 2*macLen + 2*keyLen + 2*ivLen - keyMaterial := make([]byte, n) - prfForVersion(version, suite)(keyMaterial, masterSecret, keyExpansionLabel, seed) + keyMaterial := prfForVersion(version, suite)(masterSecret, keyExpansionLabel, seed, n) clientMAC = keyMaterial[:macLen] keyMaterial = keyMaterial[macLen:] serverMAC = keyMaterial[:macLen] @@ -177,7 +181,7 @@ type finishedHash struct { buffer []byte version uint16 - prf func(result, secret, label, seed []byte) + prf prfFunc } func (h *finishedHash) Write(msg []byte) (n int, err error) { @@ -209,17 +213,13 @@ func (h finishedHash) Sum() []byte { // 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 + return h.prf(masterSecret, clientFinishedLabel, h.Sum(), finishedVerifyLength) } // serverSum returns the contents of the verify_data member of a server's // Finished message. func (h finishedHash) serverSum(masterSecret []byte) []byte { - out := make([]byte, finishedVerifyLength) - h.prf(out, masterSecret, serverFinishedLabel, h.Sum()) - return out + return h.prf(masterSecret, serverFinishedLabel, h.Sum(), finishedVerifyLength) } // hashForClientCertificate returns the handshake messages so far, pre-hashed if @@ -292,8 +292,6 @@ func ekmFromMasterSecret(version uint16, suite *cipherSuite, masterSecret, clien seed = append(seed, context...) } - keyMaterial := make([]byte, length) - prfForVersion(version, suite)(keyMaterial, masterSecret, []byte(label), seed) - return keyMaterial, nil + return prfForVersion(version, suite)(masterSecret, label, seed, length), nil } } diff --git a/quic.go b/quic.go index 9dd6168..ba8a235 100644 --- a/quic.go +++ b/quic.go @@ -206,7 +206,7 @@ func (q *QUICConn) Start(ctx context.Context) error { } 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")) + return quicError(errors.New("tls: Config MinVersion must be at least TLS 1.3")) } go q.conn.HandshakeContext(ctx) if _, ok := <-q.conn.quic.blockedc; !ok { diff --git a/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA b/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA index a35aa57..b79aec6 100644 --- a/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA +++ b/testdata/Client-TLSv10-ClientCert-ECDSA-ECDSA @@ -17,11 +17,11 @@ 000000f0 bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 |.).0.........._X| 00000100 cb 3b 74 |.;t| >>> Flow 2 (server to client) -00000000 16 03 01 00 5d 02 00 00 59 03 01 60 8a bf 2b 6f |....]...Y..`..+o| -00000010 c1 f0 f3 7b d4 78 2c d9 15 3f 33 6a 4b 96 aa 83 |...{.x,..?3jK...| -00000020 54 ae 66 8f 0f e7 b1 68 31 00 39 20 d8 4d f2 2c |T.f....h1.9 .M.,| -00000030 8b 85 98 d6 af e0 07 98 d5 cb 89 72 fb 8d c0 73 |...........r...s| -00000040 cc 68 cf 58 21 4a 8a fc c3 8e 90 0a c0 09 00 00 |.h.X!J..........| +00000000 16 03 01 00 5d 02 00 00 59 03 01 58 ee cb 2c 2e |....]...Y..X..,.| +00000010 24 e7 e4 50 2b 4b 9c 0f ab f5 79 c1 12 b6 89 e5 |$..P+K....y.....| +00000020 83 41 06 cb 45 f6 66 e9 e1 d2 fe 20 95 fd 7f 15 |.A..E.f.... ....| +00000030 44 4e 44 18 9b fc 16 a7 8a a4 d9 f7 ca 49 85 e8 |DND..........I..| +00000040 00 ce 02 26 38 38 1b 8d 6d 16 f2 b8 c0 09 00 00 |...&88..m.......| 00000050 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 17 |................| 00000060 00 00 16 03 01 02 0e 0b 00 02 0a 00 02 07 00 02 |................| 00000070 04 30 82 02 00 30 82 01 62 02 09 00 b8 bf 2d 47 |.0...0..b.....-G| @@ -57,17 +57,17 @@ 00000250 c9 86 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b |......i..|V..1x+| 00000260 e4 c7 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 |..x.....N6$1{j.9| 00000270 95 12 07 8f 2a 16 03 01 00 b5 0c 00 00 b1 03 00 |....*...........| -00000280 1d 20 68 5a 6f c6 20 26 b5 5c 47 c6 5e fb 23 c4 |. hZo. &.\G.^.#.| -00000290 bc 9c 3f 3a 9b ed d6 8e a3 c8 66 7d 9b cb d0 30 |..?:......f}...0| -000002a0 f9 60 00 8b 30 81 88 02 42 01 b2 fb f8 5f d0 14 |.`..0...B...._..| -000002b0 9d 3c 55 0f 16 50 6f d5 0c 4c 3e 73 2e a9 23 5f |.s..#_| -000002c0 e8 9c 02 5d 4c 6d b0 c1 9e 0d ac 59 36 6c d5 c2 |...]Lm.....Y6l..| -000002d0 4c 94 94 94 6f a4 df 26 1a 54 f5 74 b8 49 75 49 |L...o..&.T.t.IuI| -000002e0 9c aa cd 91 24 f3 52 88 6a 1e 80 02 42 01 81 a2 |....$.R.j...B...| -000002f0 76 e2 e8 b0 2a 8e 4e ed 6d be 2f e3 ca 4c ff f2 |v...*.N.m./..L..| -00000300 d3 14 c0 b5 f8 c5 53 04 97 de 14 b2 8e af 77 86 |......S.......w.| -00000310 de bf 4a 59 cf 7b 8a 73 d3 95 c3 28 ca 25 63 8e |..JY.{.s...(.%c.| -00000320 9e 02 7f 8a 04 bb 69 1e 41 31 76 2b 5a 54 ed 16 |......i.A1v+ZT..| +00000280 1d 20 b2 d4 b5 f1 3f 61 7d 07 30 8b fd 17 76 41 |. ....?a}.0...vA| +00000290 12 c5 e1 25 f1 e9 ad 68 26 55 4b f5 60 16 b0 44 |...%...h&UK.`..D| +000002a0 90 7a 00 8b 30 81 88 02 42 01 26 8b c1 0d 38 f2 |.z..0...B.&...8.| +000002b0 0f 22 de fd 81 53 5b 5a 87 b6 57 23 33 22 06 8f |."...S[Z..W#3"..| +000002c0 8b 59 f1 70 85 46 41 f2 b7 0c 80 77 df 40 08 77 |.Y.p.FA....w.@.w| +000002d0 e1 b8 21 7f 55 77 c7 b7 ef ef b5 31 ae a0 22 a8 |..!.Uw.....1..".| +000002e0 d3 e0 67 e8 67 bc a9 cb 03 47 76 02 42 00 f4 7a |..g.g....Gv.B..z| +000002f0 eb 2f d0 82 d7 06 75 35 e4 61 fb cf 27 93 95 29 |./....u5.a..'..)| +00000300 6e 2c b5 d4 01 45 5b b6 d9 72 e9 f9 13 a6 5f bd |n,...E[..r...._.| +00000310 24 76 3b 8e 48 7a ce 4f f5 c2 77 75 66 2d 18 6d |$v;.Hz.O..wuf-.m| +00000320 7d 9e c7 95 0c fe 0b 80 15 67 b2 f2 f6 5a dd 16 |}........g...Z..| 00000330 03 01 00 0a 0d 00 00 06 03 01 02 40 00 00 16 03 |...........@....| 00000340 01 00 04 0e 00 00 00 |.......| >>> Flow 3 (client to server) @@ -107,29 +107,29 @@ 00000210 03 01 00 25 10 00 00 21 20 2f e5 7d a3 47 cd 62 |...%...! /.}.G.b| 00000220 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 cf |C.(.._.).0......| 00000230 c2 ed 90 99 5f 58 cb 3b 74 16 03 01 00 91 0f 00 |...._X.;t.......| -00000240 00 8d 00 8b 30 81 88 02 42 01 57 eb e6 90 3c fe |....0...B.W...<.| -00000250 10 8d e9 7d 50 a5 d3 83 43 64 e7 d0 cb 65 ef b9 |...}P...Cd...e..| -00000260 56 59 b9 52 09 e8 52 d7 d7 4d a7 57 09 dd 1f 83 |VY.R..R..M.W....| -00000270 22 0e 4c 4e 8b 50 0d 68 72 26 9e 2a 8b 6d cb 88 |".LN.P.hr&.*.m..| -00000280 c8 f4 0d 7d 85 80 e2 ec 7f f1 be 02 42 01 68 5d |...}........B.h]| -00000290 f8 91 45 82 61 7f 57 e2 2e b2 54 7d f3 11 11 44 |..E.a.W...T}...D| -000002a0 2f ee 48 91 17 5c 04 3d b8 0e eb ed 66 33 b1 62 |/.H..\.=....f3.b| -000002b0 61 a1 03 e8 77 cf 44 4b 93 fc 4f 12 24 2f d1 67 |a...w.DK..O.$/.g| -000002c0 c8 4b 07 e3 cb a8 7d 5d 82 d4 a2 ec d7 0b f8 14 |.K....}]........| -000002d0 03 01 00 01 01 16 03 01 00 30 28 45 dc 18 db 52 |.........0(E...R| -000002e0 a7 b5 1e 68 7a 06 03 8a 23 87 07 ea 79 38 29 ec |...hz...#...y8).| -000002f0 1b b7 b9 cb 1b 04 ac ba 1d b2 d5 8e 71 e0 27 30 |............q.'0| -00000300 02 d1 c9 4d 35 69 38 71 c9 c7 |...M5i8q..| +00000240 00 8d 00 8b 30 81 88 02 42 01 87 b2 c8 53 94 c3 |....0...B....S..| +00000250 9f ea 8a 04 35 1e 53 fb c0 db c5 43 e8 99 b7 81 |....5.S....C....| +00000260 43 fd 47 fd a6 cd 89 b5 0a b9 d0 5d 79 b0 c2 1a |C.G........]y...| +00000270 94 1f d8 2e 2b 8d 23 2a c8 08 63 98 f6 01 1f 8f |....+.#*..c.....| +00000280 67 60 3a 88 44 63 7e 8b 91 3e 48 02 42 00 e1 1f |g`:.Dc~..>H.B...| +00000290 69 9a 97 bb b6 e9 97 4d f5 f5 d7 23 56 be 1d ff |i......M...#V...| +000002a0 f0 22 67 8d a3 a1 13 33 5b f7 0b c2 a3 c8 5a 8c |."g....3[.....Z.| +000002b0 e8 65 b8 a8 7e 6c 99 11 db ab bd 80 0a ca dd 5a |.e..~l.........Z| +000002c0 d4 e0 28 12 38 7a b4 86 e7 3e 13 05 a1 3f 5f 14 |..(.8z...>...?_.| +000002d0 03 01 00 01 01 16 03 01 00 30 9a fa ce 11 0b 50 |.........0.....P| +000002e0 fa a1 59 9b 89 41 a9 0b 1b 74 dd 8f 7b a9 e0 39 |..Y..A...t..{..9| +000002f0 5d e3 cc ee 11 f4 27 15 bc 4c 8f e1 2c 51 ec 8e |].....'..L..,Q..| +00000300 ed 6c 97 75 4c e9 da fb 68 9d |.l.uL...h.| >>> Flow 4 (server to client) -00000000 14 03 01 00 01 01 16 03 01 00 30 3d a8 df 2e 80 |..........0=....| -00000010 26 22 66 32 fb 6e bc 9e f5 d6 6a 5e 0a 18 34 92 |&"f2.n....j^..4.| -00000020 f9 42 40 e4 9c b1 7a 28 d2 52 e9 b8 13 ce 89 01 |.B@...z(.R......| -00000030 23 44 ab 2e 75 3e c2 96 f5 59 61 |#D..u>...Ya| +00000000 14 03 01 00 01 01 16 03 01 00 30 12 06 0c 4d 1c |..........0...M.| +00000010 81 47 06 5f b9 85 68 60 9c 26 4a 14 f2 40 60 7f |.G._..h`.&J..@`.| +00000020 d1 1e 51 9e f2 70 2f be 54 cb 42 b3 b3 8d 90 9c |..Q..p/.T.B.....| +00000030 9d d2 0d e2 f7 b6 56 31 11 af 5f |......V1.._| >>> Flow 5 (client to server) -00000000 17 03 01 00 20 64 ac d2 1b 98 4d 4e ec 22 25 35 |.... d....MN."%5| -00000010 7c 7c 79 15 a8 54 0d 93 1a b3 3e ff 68 27 d0 d5 |||y..T....>.h'..| -00000020 82 24 7b dc e7 17 03 01 00 20 98 da 6d 26 de b8 |.${...... ..m&..| -00000030 4b d0 ee 2c 18 52 05 86 59 b0 b6 e7 42 74 54 ea |K..,.R..Y...BtT.| -00000040 e6 96 de ec 82 dc 1f 3a 6c 5c 15 03 01 00 20 cc |.......:l\.... .| -00000050 11 da e4 75 e5 0c 06 84 e5 89 3e a4 ef 3c 28 10 |...u......>..<(.| -00000060 68 0a 6f d4 6e ae 14 a6 ca 7c ba 98 c4 28 7e |h.o.n....|...(~| +00000000 17 03 01 00 20 0f d0 65 cd d4 59 d0 9e 07 06 6d |.... ..e..Y....m| +00000010 8a 11 34 d9 3a 31 59 70 02 39 08 ef 28 98 36 2f |..4.:1Yp.9..(.6/| +00000020 0d 5b d0 23 3f 17 03 01 00 20 5b 16 82 9c 29 c6 |.[.#?.... [...).| +00000030 78 34 76 09 5e bb 01 5e fc 01 b2 55 9b ef 67 a8 |x4v.^..^...U..g.| +00000040 ca 9f b9 6d b5 ea 87 48 8e 76 15 03 01 00 20 43 |...m...H.v.... C| +00000050 12 3b f6 99 31 67 d8 8a 94 d1 37 ef 01 ca a1 62 |.;..1g....7....b| +00000060 9f d7 2a eb 7e 37 79 d9 a4 07 15 5f fb 84 32 |..*.~7y...._..2| diff --git a/testdata/Client-TLSv10-ClientCert-ECDSA-RSA b/testdata/Client-TLSv10-ClientCert-ECDSA-RSA index 687eba4..b02fe3d 100644 --- a/testdata/Client-TLSv10-ClientCert-ECDSA-RSA +++ b/testdata/Client-TLSv10-ClientCert-ECDSA-RSA @@ -17,11 +17,11 @@ 000000f0 bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 |.).0.........._X| 00000100 cb 3b 74 |.;t| >>> Flow 2 (server to client) -00000000 16 03 01 00 5d 02 00 00 59 03 01 48 c6 a1 aa 01 |....]...Y..H....| -00000010 b8 4b a1 f5 6d 71 28 f7 fd 76 9d 12 50 d3 56 e2 |.K..mq(..v..P.V.| -00000020 ca 77 d4 82 07 65 e2 c5 12 40 35 20 e3 ee 0a df |.w...e...@5 ....| -00000030 1f 4a d9 b3 a6 6f a0 24 49 8d ab d9 d8 3c 24 36 |.J...o.$I....<$6| -00000040 35 ba 64 8b 7d ec 29 91 d9 26 5e 61 c0 13 00 00 |5.d.}.)..&^a....| +00000000 16 03 01 00 5d 02 00 00 59 03 01 0b 4b bc a9 e8 |....]...Y...K...| +00000010 4a 08 69 de 73 f8 fc 53 c6 e9 cd cf 25 7a 5f b6 |J.i.s..S....%z_.| +00000020 60 48 65 3e f5 5f 9f 14 e7 38 a0 20 ec fe 94 b2 |`He>._...8. ....| +00000030 6d a9 d7 91 c4 92 6b 3e a4 2e 88 72 07 c3 47 12 |m.....k>...r..G.| +00000040 78 cc 45 86 f9 56 16 f9 d7 d9 38 c2 c0 13 00 00 |x.E..V....8.....| 00000050 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 17 |................| 00000060 00 00 16 03 01 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..| 00000070 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........| @@ -61,17 +61,17 @@ 00000290 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.| 000002a0 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..| 000002b0 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...| -000002c0 16 03 01 00 aa 0c 00 00 a6 03 00 1d 20 55 51 65 |............ UQe| -000002d0 bb 06 22 d7 d6 97 39 d1 f4 dc 95 06 b3 a4 a7 00 |.."...9.........| -000002e0 d1 e5 98 bc 97 12 03 25 03 12 ab 20 4f 00 80 71 |.......%... O..q| -000002f0 8d 3c 54 44 ba df 73 92 76 16 d1 ec b1 de a2 27 |.>> Flow 3 (client to server) @@ -111,29 +111,29 @@ 00000210 03 01 00 25 10 00 00 21 20 2f e5 7d a3 47 cd 62 |...%...! /.}.G.b| 00000220 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 cf |C.(.._.).0......| 00000230 c2 ed 90 99 5f 58 cb 3b 74 16 03 01 00 90 0f 00 |...._X.;t.......| -00000240 00 8c 00 8a 30 81 87 02 41 79 c5 57 74 46 9a 18 |....0...Ay.WtF..| -00000250 ad e0 b0 ba 68 f5 0e e2 58 94 dc 73 84 5a f8 86 |....h...X..s.Z..| -00000260 86 8e 2a 37 82 02 a1 4b 19 cd 71 b3 99 04 64 b0 |..*7...K..q...d.| -00000270 db 4a cc 41 a6 17 28 38 f1 67 bd 59 16 97 71 32 |.J.A..(8.g.Y..q2| -00000280 06 71 24 2c f3 df 34 1b a3 b8 02 42 01 2f 2f db |.q$,..4....B.//.| -00000290 45 07 94 53 89 81 59 0b 92 9d 1a 05 42 b3 1c 40 |E..S..Y.....B..@| -000002a0 38 50 a1 8e a6 35 15 76 ca 75 7e fc 8d 7b 36 f3 |8P...5.v.u~..{6.| -000002b0 e3 e7 bf f9 bd 94 35 a0 c5 2b 35 88 be 5d ee f1 |......5..+5..]..| -000002c0 00 f9 9c a8 01 a8 92 5d d6 17 b6 54 98 e4 14 03 |.......]...T....| -000002d0 01 00 01 01 16 03 01 00 30 1f d6 11 ac 58 b3 20 |........0....X. | -000002e0 31 6d 3b f5 83 98 50 75 ff 4f 79 61 2b fc 0f 6c |1m;...Pu.Oya+..l| -000002f0 a6 4d 9e 65 38 e3 ca 12 76 0a 56 1e dd 73 da e1 |.M.e8...v.V..s..| -00000300 66 5a 33 62 8f 7d c3 ed ad |fZ3b.}...| +00000240 00 8c 00 8a 30 81 87 02 41 2c f8 d2 c2 28 75 28 |....0...A,...(u(| +00000250 67 de 75 fb 7a 09 20 8a ec 06 a6 42 03 ad 3c 95 |g.u.z. ....B..<.| +00000260 bb 00 f6 10 71 c7 90 fe 08 16 fa ed 7d 71 24 a2 |....q.......}q$.| +00000270 b6 76 ce f9 1b ff a9 ff 05 b6 dd d8 63 2b 74 86 |.v..........c+t.| +00000280 65 f5 ef f7 36 41 47 77 b5 88 02 42 00 8a 80 f8 |e...6AGw...B....| +00000290 9b cf de a6 b7 c3 d8 48 a1 a0 47 7e cf 33 fc f7 |.......H..G~.3..| +000002a0 fc 87 40 cf 8d c3 81 85 c7 19 9e 37 9e 54 f7 3e |..@........7.T.>| +000002b0 d1 1c 42 83 21 d7 2e ae 02 7b 3c ce 97 f3 9b a0 |..B.!....{<.....| +000002c0 a3 4e b9 a0 9c 78 f0 7e 9c 96 fc 78 e6 08 14 03 |.N...x.~...x....| +000002d0 01 00 01 01 16 03 01 00 30 84 a0 4f 8d 01 40 ca |........0..O..@.| +000002e0 c0 fd ea 1a 9c df 27 cc 25 00 56 e2 30 05 c0 d9 |......'.%.V.0...| +000002f0 c7 21 48 37 6b 35 c3 a4 4e bf 67 98 87 78 0f 3c |.!H7k5..N.g..x.<| +00000300 74 72 4f 6a c5 0d fd 0c 84 |trOj.....| >>> Flow 4 (server to client) -00000000 14 03 01 00 01 01 16 03 01 00 30 92 36 25 46 a5 |..........0.6%F.| -00000010 44 e6 31 25 cd 24 15 df 13 f8 5b a3 af 7c 12 43 |D.1%.$....[..|.C| -00000020 b8 8c 39 84 bc 25 06 02 02 86 78 20 1b ec 98 1a |..9..%....x ....| -00000030 31 b3 b8 cf 82 92 77 ff 08 87 fb |1.....w....| +00000000 14 03 01 00 01 01 16 03 01 00 30 01 da 6e 2a 83 |..........0..n*.| +00000010 20 ad 52 16 f2 c6 c1 55 b8 77 0d 5f c6 48 dc e7 | .R....U.w._.H..| +00000020 72 29 88 0a 2b 1a d1 1e fd fb c0 c3 18 c8 43 47 |r)..+.........CG| +00000030 a9 8f d3 fe f3 d8 d2 a8 ce 79 44 |.........yD| >>> Flow 5 (client to server) -00000000 17 03 01 00 20 2f bd ab e0 2f 9a 81 58 99 35 bb |.... /.../..X.5.| -00000010 86 61 6e 15 be 31 d7 ad 44 1d d9 cf 2f fc d9 f6 |.an..1..D.../...| -00000020 da b6 48 32 27 17 03 01 00 20 76 70 b7 7d 1b 05 |..H2'.... vp.}..| -00000030 ee 54 99 bf 89 79 79 b5 68 c1 84 3c 6d 47 5c d1 |.T...yy.h..>> Flow 2 (server to client) -00000000 16 03 03 00 5d 02 00 00 59 03 03 d4 19 2c 08 c6 |....]...Y....,..| -00000010 ef 32 88 79 a1 84 fc 79 38 62 b2 dd 4b a7 0b b3 |.2.y...y8b..K...| -00000020 d3 13 3d d4 f7 c7 4d d9 8b c6 8e 20 56 8e 90 3d |..=...M.... V..=| -00000030 2b 6e 2d cf 7e c1 c6 b0 e5 d8 d2 af 3a 06 88 c6 |+n-.~.......:...| -00000040 ed e7 18 76 ab 45 c5 76 47 67 95 ad c0 09 00 00 |...v.E.vGg......| +00000000 16 03 03 00 5d 02 00 00 59 03 03 58 a7 ae 6c fc |....]...Y..X..l.| +00000010 0d 53 51 d5 37 8d 1f 88 75 27 bf 34 8f 3d d3 ee |.SQ.7...u'.4.=..| +00000020 95 db 77 d9 c0 3b f1 57 4f 84 e2 20 7f 0d d1 84 |..w..;.WO.. ....| +00000030 0c 7a 4e 41 7f 74 58 6c f5 38 e6 8d 5b 04 15 e1 |.zNA.tXl.8..[...| +00000040 3d 4a ba 7e 58 f4 e8 bf f4 42 8a f5 c0 09 00 00 |=J.~X....B......| 00000050 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 17 |................| 00000060 00 00 16 03 03 02 0e 0b 00 02 0a 00 02 07 00 02 |................| 00000070 04 30 82 02 00 30 82 01 62 02 09 00 b8 bf 2d 47 |.0...0..b.....-G| @@ -56,23 +56,23 @@ 00000240 8c 25 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a |.%.3.......7z..z| 00000250 c9 86 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b |......i..|V..1x+| 00000260 e4 c7 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 |..x.....N6$1{j.9| -00000270 95 12 07 8f 2a 16 03 03 00 b7 0c 00 00 b3 03 00 |....*...........| -00000280 1d 20 e9 3c ed d4 07 43 c1 20 f5 41 35 e1 49 4a |. .<...C. .A5.IJ| -00000290 1c 52 5d 77 f2 07 59 18 55 e0 66 d1 49 ab 95 74 |.R]w..Y.U.f.I..t| -000002a0 81 1c 04 03 00 8b 30 81 88 02 42 01 f1 52 e7 13 |......0...B..R..| -000002b0 3e 9b 3b 4f 90 0f d2 9b 50 11 fe df 1f 12 f2 d9 |>.;O....P.......| -000002c0 0d 89 bc 6c 01 93 45 ca b8 3c 09 cf b2 01 e9 99 |...l..E..<......| -000002d0 87 fb 1d ac 91 7f 77 a2 21 5e 07 5e 65 3b ec 31 |......w.!^.^e;.1| -000002e0 d7 b5 b9 1d 88 c8 82 f5 03 a9 37 e8 b9 02 42 01 |..........7...B.| -000002f0 78 c4 90 fb e3 7f 5a 7a 66 0a 44 f5 66 0e 1e ac |x.....Zzf.D.f...| -00000300 cb 53 c4 74 55 3a b8 b7 ec b3 f6 4c e1 a8 b6 cb |.S.tU:.....L....| -00000310 a1 e5 37 ac e2 17 e2 6c 8f 71 37 b0 f1 ca b8 1b |..7....l.q7.....| -00000320 df 91 16 a4 7f 31 b1 4f 3e e4 30 f0 5a 5e df 93 |.....1.O>.0.Z^..| -00000330 85 16 03 03 00 3a 0d 00 00 36 03 01 02 40 00 2e |.....:...6...@..| -00000340 04 03 05 03 06 03 08 07 08 08 08 09 08 0a 08 0b |................| -00000350 08 04 08 05 08 06 04 01 05 01 06 01 03 03 02 03 |................| -00000360 03 01 02 01 03 02 02 02 04 02 05 02 06 02 00 00 |................| -00000370 16 03 03 00 04 0e 00 00 00 |.........| +00000270 95 12 07 8f 2a 16 03 03 00 b6 0c 00 00 b2 03 00 |....*...........| +00000280 1d 20 b2 5c fe 84 12 a5 2f 8f da 91 3f 44 52 4e |. .\..../...?DRN| +00000290 03 ad e4 f9 8a 14 71 0d 0b 46 86 81 d2 1b 4a 5b |......q..F....J[| +000002a0 d9 4e 04 03 00 8a 30 81 87 02 42 01 78 d2 c2 a9 |.N....0...B.x...| +000002b0 b7 77 f8 eb 8d 9d 36 1f 79 e7 d1 74 a9 9d a4 53 |.w....6.y..t...S| +000002c0 5e 08 4a 92 dc ac b3 32 bf ce e7 f0 80 af 56 48 |^.J....2......VH| +000002d0 42 e2 fd 22 c7 35 11 13 db 2a 6d 74 4a e5 02 ce |B..".5...*mtJ...| +000002e0 ea 43 e6 b0 69 18 3e d3 86 85 76 ff 56 02 41 44 |.C..i.>...v.V.AD| +000002f0 2a e1 6b ae 5d f9 39 5c ec 76 ee 01 d7 04 42 ca |*.k.].9\.v....B.| +00000300 45 07 e0 59 38 75 d4 47 61 a4 5b 0f a9 7a ba 79 |E..Y8u.Ga.[..z.y| +00000310 fa 79 92 41 88 8a d3 be c2 38 3e f7 95 6c 99 3f |.y.A.....8>..l.?| +00000320 4c ff af 10 33 14 25 e0 d7 8a fd 70 17 2b 73 ee |L...3.%....p.+s.| +00000330 16 03 03 00 3a 0d 00 00 36 03 01 02 40 00 2e 04 |....:...6...@...| +00000340 03 05 03 06 03 08 07 08 08 08 09 08 0a 08 0b 08 |................| +00000350 04 08 05 08 06 04 01 05 01 06 01 03 03 02 03 03 |................| +00000360 01 02 01 03 02 02 02 04 02 05 02 06 02 00 00 16 |................| +00000370 03 03 00 04 0e 00 00 00 |........| >>> Flow 3 (client to server) 00000000 16 03 03 02 0a 0b 00 02 06 00 02 03 00 02 00 30 |...............0| 00000010 82 01 fc 30 82 01 5e 02 09 00 9a 30 84 6c 26 35 |...0..^....0.l&5| @@ -109,32 +109,32 @@ 00000200 e4 fa cc b1 8a ce e2 23 a0 87 f0 e1 67 51 eb 16 |.......#....gQ..| 00000210 03 03 00 25 10 00 00 21 20 2f e5 7d a3 47 cd 62 |...%...! /.}.G.b| 00000220 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 cf |C.(.._.).0......| -00000230 c2 ed 90 99 5f 58 cb 3b 74 16 03 03 00 93 0f 00 |...._X.;t.......| -00000240 00 8f 04 03 00 8b 30 81 88 02 42 01 c9 2c e8 1f |......0...B..,..| -00000250 5b a0 97 0f 70 2d 46 e3 6e b4 4c 82 fc be df 05 |[...p-F.n.L.....| -00000260 09 b0 d7 e4 0a 06 71 66 d6 3b 3e b6 56 2c 22 c7 |......qf.;>.V,".| -00000270 7c 3a 63 5c 34 22 5b 3a 49 a2 97 f3 2f 58 e7 2a ||:c\4"[:I.../X.*| -00000280 cd f4 05 84 db 8b 0b 22 e3 50 1b e7 6d 02 42 01 |.......".P..m.B.| -00000290 fd 1e bc 64 8b 01 3f a0 f8 8b 41 be 4d 97 07 6c |...d..?...A.M..l| -000002a0 3b 09 74 d6 00 76 b3 a0 c0 d0 92 d0 78 24 d7 b2 |;.t..v......x$..| -000002b0 1f c0 86 90 59 3a fb a8 e1 f6 80 23 db f7 90 93 |....Y:.....#....| -000002c0 95 fd 37 14 8c 34 f3 07 10 f8 dd e3 66 28 1c b2 |..7..4......f(..| -000002d0 a0 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 |...........@....| -000002e0 00 00 00 00 00 00 00 00 00 00 00 00 6c b8 ab 0b |............l...| -000002f0 c1 2d 2f 4e d2 25 14 96 24 c6 18 97 82 93 1c 1b |.-/N.%..$.......| -00000300 b4 ca 77 8b 17 7c cf 08 ca 06 e2 ef f3 97 6a 31 |..w..|........j1| -00000310 88 aa 46 a2 d3 7b 65 56 e2 77 72 df |..F..{eV.wr.| +00000230 c2 ed 90 99 5f 58 cb 3b 74 16 03 03 00 92 0f 00 |...._X.;t.......| +00000240 00 8e 04 03 00 8a 30 81 87 02 41 0c 11 31 08 e0 |......0...A..1..| +00000250 7d 4e 11 1e 80 7c 50 70 ea 1b 68 16 d9 e6 93 b7 |}N...|Pp..h.....| +00000260 fb 6f 0d 6e ce 8b d2 7c 86 70 c3 e9 ed 35 3c 29 |.o.n...|.p...5<)| +00000270 c7 d8 6c 8f 43 c9 a1 7a 4a ae 19 22 6e e3 85 7e |..l.C..zJ.."n..~| +00000280 a0 5d 7f 19 a5 b7 25 ad d7 1a 5f 42 02 42 00 d8 |.]....%..._B.B..| +00000290 6a 25 47 2a 0e 1a 38 51 1c 73 aa 2d 10 9e 4b 65 |j%G*..8Q.s.-..Ke| +000002a0 56 34 72 e5 02 09 f6 30 ce a8 a0 59 75 7b 13 42 |V4r....0...Yu{.B| +000002b0 1e 31 f3 dd 08 a9 65 df 2d e6 aa 29 5d 9a 5c eb |.1....e.-..)].\.| +000002c0 d3 67 af 29 4e d2 76 e5 17 1d 7e e7 0a 50 c4 d0 |.g.)N.v...~..P..| +000002d0 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....| +000002e0 00 00 00 00 00 00 00 00 00 00 00 8b ef 24 70 ef |.............$p.| +000002f0 b9 b3 fd 59 49 62 b7 18 9c bc 7f 74 06 21 21 ab |...YIb.....t.!!.| +00000300 9c 4d cf 70 0c 6b 3b d3 d4 12 51 a7 f9 09 65 d1 |.M.p.k;...Q...e.| +00000310 ce b7 28 01 c1 e9 0b e4 41 55 b6 |..(.....AU.| >>> Flow 4 (server to client) -00000000 14 03 03 00 01 01 16 03 03 00 40 b8 19 a7 e2 35 |..........@....5| -00000010 8a be 5f 4c 91 d2 db 3f f3 42 90 8e b5 7f ea f7 |.._L...?.B......| -00000020 52 3e 61 2f 4d ef 25 8d ce 82 77 22 05 6f 0d b6 |R>a/M.%...w".o..| -00000030 04 c1 f0 f0 a2 9d bf 80 a9 f5 e1 62 5c e2 30 ef |...........b\.0.| -00000040 83 e7 c5 78 de 8f 17 4e d3 57 dd |...x...N.W.| +00000000 14 03 03 00 01 01 16 03 03 00 40 af 82 f6 ba d1 |..........@.....| +00000010 e8 f9 c5 58 0e ba 94 ec 98 68 b3 20 5b db 4e 8c |...X.....h. [.N.| +00000020 f5 00 29 e8 9a c1 34 35 ac 77 af e2 a0 6a 6f 45 |..)...45.w...joE| +00000030 05 56 d4 a0 5b 75 19 c6 1b 8a c8 65 04 ed 3d f3 |.V..[u.....e..=.| +00000040 84 e5 1b c0 26 31 5d 03 d5 b4 31 |....&1]...1| >>> Flow 5 (client to server) 00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| -00000010 00 00 00 00 00 cb 79 13 ea 9e 7a 63 8f 5a 2b 8c |......y...zc.Z+.| -00000020 3a f3 bb 7e dc ff d1 2c 4a a6 4a aa ad bf 44 b8 |:..~...,J.J...D.| -00000030 cb 67 ce 3d f0 15 03 03 00 30 00 00 00 00 00 00 |.g.=.....0......| -00000040 00 00 00 00 00 00 00 00 00 00 f4 15 a0 f0 64 d1 |..............d.| -00000050 dd 5f 14 66 6c ce 51 95 bc b5 0b f8 4f 42 48 57 |._.fl.Q.....OBHW| -00000060 cf f3 09 62 75 0d 3e 64 64 e0 |...bu.>dd.| +00000010 00 00 00 00 00 2e a5 7c 6f cc f7 44 1a 38 99 4a |.......|o..D.8.J| +00000020 2e a4 4b 79 bf ee b6 c9 12 57 f5 2e 98 dd 1d 2a |..Ky.....W.....*| +00000030 5b 79 d5 ef 44 15 03 03 00 30 00 00 00 00 00 00 |[y..D....0......| +00000040 00 00 00 00 00 00 00 00 00 00 3d fe 37 cb 65 03 |..........=.7.e.| +00000050 13 ce 3d 3b c2 18 c2 27 f9 a4 b7 fc e6 37 eb 2a |..=;...'.....7.*| +00000060 27 6c 52 38 2f 3a 61 d4 a9 e6 |'lR8/:a...| diff --git a/testdata/Client-TLSv12-ClientCert-ECDSA-RSA b/testdata/Client-TLSv12-ClientCert-ECDSA-RSA index 692092e..de57515 100644 --- a/testdata/Client-TLSv12-ClientCert-ECDSA-RSA +++ b/testdata/Client-TLSv12-ClientCert-ECDSA-RSA @@ -17,11 +17,11 @@ 000000f0 bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 |.).0.........._X| 00000100 cb 3b 74 |.;t| >>> Flow 2 (server to client) -00000000 16 03 03 00 5d 02 00 00 59 03 03 ed c3 6f 59 34 |....]...Y....oY4| -00000010 78 33 49 00 68 50 1f a5 aa 93 45 9a 87 34 c4 4e |x3I.hP....E..4.N| -00000020 71 b0 ab 5e 43 f7 a1 5c 89 e8 2f 20 f7 42 d7 2a |q..^C..\../ .B.*| -00000030 a5 fe 16 76 ac 6f cf 20 1d a6 bc d5 9d 27 9d 81 |...v.o. .....'..| -00000040 80 b4 0d 4e 12 89 de 7b 7a 5b a0 2b c0 2f 00 00 |...N...{z[.+./..| +00000000 16 03 03 00 5d 02 00 00 59 03 03 f8 3d 7c a4 a8 |....]...Y...=|..| +00000010 11 e3 56 0f 1c 7e 2e 7c 50 7e 75 5c de 1c 51 8e |..V..~.|P~u\..Q.| +00000020 de d3 8a 84 d2 90 84 f9 e9 07 d5 20 98 6a a8 c1 |........... .j..| +00000030 f4 28 bd 0f 6a 25 a5 26 3d 8d 35 b6 3e bb 77 c6 |.(..j%.&=.5.>.w.| +00000040 8e ab 36 bd 7d c8 a9 b1 5b 30 0f b2 c0 2f 00 00 |..6.}...[0.../..| 00000050 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 17 |................| 00000060 00 00 16 03 03 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..| 00000070 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........| @@ -61,18 +61,18 @@ 00000290 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.| 000002a0 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..| 000002b0 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...| -000002c0 16 03 03 00 ac 0c 00 00 a8 03 00 1d 20 d9 02 16 |............ ...| -000002d0 ba b6 db c0 c6 65 6d fb b8 69 dc 04 1a 8d 4a 36 |.....em..i....J6| -000002e0 27 6b f0 53 c3 72 56 1a 99 07 0f b9 79 08 04 00 |'k.S.rV.....y...| -000002f0 80 5d c6 1e a2 20 7d ae 35 e6 20 2f ff 7a 7b 8d |.]... }.5. /.z{.| -00000300 3f fd 49 d4 ce 26 f7 d0 c2 51 2a 42 03 26 b7 30 |?.I..&...Q*B.&.0| -00000310 af 5f 7d 34 1d 7d 37 ef 8a 1c e6 be 19 99 f1 30 |._}4.}7........0| -00000320 2e 26 6d 7b 83 cd 6e 26 c4 03 f5 7a 30 8f bf e3 |.&m{..n&...z0...| -00000330 c1 64 da 43 fc f0 32 c9 a2 68 e4 97 d0 34 8a c4 |.d.C..2..h...4..| -00000340 fc f8 bb 61 ec df 69 f7 d0 d7 1e 19 c0 5b 21 86 |...a..i......[!.| -00000350 eb 79 93 46 3b 7a 50 83 41 8b d3 7c 59 d5 34 8a |.y.F;zP.A..|Y.4.| -00000360 0c b5 70 e4 86 38 3a 6c 11 04 57 d6 94 c0 78 c7 |..p..8:l..W...x.| -00000370 91 16 03 03 00 3a 0d 00 00 36 03 01 02 40 00 2e |.....:...6...@..| +000002c0 16 03 03 00 ac 0c 00 00 a8 03 00 1d 20 5f c1 31 |............ _.1| +000002d0 d7 64 f0 0b 72 6a 66 2c 49 d7 d1 9c dd 6f e3 3a |.d..rjf,I....o.:| +000002e0 ab 2c 78 6d ca b0 ed 16 26 65 9f ff 66 08 04 00 |.,xm....&e..f...| +000002f0 80 a6 91 d0 03 b8 d2 67 48 69 16 8e 30 dc 5b 3f |.......gHi..0.[?| +00000300 ac 4d e4 33 5f 46 e7 0c 49 a0 71 9d 8c 60 63 f2 |.M.3_F..I.q..`c.| +00000310 2d ff 9e 89 21 7d af 71 ce 41 6b d2 22 fc 1f bd |-...!}.q.Ak."...| +00000320 a9 9e 15 2c d7 c3 cb 69 6d df 23 07 7c 13 e9 2b |...,...im.#.|..+| +00000330 7d 05 f0 18 1e 86 c8 37 ad cd 9e 39 26 0c 8a 9b |}......7...9&...| +00000340 12 90 60 12 95 06 e9 bb f2 46 41 20 10 f5 64 ea |..`......FA ..d.| +00000350 66 13 cb 8e 51 7e 41 78 2a 40 fa 15 e2 0d 5b 37 |f...Q~Ax*@....[7| +00000360 a7 a8 4a f6 8e 93 82 2a a2 91 06 66 4e 49 72 68 |..J....*...fNIrh| +00000370 f9 16 03 03 00 3a 0d 00 00 36 03 01 02 40 00 2e |.....:...6...@..| 00000380 04 03 05 03 06 03 08 07 08 08 08 09 08 0a 08 0b |................| 00000390 08 04 08 05 08 06 04 01 05 01 06 01 03 03 02 03 |................| 000003a0 03 01 02 01 03 02 02 02 04 02 05 02 06 02 00 00 |................| @@ -114,27 +114,27 @@ 00000210 03 03 00 25 10 00 00 21 20 2f e5 7d a3 47 cd 62 |...%...! /.}.G.b| 00000220 43 15 28 da ac 5f bb 29 07 30 ff f6 84 af c4 cf |C.(.._.).0......| 00000230 c2 ed 90 99 5f 58 cb 3b 74 16 03 03 00 93 0f 00 |...._X.;t.......| -00000240 00 8f 04 03 00 8b 30 81 88 02 42 01 a3 80 63 a4 |......0...B...c.| -00000250 49 60 35 5c 06 87 9f 7f ae 40 37 d0 64 58 b2 60 |I`5\.....@7.dX.`| -00000260 61 59 8b 6d a6 d9 55 67 81 f6 7e 9c de 40 69 00 |aY.m..Ug..~..@i.| -00000270 42 e1 2e 67 0d 48 cf 23 a7 28 f2 e0 9e 26 61 20 |B..g.H.#.(...&a | -00000280 4f 50 b6 e3 85 0b f5 f6 96 ec 03 32 35 02 42 01 |OP.........25.B.| -00000290 bd 3b 3e d6 03 98 b5 09 9a aa a9 73 1d 98 6a a5 |.;>........s..j.| -000002a0 29 ff e5 b3 f8 1f 58 2a a1 92 ea 2d 6d e9 71 d3 |).....X*...-m.q.| -000002b0 ce 04 46 ef d6 fb e4 5e e3 70 19 01 1a 22 85 70 |..F....^.p...".p| -000002c0 06 ec 15 bd ff ef af ed 2b 6d 00 97 cb 67 1f cb |........+m...g..| -000002d0 f6 14 03 03 00 01 01 16 03 03 00 28 00 00 00 00 |...........(....| -000002e0 00 00 00 00 80 da 31 59 19 74 39 ff a4 8c 30 1b |......1Y.t9...0.| -000002f0 25 fd 6b 25 57 ef 26 57 bd a1 df 90 4e d6 6a 47 |%.k%W.&W....N.jG| -00000300 96 08 54 92 |..T.| +00000240 00 8f 04 03 00 8b 30 81 88 02 42 01 0f 51 5e 59 |......0...B..Q^Y| +00000250 78 34 8f 99 03 da 07 66 3b 0d 48 b2 79 57 e2 d5 |x4.....f;.H.yW..| +00000260 d2 c2 f3 81 8e 25 98 81 e2 9a f7 1f 02 99 b0 7d |.....%.........}| +00000270 1c d1 1f e4 ef d7 bc a1 ad 67 c7 a9 cc 4f 67 58 |.........g...OgX| +00000280 8b 1e 8c 3f 04 73 31 53 60 aa 67 33 27 02 42 01 |...?.s1S`.g3'.B.| +00000290 f1 66 ba 8f ec 9e 3f 76 76 ac 7a e7 56 cb fb 46 |.f....?vv.z.V..F| +000002a0 f4 9b 64 03 3a 72 5a d7 cf 49 39 69 26 19 68 52 |..d.:rZ..I9i&.hR| +000002b0 8b 98 8e ea d3 8e d9 6d 93 f5 e8 23 cd 20 a8 5a |.......m...#. .Z| +000002c0 4c 24 10 70 bd a2 ae a3 b1 4f 38 17 dd b9 f5 93 |L$.p.....O8.....| +000002d0 4b 14 03 03 00 01 01 16 03 03 00 28 00 00 00 00 |K..........(....| +000002e0 00 00 00 00 e1 2b da c6 4a 5c d2 03 c0 7e f0 eb |.....+..J\...~..| +000002f0 a0 4b ed a1 7d e4 45 93 ec f9 37 a0 5b 7e bb 64 |.K..}.E...7.[~.d| +00000300 af d4 fc ac |....| >>> Flow 4 (server to client) -00000000 14 03 03 00 01 01 16 03 03 00 28 0e 1f ce 8a 46 |..........(....F| -00000010 77 28 6d e2 fc c4 e4 39 70 6b ab 6e 14 14 2a 34 |w(m....9pk.n..*4| -00000020 13 a0 5b c5 95 f5 fa a9 a2 f6 60 20 e3 1f c5 84 |..[.......` ....| -00000030 9c 00 5e |..^| +00000000 14 03 03 00 01 01 16 03 03 00 28 d3 4a 1e 2b ea |..........(.J.+.| +00000010 26 12 c9 fd b0 7b e6 bf e4 bb b6 d2 6b b4 3c 05 |&....{......k.<.| +00000020 1f 6c 46 44 5e 25 e6 f9 80 c8 b9 16 19 59 68 90 |.lFD^%.......Yh.| +00000030 5a 90 16 |Z..| >>> Flow 5 (client to server) -00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 88 d1 a4 |................| -00000010 c9 1b a6 a5 21 4d 93 e8 04 8b 3b 69 a9 2f bd 7d |....!M....;i./.}| -00000020 97 c1 3d 15 03 03 00 1a 00 00 00 00 00 00 00 02 |..=.............| -00000030 ba 48 db 22 9e ae d8 a9 24 b7 a6 52 13 92 68 d6 |.H."....$..R..h.| -00000040 aa b5 |..| +00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 35 25 df |.............5%.| +00000010 1f 16 81 00 e3 c4 9e 45 e2 a1 ef 54 72 66 99 3d |.......E...Trf.=| +00000020 30 13 25 15 03 03 00 1a 00 00 00 00 00 00 00 02 |0.%.............| +00000030 16 a5 e9 36 c1 fb 02 d7 c8 7a aa bc aa 77 7b 5c |...6.....z...w{\| +00000040 4f a1 |O.| diff --git a/testdata/Client-TLSv13-ClientCert-ECDSA-RSA b/testdata/Client-TLSv13-ClientCert-ECDSA-RSA index 8984790..db13bd6 100644 --- a/testdata/Client-TLSv13-ClientCert-ECDSA-RSA +++ b/testdata/Client-TLSv13-ClientCert-ECDSA-RSA @@ -17,124 +17,124 @@ 000000f0 bb 29 07 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 |.).0.........._X| 00000100 cb 3b 74 |.;t| >>> Flow 2 (server to client) -00000000 16 03 03 00 7a 02 00 00 76 03 03 ea 8b 99 cb 5b |....z...v......[| -00000010 d8 fb e9 14 7f 17 20 9c b8 41 01 dd ce 8a 90 4e |...... ..A.....N| -00000020 a9 f0 fb eb 71 37 24 02 d2 ee 96 20 00 00 00 00 |....q7$.... ....| +00000000 16 03 03 00 7a 02 00 00 76 03 03 37 0c 23 2f 26 |....z...v..7.#/&| +00000010 2a b0 8d 47 84 3b 9b 9c 7e 0f 0a cd 77 39 6c c2 |*..G.;..~...w9l.| +00000020 7b c0 56 a8 9d 07 a0 ec b6 e5 79 20 00 00 00 00 |{.V.......y ....| 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| 00000040 00 00 00 00 00 00 00 00 00 00 00 00 13 03 00 00 |................| -00000050 2e 00 2b 00 02 03 04 00 33 00 24 00 1d 00 20 c8 |..+.....3.$... .| -00000060 c6 52 8e 3c 6f 0d ab 2d be 4c e0 01 c8 af 1f 8e |.R........t| -00000100 69 88 5d 8e a4 fa 02 b0 6f f4 0e 38 cc 95 12 e2 |i.].....o..8....| -00000110 f1 e3 47 89 a6 1d 80 26 cb 23 6e f3 83 1c e4 85 |..G....&.#n.....| -00000120 7b 1a 4d 12 c8 bf ff 07 39 a9 4e 4e d7 45 35 23 |{.M.....9.NN.E5#| -00000130 9b f9 59 6d a5 b0 49 1b 5f e7 42 62 17 00 1e 57 |..Ym..I._.Bb...W| -00000140 53 c5 22 fb 05 89 fd fe 5d de 71 e8 26 fd 6d e3 |S.".....].q.&.m.| -00000150 fc b9 cb 1f d5 d4 84 d1 67 fe 8a a0 74 ff ad ff |........g...t...| -00000160 c8 35 fe c2 9a 17 33 18 51 c2 cd 19 7c dc 5d c5 |.5....3.Q...|.].| -00000170 7c e6 d0 38 ab 28 0b 8c 78 51 aa 7f ef f0 9a c3 ||..8.(..xQ......| -00000180 df 26 d2 bc 1b b6 98 b1 16 35 9d f0 73 b7 15 f7 |.&.......5..s...| -00000190 7d 9f 3e fe 4c 75 e7 c7 5d cb c2 e5 29 4a 30 32 |}.>.Lu..]...)J02| -000001a0 e2 da 3c 2c 16 ba 89 41 28 5c 33 75 b3 ed d1 e6 |..<,...A(\3u....| -000001b0 4f f6 bb 33 62 53 9d be fe d3 53 b5 bd 3e e3 b5 |O..3bS....S..>..| -000001c0 0a 37 67 60 33 c3 72 a8 ea 55 73 3c b2 7f ed 8b |.7g`3.r..Us<....| -000001d0 59 5e 44 e4 76 0d 1e 3c 3e 1c 9c 8c 86 3d 0a a3 |Y^D.v..<>....=..| -000001e0 78 bd 13 77 eb dc 22 e5 96 ff ae 44 94 cd ef ae |x..w.."....D....| -000001f0 ca 64 ec 06 a1 38 49 17 ce a5 c4 34 86 fd 55 1f |.d...8I....4..U.| -00000200 32 31 d5 b0 8c d6 b5 bc b8 29 29 97 b5 39 a0 f8 |21.......))..9..| -00000210 b1 b5 72 24 ff ce 6f 7d 6a 0d 18 26 8f 9a e8 d3 |..r$..o}j..&....| -00000220 e6 e0 7c 9c 56 45 ca 48 44 69 05 53 40 eb 96 c5 |..|.VE.HDi.S@...| -00000230 17 aa 28 56 20 ee 82 fd de d1 55 b8 e9 27 ae 3d |..(V .....U..'.=| -00000240 e1 44 d6 0b b9 7a 25 77 b0 f1 be 64 ae cc 0d 44 |.D...z%w...d...D| -00000250 af 57 32 9f cf bc c0 07 00 0b 19 97 08 0a d5 50 |.W2............P| -00000260 20 0e ef d5 1e 2e 68 82 ae 2d 84 47 3d 9b 5b 24 | .....h..-.G=.[$| -00000270 55 95 b2 93 e0 60 a2 cd e5 72 69 b3 e2 de da 70 |U....`...ri....p| -00000280 76 88 be 1f 5a 8e 7a d8 ff 94 db b0 92 b2 a1 a1 |v...Z.z.........| -00000290 26 5b 3b d5 5e 67 16 b7 6a 9f dc ab 21 7e df 6e |&[;.^g..j...!~.n| -000002a0 2a 73 e9 20 40 4b c8 34 fc 4b be f6 d8 ba 78 d7 |*s. @K.4.K....x.| -000002b0 a4 c2 ed a9 52 f9 ea 88 67 5e 10 92 8f ba 83 cd |....R...g^......| -000002c0 19 79 82 c5 76 06 d9 98 9f 68 2e 34 82 2f 9a 41 |.y..v....h.4./.A| -000002d0 fb 63 b6 8c 79 56 62 9d bb bc b5 22 ab 28 35 f7 |.c..yVb....".(5.| -000002e0 bc c6 a8 6b e7 86 01 c3 6c e5 88 28 57 09 65 31 |...k....l..(W.e1| -000002f0 b2 5c 0a 60 46 af 94 2c d4 37 49 9b 4c e4 4b bb |.\.`F..,.7I.L.K.| -00000300 fb 0d 87 94 82 11 09 26 04 6f ec e9 a6 f4 9c ca |.......&.o......| -00000310 79 a0 d0 48 32 5a 02 58 70 81 d0 b6 6d 77 2f 6f |y..H2Z.Xp...mw/o| -00000320 4e 9b db ca 82 38 ed a6 60 8e e2 b6 14 33 f5 02 |N....8..`....3..| -00000330 4b 97 a4 86 2f 43 9d ee 88 2c 1c 58 7e 47 30 cc |K.../C...,.X~G0.| -00000340 ec 0d aa 22 13 60 be e2 c9 c4 59 90 67 96 9b 2a |...".`....Y.g..*| -00000350 41 79 49 00 71 80 e9 0d 12 c3 17 03 03 00 99 ec |AyI.q...........| -00000360 f5 a1 45 64 61 fc 61 35 d5 2f bf 20 02 68 0b 10 |..Eda.a5./. .h..| -00000370 df c4 4b e7 2c 43 bc f5 3d 0b 7e 9f a4 71 09 2c |..K.,C..=.~..q.,| -00000380 a6 cf f4 f4 b4 2c 08 0c 03 50 ac 47 74 ad 24 f1 |.....,...P.Gt.$.| -00000390 04 f3 d4 83 42 3f 35 a5 57 ff ab 59 0c 9a a2 ca |....B?5.W..Y....| -000003a0 6c 30 b7 87 73 af 53 f9 1d 6b e7 44 ec d1 bd 14 |l0..s.S..k.D....| -000003b0 15 09 cf ff 82 5e a2 6d ba 00 53 b8 b3 7c 0e e5 |.....^.m..S..|..| -000003c0 d1 e2 a2 38 25 88 aa ee 93 c8 d9 d1 88 42 42 90 |...8%........BB.| -000003d0 43 8d 22 d8 48 02 57 22 6a f4 e9 23 71 f0 64 30 |C.".H.W"j..#q.d0| -000003e0 6a 68 12 a5 3c 8c 61 5e bc 73 91 6a 01 3a 14 14 |jh..<.a^.s.j.:..| -000003f0 86 7c 4d f5 aa cc 2f f5 17 03 03 00 35 7e f6 f5 |.|M.../.....5~..| -00000400 6b 75 e2 63 64 5a 6b 9b ce 6b 76 d7 47 bc 1b 47 |ku.cdZk..kv.G..G| -00000410 9e 68 25 fe 47 2b 06 a9 72 d0 a7 3f 23 3d 71 4a |.h%.G+..r..?#=qJ| -00000420 da 29 fb e3 dd ee e6 f6 a5 32 cc eb da 61 2b 52 |.).......2...a+R| -00000430 24 26 |$&| +00000050 2e 00 2b 00 02 03 04 00 33 00 24 00 1d 00 20 5e |..+.....3.$... ^| +00000060 9a da 1d cb 90 03 f2 d2 23 e3 54 fc 3d 9b 8c 92 |........#.T.=...| +00000070 42 df cf 7a 3d 47 3f 66 a4 a6 7a 07 44 76 5c 14 |B..z=G?f..z.Dv\.| +00000080 03 03 00 01 01 17 03 03 00 17 53 54 e6 de 6a af |..........ST..j.| +00000090 c2 d9 b8 39 b6 ae 3e 9f 54 60 e1 fa 29 5a fb 16 |...9..>.T`..)Z..| +000000a0 20 17 03 03 00 42 f4 a5 7f e3 e7 ba 6a 88 e1 f2 | ....B......j...| +000000b0 fd 25 3e ba 05 00 29 77 ff 69 6e e0 ac 50 99 50 |.%>...)w.in..P.P| +000000c0 ac 1e bd 8c 52 e6 28 5d 67 bb bb 20 61 69 5b 97 |....R.(]g.. ai[.| +000000d0 7f 29 79 97 bf 13 24 25 ad 3b 01 78 c4 a4 4e 9a |.)y...$%.;.x..N.| +000000e0 06 d6 20 da 63 27 97 8d 17 03 03 02 6d 79 7e 9e |.. .c'......my~.| +000000f0 01 98 a9 7c ba 63 43 2b 21 b1 bc 2c b2 17 c2 35 |...|.cC+!..,...5| +00000100 76 f2 30 01 69 45 d4 56 e0 5a 2c 62 aa 6f a8 1f |v.0.iE.V.Z,b.o..| +00000110 b8 31 df be 6f 3f 60 16 dc 61 5c 9f 99 a9 63 7d |.1..o?`..a\...c}| +00000120 7e 2b d2 ae 02 46 12 db be 51 9e 15 dd 1c 96 b0 |~+...F...Q......| +00000130 74 69 20 c0 e1 78 46 01 a6 23 72 28 ba c7 a3 48 |ti ..xF..#r(...H| +00000140 1e dc 0b 57 c9 b4 39 88 3d 39 c7 6c 38 c7 3a 29 |...W..9.=9.l8.:)| +00000150 a4 45 79 10 04 61 cd db df 5b 88 c1 35 4b 38 ea |.Ey..a...[..5K8.| +00000160 6d 72 57 9f e0 2e 37 61 3d c8 aa b2 25 a6 11 5c |mrW...7a=...%..\| +00000170 09 e2 3a 17 d3 c5 37 2f a7 b4 73 fe e2 61 df 1d |..:...7/..s..a..| +00000180 cd 4f 72 4a 67 c3 c7 e2 53 78 61 78 2c 37 44 12 |.OrJg...Sxax,7D.| +00000190 4d 0e 8c 14 0b de 3b a4 cf ad 8f d4 74 61 77 4b |M.....;.....tawK| +000001a0 36 2d f2 8f 68 95 38 9d e2 9f cf cc 03 15 89 b9 |6-..h.8.........| +000001b0 96 c4 47 e5 2f 65 0a 5c 5d 8f 5c 64 9d c8 76 d2 |..G./e.\].\d..v.| +000001c0 5d a7 90 4d f5 84 2d 31 2d 6c bd ee 0d 45 2b 50 |]..M..-1-l...E+P| +000001d0 79 f7 8c 34 40 f9 bc f4 38 b3 56 a9 6b ca 54 50 |y..4@...8.V.k.TP| +000001e0 19 f8 9a 73 74 9b 0a 92 ee 22 53 05 01 38 43 3a |...st...."S..8C:| +000001f0 49 fe 2d e9 39 c1 76 b0 04 df 8a 3d cc fc 9b 84 |I.-.9.v....=....| +00000200 cd 22 ba 40 24 69 93 b9 c5 b3 ed fd ad 94 1b 83 |.".@$i..........| +00000210 b5 07 a9 e7 94 14 4b c1 59 89 78 56 03 28 29 c3 |......K.Y.xV.().| +00000220 a8 a4 96 14 5a 51 9a 50 f9 34 3c 5a 76 8f 74 68 |....ZQ.P.4..K..k*D?.#..U| +000002f0 a0 11 51 04 0b 82 02 d9 24 85 13 2e d1 29 44 9a |..Q.....$....)D.| +00000300 15 7f a4 1b c4 f5 36 44 88 9a 6e 5a 1e 2f 14 fa |......6D..nZ./..| +00000310 d0 e7 fc 6a fa e5 f3 4a 55 20 73 9b e4 73 2e 47 |...j...JU s..s.G| +00000320 88 25 b7 69 d9 28 fe 50 8c fc f2 94 29 84 c4 7f |.%.i.(.P....)...| +00000330 d6 b2 eb 28 fa 51 8e ff 00 09 35 d3 b2 32 3a c6 |...(.Q....5..2:.| +00000340 bb 91 a7 c4 b7 88 df 4b f7 09 ef e7 e1 92 60 cd |.......K......`.| +00000350 de 34 4f 39 ee b8 ce 50 6a b9 17 03 03 00 99 64 |.4O9...Pj......d| +00000360 76 ab 48 eb 68 7d a6 68 60 aa c2 93 bd 31 81 c8 |v.H.h}.h`....1..| +00000370 b6 15 ba d1 54 94 04 1b 4b 29 86 e1 12 84 ad d5 |....T...K)......| +00000380 ba eb 4a 7a 7a a8 56 41 04 8c 84 c7 83 46 8c 50 |..Jzz.VA.....F.P| +00000390 c4 e3 02 d0 28 a4 fe 24 c4 b8 96 13 4f 87 27 ec |....(..$....O.'.| +000003a0 6b e3 84 4b 97 13 65 fa 1e 5e 9d ac 85 ea a0 3d |k..K..e..^.....=| +000003b0 67 96 e5 ec 88 84 6b 79 d9 16 55 c1 1c 72 17 aa |g.....ky..U..r..| +000003c0 9b 49 13 86 d4 39 0d 2a c8 88 b3 5f f5 11 cb 5f |.I...9.*..._..._| +000003d0 bd 22 57 2c bc c1 01 72 b9 c3 f5 d9 a2 3b 8e ff |."W,...r.....;..| +000003e0 44 b2 82 b6 5b 35 75 b5 7a 50 40 81 4e a7 2d 40 |D...[5u.zP@.N.-@| +000003f0 21 28 d9 c4 d9 e5 07 e1 17 03 03 00 35 23 ab b2 |!(..........5#..| +00000400 60 56 47 6a fe cb 0e 54 22 d5 8f 29 0a 34 e6 82 |`VGj...T"..).4..| +00000410 5b 10 35 ac 93 97 92 6b 39 5d b8 01 54 9c 86 b0 |[.5....k9]..T...| +00000420 41 70 52 88 92 cf dd b7 f8 5d d1 18 e1 1f 78 53 |ApR......]....xS| +00000430 e4 43 |.C| >>> Flow 3 (client to server) -00000000 14 03 03 00 01 01 17 03 03 02 1e 67 f8 08 c4 15 |...........g....| -00000010 47 a9 da 91 df f1 61 0f 50 21 f6 da dd a9 a6 e4 |G.....a.P!......| -00000020 00 05 29 62 d2 cc fe 14 57 a2 aa 46 16 b3 46 6e |..)b....W..F..Fn| -00000030 82 87 dc bb 1a d9 e7 c6 e9 1a 3b 7e a5 94 9b 7e |..........;~...~| -00000040 bb 07 b8 f3 de cc f1 85 d5 ee ff 0b 5a 19 c1 e3 |............Z...| -00000050 5f 47 f4 81 f4 d3 2d 85 f8 38 90 00 10 54 9a 3e |_G....-..8...T.>| -00000060 56 e4 99 a5 31 b1 dc d4 77 fe 28 3a b4 3e 63 42 |V...1...w.(:.>cB| -00000070 bc 05 c7 8a e5 d9 01 5c c9 18 39 1e 62 4f b4 58 |.......\..9.bO.X| -00000080 d3 9a 6a e1 a4 d3 ef 7f b8 0f 35 ac 2a 4a ba 77 |..j.......5.*J.w| -00000090 24 1e 24 6e 0e a2 8e f1 ba 5f fe 24 03 ed 1d e7 |$.$n....._.$....| -000000a0 43 2d fb bb 2e a9 a5 9a 18 11 39 99 a1 5a b7 92 |C-........9..Z..| -000000b0 37 91 f2 80 b2 f6 57 87 f9 a5 d9 da 36 b5 db 1a |7.....W.....6...| -000000c0 28 d8 9e f7 92 6f cf 0c 57 9a 95 42 ef 6a 50 f6 |(....o..W..B.jP.| -000000d0 38 4f 74 52 09 34 ca 8a d8 c7 a2 d2 69 bb db 13 |8OtR.4......i...| -000000e0 b1 ef f2 6d d3 f0 dd 5d b7 93 25 87 84 cd 87 6d |...m...]..%....m| -000000f0 c7 24 99 2b c8 02 25 1c 58 bc 98 03 33 15 ee 3e |.$.+..%.X...3..>| -00000100 96 d0 af 82 c7 74 71 ec ef a6 eb 88 20 55 1b fa |.....tq..... U..| -00000110 ea 01 38 5b 68 77 f1 3b 2a e6 d1 96 bc 28 b3 97 |..8[hw.;*....(..| -00000120 47 94 14 3f 6b 73 82 bf b0 ba fe 45 2a 45 45 1c |G..?ks.....E*EE.| -00000130 30 68 f5 74 07 15 18 00 a7 4f 62 df 7c ff 4c 0a |0h.t.....Ob.|.L.| -00000140 93 d6 60 8b 1f 0e 7f d3 88 43 90 f6 18 05 f1 ae |..`......C......| -00000150 ac 04 63 8b 43 b8 11 74 b1 87 e6 bc 2d 72 4a 2e |..c.C..t....-rJ.| -00000160 75 ab 16 5e 0e bc 76 6d d6 0e bc 39 0d 3c 76 01 |u..^..vm...9.fDcKC.%(D.-.| -000001b0 39 0e cd 15 a3 04 d9 db 4f a4 03 cb d2 84 da 41 |9.......O......A| -000001c0 5c 60 bf 00 77 19 68 b1 50 99 c2 88 d2 56 1e 29 |\`..w.h.P....V.)| -000001d0 c9 8c 83 98 7a 2f 04 e6 d6 09 5c 27 af a7 69 c4 |....z/....\'..i.| -000001e0 f7 4a ed b5 db d3 7a aa 75 f1 b2 8d 87 15 9a d1 |.J....z.u.......| -000001f0 00 fa 7f 58 06 2d 72 2b f7 27 b3 5b 76 57 21 35 |...X.-r+.'.[vW!5| -00000200 80 ce 8e bd e4 bb 45 01 c4 6b 43 f1 44 0a 5f 21 |......E..kC.D._!| -00000210 76 48 ae ce 8e 1d ba f7 7c 4f ae a2 d8 77 ce 9c |vH......|O...w..| -00000220 6d 69 b7 1e 78 ab 02 ed 15 17 03 03 00 a3 84 ed |mi..x...........| -00000230 ba 37 b6 6f 96 68 ef 9e b7 c6 23 5a 9f 1b ed ad |.7.o.h....#Z....| -00000240 3b 5d ab d4 22 d8 3e ab db db c5 b9 57 f8 68 d1 |;]..".>.....W.h.| -00000250 93 d5 36 fe 0c c0 fe 29 88 8b 63 ac 0e 06 4e bd |..6....)..c...N.| -00000260 9e b9 24 65 b5 9c e7 b9 58 4c 8a 07 10 9c 17 f7 |..$e....XL......| -00000270 c0 67 af ff c8 ff fc 87 1b fa c8 21 21 17 2d 43 |.g.........!!.-C| -00000280 f5 fc 4f 0d bf 01 58 b6 f1 58 08 39 f4 0d 94 69 |..O...X..X.9...i| -00000290 8f f0 c1 14 93 41 56 32 41 11 84 58 73 13 69 2a |.....AV2A..Xs.i*| -000002a0 ed 2a 34 61 73 8d 47 41 62 33 39 66 fa 3d 2a e5 |.*4as.GAb39f.=*.| -000002b0 bf 09 d6 c0 1e 3c 98 b3 86 a6 87 b5 a7 d2 cf d9 |.....<..........| -000002c0 dd f8 2e 86 f7 13 84 4a f7 3b ec 8e e5 06 f5 cd |.......J.;......| -000002d0 42 17 03 03 00 35 b2 38 87 30 58 9e 03 6e 44 dd |B....5.8.0X..nD.| -000002e0 fb 87 11 3a a0 e7 c1 2d 74 3b 35 d0 3f bc de cd |...:...-t;5.?...| -000002f0 71 61 8b 7c a5 7e c6 2d 76 67 44 9e 75 e5 9b 3b |qa.|.~.-vgD.u..;| -00000300 c5 2b 42 8a 4a 7f 0e 12 4c 2e 0f 17 03 03 00 17 |.+B.J...L.......| -00000310 d3 61 ed 75 dd 2e ee dd 79 fe d1 7c 4d 23 b1 95 |.a.u....y..|M#..| -00000320 ea 14 d6 27 d0 02 46 17 03 03 00 13 84 c1 07 6f |...'..F........o| -00000330 1c c6 22 a8 ae 6d a8 e8 62 54 ac b2 53 57 bb |.."..m..bT..SW.| +00000000 14 03 03 00 01 01 17 03 03 02 1e 19 c5 11 9d 64 |...............d| +00000010 8f f5 a9 21 53 fa cc 91 67 30 39 c0 77 d6 35 7b |...!S...g09.w.5{| +00000020 b8 a3 ae 44 8a 9a b1 68 5a 20 72 a6 ae 3a 1b 9f |...D...hZ r..:..| +00000030 03 eb d9 ed 91 20 49 ba 88 39 99 1c 4e 3a 2b 1b |..... I..9..N:+.| +00000040 42 b7 a3 97 a3 a3 6c 7e 3b 4c c1 74 dc 71 e6 14 |B.....l~;L.t.q..| +00000050 6c 5a 36 12 cb 87 a6 75 ce b3 e3 a8 f2 c8 36 12 |lZ6....u......6.| +00000060 3d c8 b8 2a 36 e4 40 38 3e 20 1d de 2a 31 b1 04 |=..*6.@8> ..*1..| +00000070 86 cb 9b c1 f3 fc 01 67 7e 40 0b b8 c4 fa 8f a0 |.......g~@......| +00000080 a7 5b 24 43 a9 d3 eb 55 99 ec 0b 19 cb ec 19 97 |.[$C...U........| +00000090 c8 0f c0 5e 8c b2 b1 93 80 70 7c 0b aa 7c 6c 44 |...^.....p|..|lD| +000000a0 1a 11 dc bd 0d 97 18 f3 ca c6 50 68 ca 77 ab 18 |..........Ph.w..| +000000b0 79 a9 8b 73 13 48 90 c3 4a cd ae f2 60 ea 0c 20 |y..s.H..J...`.. | +000000c0 eb ad 84 fb 2f 01 7e 2c f6 7d ea da 22 59 5f 88 |..../.~,.}.."Y_.| +000000d0 ff 10 19 81 d5 29 1b 0f 36 7b 84 66 eb bf e3 f9 |.....)..6{.f....| +000000e0 1c 68 fa 03 93 3e ba c6 58 e2 a9 57 94 8c a8 29 |.h...>..X..W...)| +000000f0 e2 f9 4b 6d 85 01 e8 f2 11 a2 04 38 73 8e 69 49 |..Km.......8s.iI| +00000100 4b 7f ca be aa 5f 50 ac 82 16 e6 92 78 87 13 f4 |K...._P.....x...| +00000110 fc 21 e8 2d 89 d0 f7 fb 73 0b f3 b7 6a 67 24 e8 |.!.-....s...jg$.| +00000120 d9 33 59 49 d6 88 24 a2 66 f5 c8 4d fe 88 93 77 |.3YI..$.f..M...w| +00000130 f9 3f ee ae 0b 6a 23 7a 8f b7 66 d4 68 7d 38 51 |.?...j#z..f.h}8Q| +00000140 85 0a a0 f5 03 f6 e8 2f cd 0b ac 58 64 82 38 20 |......./...Xd.8 | +00000150 f2 72 0a 85 83 55 cb db 62 59 f4 40 08 28 f9 8a |.r...U..bY.@.(..| +00000160 47 a2 ea a1 1b e1 4c 0a a4 74 cb a2 11 6f e5 68 |G.....L..t...o.h| +00000170 e4 ff 38 b0 a5 fc 21 9e eb de 43 b6 e7 27 cf 9a |..8...!...C..'..| +00000180 80 23 59 a2 e9 a8 12 ae 47 09 5c 48 c2 cb c8 e0 |.#Y.....G.\H....| +00000190 a6 fa 81 c5 49 a4 77 4b d3 83 0a ce 6e a5 2b 88 |....I.wK....n.+.| +000001a0 f2 f5 12 2f 0e 7e 10 20 5b c7 31 39 54 ed 19 33 |.../.~. [.19T..3| +000001b0 5c 94 b5 56 16 fa 0c b0 ec 28 76 fa 38 ca 08 c6 |\..V.....(v.8...| +000001c0 13 c3 1f 8a 20 35 73 4b bb bf d8 96 65 de cd f3 |.... 5sK....e...| +000001d0 44 d4 5b 3d 54 aa ac 53 a9 cc 31 99 86 22 5a f9 |D.[=T..S..1.."Z.| +000001e0 9e bd f1 f3 74 07 e4 fe f7 3a 35 44 e5 c6 48 3f |....t....:5D..H?| +000001f0 a3 81 1e 67 96 51 0f e6 7d 43 67 9c 12 6c dd 91 |...g.Q..}Cg..l..| +00000200 c4 f9 20 4d 88 41 fc 40 c5 ee c2 11 fb f1 67 da |.. M.A.@......g.| +00000210 7b b6 d0 1b f8 6e f7 8b 07 f2 9e 12 56 dc 75 31 |{....n......V.u1| +00000220 cd b9 53 62 3f 2f 72 cf ee 17 03 03 00 a4 f9 ec |..Sb?/r.........| +00000230 72 94 94 1c 52 ab 9e 6d 04 5a 26 07 15 3a f5 dd |r...R..m.Z&..:..| +00000240 f3 45 18 20 de 2e 97 f7 6a a4 7c 92 68 aa 71 55 |.E. ....j.|.h.qU| +00000250 b7 7a 3c 8f 54 e7 cc 31 e1 54 9c ad 8e b4 57 11 |.z<.T..1.T....W.| +00000260 1d 79 85 4a da 3f 1b ab fb f4 d4 d9 4d 8a 2e da |.y.J.?......M...| +00000270 68 3e f9 aa 16 52 cc 4e 49 7a 00 bf fc e8 b5 16 |h>...R.NIz......| +00000280 43 0c 6d aa 82 49 3c 16 43 56 55 35 ee 47 c3 1c |C.m..I<.CVU5.G..| +00000290 99 25 6d 30 89 64 5e 23 bf de fc cc 7c 40 94 28 |.%m0.d^#....|@.(| +000002a0 d8 ed ec c2 e2 8b 24 64 64 b6 e8 6c 29 82 b5 ba |......$dd..l)...| +000002b0 d5 59 7a 6f 11 6e cc 30 91 c8 c0 8b 9f dd 13 59 |.Yzo.n.0.......Y| +000002c0 a9 72 18 f6 6e ce 3a 47 6b 4f 26 55 61 bf 20 7a |.r..n.:GkO&Ua. z| +000002d0 d8 c3 17 03 03 00 35 d7 fc bc 10 29 75 c3 70 0a |......5....)u.p.| +000002e0 02 e4 cf 36 20 49 5d 64 78 e9 27 db 2d e3 1a 66 |...6 I]dx.'.-..f| +000002f0 f3 a8 82 7e 64 f0 29 27 81 6c bd b7 a7 86 a2 6a |...~d.)'.l.....j| +00000300 ac 4e 7b da 48 7c d8 9c 39 6c 95 45 17 03 03 00 |.N{.H|..9l.E....| +00000310 17 c9 13 54 e9 22 62 7e 89 17 de 98 52 93 26 76 |...T."b~....R.&v| +00000320 73 a0 7d 2c 60 68 c8 68 17 03 03 00 13 ad 3a 53 |s.},`h.h......:S| +00000330 d1 41 0e 99 26 c8 fb 22 8f e6 d3 a4 1d 83 ff 28 |.A..&..".......(| diff --git a/testdata/Server-TLSv10-ECDHE-ECDSA-AES b/testdata/Server-TLSv10-ECDHE-ECDSA-AES index c5d947c..3bc3a75 100644 --- a/testdata/Server-TLSv10-ECDHE-ECDSA-AES +++ b/testdata/Server-TLSv10-ECDHE-ECDSA-AES @@ -1,7 +1,7 @@ >>> Flow 1 (client to server) -00000000 16 03 01 00 51 01 00 00 4d 03 01 5e bf ff e7 c2 |....Q...M..^....| -00000010 c1 98 4a a3 cf 5a e8 8d 8f 19 9e 85 48 5b 92 cc |..J..Z......H[..| -00000020 7d 0c 14 1e 2e 50 5b d7 dd fe ef 00 00 04 c0 0a |}....P[.........| +00000000 16 03 01 00 51 01 00 00 4d 03 01 76 4b 58 ef 57 |....Q...M..vKX.W| +00000010 d5 8d ba b7 0b fe d4 b3 2b a7 76 7c f1 72 59 39 |........+.v|.rY9| +00000020 fa 02 66 88 4a 55 72 15 9e 42 8c 00 00 04 c0 0a |..f.JUr..B......| 00000030 00 ff 01 00 00 20 00 0b 00 04 03 00 01 02 00 0a |..... ..........| 00000040 00 0c 00 0a 00 1d 00 17 00 1e 00 19 00 18 00 16 |................| 00000050 00 00 00 17 00 00 |......| @@ -43,37 +43,37 @@ 00000220 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 |.3.......7z..z..| 00000230 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 |....i..|V..1x+..| 00000240 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 |x.....N6$1{j.9..| -00000250 07 8f 2a 16 03 01 00 b5 0c 00 00 b1 03 00 1d 20 |..*............ | +00000250 07 8f 2a 16 03 01 00 b3 0c 00 00 af 03 00 1d 20 |..*............ | 00000260 2f e5 7d a3 47 cd 62 43 15 28 da ac 5f bb 29 07 |/.}.G.bC.(.._.).| 00000270 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 cb 3b 74 |0.........._X.;t| -00000280 00 8b 30 81 88 02 42 00 c3 eb 60 b8 d3 af cb 2d |..0...B...`....-| -00000290 4f ca 46 6d e4 fe 47 41 82 1e d4 14 0f 08 ab b6 |O.Fm..GA........| -000002a0 b8 41 8b 46 f5 28 bb 87 28 73 a0 5c e9 ce f5 56 |.A.F.(..(s.\...V| -000002b0 11 02 17 2c 39 b6 28 6c ec de 12 bf 22 91 3d 06 |...,9.(l....".=.| -000002c0 ac 8e 0a 92 b1 46 69 86 44 02 42 01 fd 70 6e 63 |.....Fi.D.B..pnc| -000002d0 1b 2a 21 47 9b 42 9c d4 4a 38 20 dd 94 05 c4 0f |.*!G.B..J8 .....| -000002e0 5d b2 48 c8 17 90 01 4d 4f 7e 7a ef bb b2 5b 26 |].H....MO~z...[&| -000002f0 7e e1 24 f5 80 93 69 72 3f cf bb 5d 52 ee ec b4 |~.$...ir?..]R...| -00000300 cc 0c 96 1f 93 4c d6 a8 c7 b2 91 f5 6d 16 03 01 |.....L......m...| -00000310 00 04 0e 00 00 00 |......| +00000280 00 89 30 81 86 02 41 00 b5 7c a4 63 77 fa 75 cd |..0...A..|.cw.u.| +00000290 82 a5 75 15 08 09 e8 6d e9 ba 07 1f f9 9c 24 a5 |..u....m......$.| +000002a0 30 08 d0 51 3b d1 82 14 14 dd 5a 5d c9 2d 91 6f |0..Q;.....Z].-.o| +000002b0 b3 92 30 f1 38 36 e8 34 9e 99 50 a0 c4 29 04 ef |..0.86.4..P..)..| +000002c0 97 f3 cd dc be 22 86 b9 02 41 6a dd 3a 57 5b 61 |....."...Aj.:W[a| +000002d0 ff 68 7d 1e 5e bb 67 5f 76 44 7c f2 f2 03 95 f2 |.h}.^.g_vD|.....| +000002e0 e0 1a 53 70 ce b0 fa cc 7a f3 9a e3 2f 37 a3 cf |..Sp....z.../7..| +000002f0 b5 ca 1d fb fe a3 0d e2 d6 c1 d2 7d 48 80 5b 82 |...........}H.[.| +00000300 56 29 1b b7 43 2e b3 38 19 39 49 16 03 01 00 04 |V)..C..8.9I.....| +00000310 0e 00 00 00 |....| >>> Flow 3 (client to server) -00000000 16 03 01 00 25 10 00 00 21 20 ec f2 2d ca 02 ce |....%...! ..-...| -00000010 11 2d eb 26 d7 d9 fc b2 a7 2d 34 5b a9 3a 0b 2f |.-.&.....-4[.:./| -00000020 5c 49 a9 69 1a 3a 83 90 ec 5f 14 03 01 00 01 01 |\I.i.:..._......| -00000030 16 03 01 00 30 9f 06 c7 a7 a0 c3 a5 3d 60 6e fb |....0.......=`n.| -00000040 c6 18 a4 d2 80 2e ad 8f cf 92 84 94 36 f8 81 28 |............6..(| -00000050 c5 3f 37 e8 d6 e7 6d a3 f5 32 63 a0 ab 7a db 12 |.?7...m..2c..z..| -00000060 17 e1 e4 33 d6 |...3.| +00000000 16 03 01 00 25 10 00 00 21 20 82 03 ad 10 45 0a |....%...! ....E.| +00000010 b9 a4 85 0d 88 bb 9e 16 f1 6c 6a 17 c0 11 09 48 |.........lj....H| +00000020 b4 8b 27 4e 3a 45 a1 b7 a2 03 14 03 01 00 01 01 |..'N:E..........| +00000030 16 03 01 00 30 33 5d a1 85 df 96 6d cf a1 b3 c4 |....03]....m....| +00000040 3f 3c 40 aa 05 25 af 62 ee e9 ce 48 ba e8 08 88 |?<@..%.b...H....| +00000050 95 77 c7 f1 87 c6 ce 46 a2 50 2f 41 3c 8f bf 1a |.w.....F.P/A<...| +00000060 1e 1e 1b 39 9c |...9.| >>> Flow 4 (server to client) -00000000 14 03 01 00 01 01 16 03 01 00 30 18 29 35 d7 c5 |..........0.)5..| -00000010 a2 31 3b 26 85 de 50 26 39 4d 16 22 58 a2 17 bd |.1;&..P&9M."X...| -00000020 4b 73 33 8d dc 3f 92 20 f2 ca 22 00 f5 31 db a7 |Ks3..?. .."..1..| -00000030 18 79 fc 71 87 68 a5 1d a6 db 33 17 03 01 00 20 |.y.q.h....3.... | -00000040 0d be 57 e4 12 6d 2d 3a 33 24 a0 0c c4 9b 27 09 |..W..m-:3$....'.| -00000050 85 e0 0e 42 04 79 21 9a bf 47 fa 0b 38 1a ce 8f |...B.y!..G..8...| -00000060 17 03 01 00 30 6d 27 f1 9b cf 55 4d 65 48 38 1b |....0m'...UMeH8.| -00000070 d9 dd 1d 5b 81 2f 10 a5 65 28 83 93 b3 b1 3a 72 |...[./..e(....:r| -00000080 f0 15 9a e5 9f 21 80 f1 59 a5 0e f1 0c 2b d1 0c |.....!..Y....+..| -00000090 d4 27 73 f3 7e 15 03 01 00 20 6f 08 27 3a d2 60 |.'s.~.... o.':.`| -000000a0 c3 27 bc 73 55 bb 43 53 e2 e0 87 16 ca 8f 49 f0 |.'.sU.CS......I.| -000000b0 88 a8 20 30 9d 42 86 d9 c3 36 |.. 0.B...6| +00000000 14 03 01 00 01 01 16 03 01 00 30 7b 30 af df 92 |..........0{0...| +00000010 2b ee 4d 02 e3 6c 6f 8b 72 32 16 0e 4d ba 71 0d |+.M..lo.r2..M.q.| +00000020 86 0d f5 7d fe dd 07 05 3d fe 70 9b 7f d9 2b c6 |...}....=.p...+.| +00000030 7e 04 82 5f ef 0c 0b c1 e7 a5 18 17 03 01 00 20 |~.._........... | +00000040 ad bd 43 a6 10 e8 e2 41 39 35 69 af a0 00 5f 81 |..C....A95i..._.| +00000050 1e 0a 5e 78 45 2f 66 27 cb 4f db 06 22 c0 ea 0f |..^xE/f'.O.."...| +00000060 17 03 01 00 30 16 69 7f fa 1c 89 36 9f 99 c6 49 |....0.i....6...I| +00000070 e6 0d 9a b7 81 00 75 f5 2d cc 89 e8 be 47 64 76 |......u.-....Gdv| +00000080 ef 34 27 a2 46 bd 8c 14 5b 15 67 ab f1 d9 30 c3 |.4'.F...[.g...0.| +00000090 df 96 a1 59 17 15 03 01 00 20 77 ea 6f e2 ae 81 |...Y..... w.o...| +000000a0 80 4f 37 7b ee 37 3f 40 df a3 e4 be 80 1e 3e 83 |.O7{.7?@......>.| +000000b0 9b 42 f7 95 50 af ab a5 60 54 |.B..P...`T| diff --git a/testdata/Server-TLSv12-ECDHE-ECDSA-AES b/testdata/Server-TLSv12-ECDHE-ECDSA-AES index 697b810..f0d266b 100644 --- a/testdata/Server-TLSv12-ECDHE-ECDSA-AES +++ b/testdata/Server-TLSv12-ECDHE-ECDSA-AES @@ -1,7 +1,7 @@ >>> Flow 1 (client to server) -00000000 16 03 01 00 85 01 00 00 81 03 03 83 21 a6 e4 ea |............!...| -00000010 e9 7b 3a 7c 72 28 ee 68 c5 c7 fa f1 98 ed 4a be |.{:|r(.h......J.| -00000020 b8 42 13 fb d3 ab 63 16 d2 74 c8 00 00 04 c0 0a |.B....c..t......| +00000000 16 03 01 00 85 01 00 00 81 03 03 19 8a e1 c7 50 |...............P| +00000010 ba 63 15 9b d5 85 f1 8c 55 43 d3 ce 9c d6 35 20 |.c......UC....5 | +00000020 f3 49 3d 55 a5 11 57 6d db 42 1d 00 00 04 c0 0a |.I=U..Wm.B......| 00000030 00 ff 01 00 00 54 00 0b 00 04 03 00 01 02 00 0a |.....T..........| 00000040 00 0c 00 0a 00 1d 00 17 00 1e 00 19 00 18 00 16 |................| 00000050 00 00 00 17 00 00 00 0d 00 30 00 2e 04 03 05 03 |.........0......| @@ -46,39 +46,39 @@ 00000220 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a c9 86 |.3.......7z..z..| 00000230 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b e4 c7 |....i..|V..1x+..| 00000240 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 95 12 |x.....N6$1{j.9..| -00000250 07 8f 2a 16 03 03 00 b7 0c 00 00 b3 03 00 1d 20 |..*............ | +00000250 07 8f 2a 16 03 03 00 b6 0c 00 00 b2 03 00 1d 20 |..*............ | 00000260 2f e5 7d a3 47 cd 62 43 15 28 da ac 5f bb 29 07 |/.}.G.bC.(.._.).| 00000270 30 ff f6 84 af c4 cf c2 ed 90 99 5f 58 cb 3b 74 |0.........._X.;t| -00000280 04 03 00 8b 30 81 88 02 42 00 b9 39 44 59 12 77 |....0...B..9DY.w| -00000290 8d e2 79 25 01 d1 6a 05 3d 53 ea f3 91 d6 c5 09 |..y%..j.=S......| -000002a0 24 bd 0c ad 24 cc 1c a7 fb 03 eb 0a 0d f4 30 96 |$...$.........0.| -000002b0 8d 28 a1 b3 64 ba 30 27 95 29 23 22 91 62 c3 1f |.(..d.0'.)#".b..| -000002c0 51 aa c8 be 17 85 31 8e f5 40 3e 02 42 00 ee a1 |Q.....1..@>.B...| -000002d0 64 14 a1 52 b3 e5 54 c9 24 53 94 5a 43 d8 4f 79 |d..R..T.$S.ZC.Oy| -000002e0 69 4b a8 51 ee de b3 b0 f7 1a 57 a3 28 72 d2 13 |iK.Q......W.(r..| -000002f0 a6 d3 17 0b c4 45 34 7f 10 3b 81 cb 0c 8d 51 b6 |.....E4..;....Q.| -00000300 0b 86 21 d0 ee 1d 7e 73 6b ea 77 8c 66 dc 65 16 |..!...~sk.w.f.e.| -00000310 03 03 00 04 0e 00 00 00 |........| +00000280 04 03 00 8a 30 81 87 02 42 01 f2 09 77 4a e7 f5 |....0...B...wJ..| +00000290 a8 35 3b dd 9d 62 5a 07 97 1e 76 93 b6 07 21 3e |.5;..bZ...v...!>| +000002a0 c8 fd 99 35 50 8a 8b ad e5 de 03 07 c8 5e fe 03 |...5P........^..| +000002b0 c1 99 04 ad 53 b6 76 67 eb 04 99 54 11 4d 4d e9 |....S.vg...T.MM.| +000002c0 74 3f 89 6e d9 c8 02 98 c5 3c cf 02 41 4e 64 21 |t?.n.....<..ANd!| +000002d0 1a 01 5f 2e 89 17 cc 65 33 d0 59 ed 17 59 c4 43 |.._....e3.Y..Y.C| +000002e0 0a fc 68 30 9c e2 c3 86 fb 2a c1 4a ae 32 ef 1d |..h0.....*.J.2..| +000002f0 06 27 36 7d d5 cd 68 23 4c e9 7e 64 b8 eb 42 05 |.'6}..h#L.~d..B.| +00000300 ef 83 36 b2 9e a7 ae 1a cd b0 3a 17 3a 46 16 03 |..6.......:.:F..| +00000310 03 00 04 0e 00 00 00 |.......| >>> Flow 3 (client to server) -00000000 16 03 03 00 25 10 00 00 21 20 ed 3e ba a7 43 53 |....%...! .>..CS| -00000010 5e e4 60 aa 31 3f e1 69 60 32 25 3d fd 8b 32 da |^.`.1?.i`2%=..2.| -00000020 f2 c5 db c7 02 e6 4d d0 de 15 14 03 03 00 01 01 |......M.........| -00000030 16 03 03 00 40 ee 28 f2 27 82 24 9d 17 d1 48 7a |....@.(.'.$...Hz| -00000040 74 2d dd 16 18 b7 70 97 2f 2b 91 47 eb c2 1d ae |t-....p./+.G....| -00000050 3f 48 52 cd ff e7 9e 0b 35 ad 1f 60 5e 07 b1 5e |?HR.....5..`^..^| -00000060 1c ba 6a 85 bb 6b 30 94 41 8a 59 81 cf 37 5f 26 |..j..k0.A.Y..7_&| -00000070 b1 52 36 5f df |.R6_.| +00000000 16 03 03 00 25 10 00 00 21 20 73 43 c2 08 92 f5 |....%...! sC....| +00000010 db bf 2f 8a eb 49 55 f7 5d 6b 80 64 f7 d9 75 1f |../..IU.]k.d..u.| +00000020 67 f6 35 21 3c 95 3f 1c 04 1a 14 03 03 00 01 01 |g.5!<.?.........| +00000030 16 03 03 00 40 59 bb 5a 5d 76 73 a5 30 0e 29 d3 |....@Y.Z]vs.0.).| +00000040 17 d8 2f 30 e6 ed 02 c6 83 12 44 42 d8 79 86 e0 |../0......DB.y..| +00000050 78 7b 43 8d 5b 7c 85 42 fb 7c 67 b0 d0 71 03 0e |x{C.[|.B.|g..q..| +00000060 d0 6b b6 06 f1 16 72 c0 16 66 cf 53 df ae 62 3b |.k....r..f.S..b;| +00000070 f3 57 52 4d 08 |.WRM.| >>> Flow 4 (server to client) 00000000 14 03 03 00 01 01 16 03 03 00 40 00 00 00 00 00 |..........@.....| -00000010 00 00 00 00 00 00 00 00 00 00 00 f5 05 5a a6 22 |.............Z."| -00000020 90 4e 8d d9 f1 55 c4 78 f2 ec 9d 97 cd fe af ae |.N...U.x........| -00000030 b7 62 00 67 2e b2 d9 1e 0c a3 c8 6a bf d2 3c 42 |.b.g.......j..u......| +00000080 cd 14 5b 4b 0a 7b a2 e6 54 b3 bd 3c f0 eb ca 78 |..[K.{..T..<...x| 00000090 15 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| -000000a0 00 00 00 00 00 13 3e 42 a5 61 84 ae 49 8b b9 91 |......>B.a..I...| -000000b0 c2 a3 76 74 1e 4f 53 0a fc 71 de 0d d2 44 c8 ac |..vt.OS..q...D..| -000000c0 2e 09 27 e6 ad |..'..| +000000a0 00 00 00 00 00 e6 4b 35 cc 69 58 89 49 67 99 f4 |......K5.iX.Ig..| +000000b0 c2 14 2a bb e7 21 2b fe fe b5 60 ae b2 2a 96 15 |..*..!+...`..*..| +000000c0 e0 65 d2 54 0b |.e.T.| diff --git a/testdata/Server-TLSv13-ECDHE-ECDSA-AES b/testdata/Server-TLSv13-ECDHE-ECDSA-AES index d2ed2ee..d80662d 100644 --- a/testdata/Server-TLSv13-ECDHE-ECDSA-AES +++ b/testdata/Server-TLSv13-ECDHE-ECDSA-AES @@ -1,93 +1,93 @@ >>> Flow 1 (client to server) -00000000 16 03 01 00 ca 01 00 00 c6 03 03 be 5b 8c 08 2b |............[..+| -00000010 26 a0 41 0f e3 4e b6 5c 9f 5d 53 04 67 4a 1d a2 |&.A..N.\.]S.gJ..| -00000020 26 3b 83 ab b4 7b c6 ec f8 a6 41 20 a6 de ad e2 |&;...{....A ....| -00000030 0c fd 02 99 11 51 c6 be e8 52 df 0b e2 b3 6f fe |.....Q...R....o.| -00000040 33 3e 2f 90 ac d2 e8 a2 53 8b d9 05 00 04 13 01 |3>/.....S.......| +00000000 16 03 01 00 ca 01 00 00 c6 03 03 1d 95 21 d3 93 |.............!..| +00000010 6b 69 ad 44 69 28 2d 2e 74 c3 77 24 86 82 52 91 |ki.Di(-.t.w$..R.| +00000020 a8 15 64 82 15 2e 02 f8 41 3d c5 20 87 ff 55 4c |..d.....A=. ..UL| +00000030 00 16 80 c2 f7 44 15 18 bc 00 81 d8 7b d8 2c 88 |.....D......{.,.| +00000040 cb 19 31 89 23 d0 82 be d8 7f a4 26 00 04 13 01 |..1.#......&....| 00000050 00 ff 01 00 00 79 00 0b 00 04 03 00 01 02 00 0a |.....y..........| 00000060 00 0c 00 0a 00 1d 00 17 00 1e 00 19 00 18 00 16 |................| 00000070 00 00 00 17 00 00 00 0d 00 1e 00 1c 04 03 05 03 |................| 00000080 06 03 08 07 08 08 08 09 08 0a 08 0b 08 04 08 05 |................| 00000090 08 06 04 01 05 01 06 01 00 2b 00 03 02 03 04 00 |.........+......| -000000a0 2d 00 02 01 01 00 33 00 26 00 24 00 1d 00 20 31 |-.....3.&.$... 1| -000000b0 8e dd f4 7c cf 22 04 c1 c3 04 5c 24 49 db ae ab |...|."....\$I...| -000000c0 0a d0 42 e8 70 51 c7 4f 88 e2 4e 2e 0b 80 65 |..B.pQ.O..N...e| +000000a0 2d 00 02 01 01 00 33 00 26 00 24 00 1d 00 20 8d |-.....3.&.$... .| +000000b0 18 6e 7e 5a 97 58 25 0d 07 9e af 9c 9b bd 6f 92 |.n~Z.X%.......o.| +000000c0 e9 08 8f 92 55 28 d2 90 3f fe bc dd db b7 00 |....U(..?......| >>> Flow 2 (server to client) 00000000 16 03 03 00 7a 02 00 00 76 03 03 00 00 00 00 00 |....z...v.......| 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| -00000020 00 00 00 00 00 00 00 00 00 00 00 20 a6 de ad e2 |........... ....| -00000030 0c fd 02 99 11 51 c6 be e8 52 df 0b e2 b3 6f fe |.....Q...R....o.| -00000040 33 3e 2f 90 ac d2 e8 a2 53 8b d9 05 13 01 00 00 |3>/.....S.......| +00000020 00 00 00 00 00 00 00 00 00 00 00 20 87 ff 55 4c |........... ..UL| +00000030 00 16 80 c2 f7 44 15 18 bc 00 81 d8 7b d8 2c 88 |.....D......{.,.| +00000040 cb 19 31 89 23 d0 82 be d8 7f a4 26 13 01 00 00 |..1.#......&....| 00000050 2e 00 2b 00 02 03 04 00 33 00 24 00 1d 00 20 2f |..+.....3.$... /| 00000060 e5 7d a3 47 cd 62 43 15 28 da ac 5f bb 29 07 30 |.}.G.bC.(.._.).0| 00000070 ff f6 84 af c4 cf c2 ed 90 99 5f 58 cb 3b 74 14 |.........._X.;t.| -00000080 03 03 00 01 01 17 03 03 00 17 cc d0 60 a1 dc d6 |............`...| -00000090 46 57 69 3d df 0e 0f 7f a8 34 2d 87 71 84 16 54 |FWi=.....4-.q..T| -000000a0 9d 17 03 03 02 22 cb 9c f9 3e a0 fd bf 07 03 7c |....."...>.....|| -000000b0 53 0c 15 22 0b 78 e5 02 36 e6 e7 6c 5b f9 aa 8d |S..".x..6..l[...| -000000c0 54 8e b1 15 d4 23 05 12 5e 6e 0f 0f 65 77 bf b5 |T....#..^n..ew..| -000000d0 32 28 0e 32 ca 9f 61 c3 37 23 87 e8 ec 19 1d ba |2(.2..a.7#......| -000000e0 ef f5 18 eb ba 49 2d 86 a6 d1 f7 c1 9e 67 10 9f |.....I-......g..| -000000f0 a1 d2 62 bd 4c 6c 5e a4 41 f6 1e fa fd e7 55 bc |..b.Ll^.A.....U.| -00000100 16 ad 91 91 de 03 86 d7 e1 88 87 ab 0e f4 f5 bb |................| -00000110 16 da 37 bb a4 ce 4e 6c 5f 88 41 f9 a2 90 9a 2d |..7...Nl_.A....-| -00000120 5c 14 d5 01 28 06 a9 20 a4 ae 92 17 c5 95 b1 dc |\...(.. ........| -00000130 02 a8 3f 3b a7 97 91 5a 4f 56 bb db b6 30 0d 80 |..?;...ZOV...0..| -00000140 35 ac 91 6f 4f ba 1e 10 c6 fc d2 ca 96 e4 9d 1f |5..oO...........| -00000150 2f 29 00 3a 11 b7 77 dd d5 ed 76 9f 67 a1 b1 0c |/).:..w...v.g...| -00000160 5d 34 34 eb 42 49 23 15 49 12 24 24 73 be a9 65 |]44.BI#.I.$$s..e| -00000170 99 b6 b4 3f 18 0c d1 32 26 eb 86 93 5c 0e e8 06 |...?...2&...\...| -00000180 fb d7 9f 0e d9 26 14 47 b8 e5 67 8c c8 cb 0c 55 |.....&.G..g....U| -00000190 61 70 a9 ce 0d 4e bf ca 40 9c e7 0d 2b 5d 54 b7 |ap...N..@...+]T.| -000001a0 5a 64 50 e6 a1 c9 fc 47 7d e7 0a 13 36 8d 70 eb |ZdP....G}...6.p.| -000001b0 68 65 e4 9b 9d 12 d1 d9 0d 32 72 59 0f 46 b2 e2 |he.......2rY.F..| -000001c0 21 ab 13 d4 ab f3 6e b6 44 16 b8 85 bb dc f4 f7 |!.....n.D.......| -000001d0 d6 ce 5c 9f c0 4c 28 8f 36 39 ec 29 c7 33 bd ea |..\..L(.69.).3..| -000001e0 2d 10 16 84 50 c5 18 5b 2c a3 99 bb 3b 0b 70 66 |-...P..[,...;.pf| -000001f0 72 9a 83 01 06 2a bf 4a 60 c5 5d 41 a1 f0 92 bb |r....*.J`.]A....| -00000200 3b 2a 1a 41 3a 57 c3 22 13 2c b4 7b 3e 47 52 ea |;*.A:W.".,.{>GR.| -00000210 79 8a bf ef 2e 2c f7 89 c7 36 5a df 38 c2 04 b6 |y....,...6Z.8...| -00000220 6f 96 cd 7c 01 b3 e3 cd 4a 83 56 40 06 58 8a 7c |o..|....J.V@.X.|| -00000230 8c 75 df b6 b8 76 63 71 89 72 0a 64 de 23 7d 50 |.u...vcq.r.d.#}P| -00000240 77 a8 f6 a0 81 9d e9 ed 81 5e 20 c8 9f 65 3c 95 |w........^ ..e<.| -00000250 cf ed 99 80 71 06 5e 00 46 0d 0c 22 b3 88 f0 c5 |....q.^.F.."....| -00000260 33 3e 13 6b f2 07 9c db 20 31 9c 8d ea d7 73 e8 |3>.k.... 1....s.| -00000270 00 e1 2b f4 c8 d7 34 37 4a 98 b9 4d 28 db 15 8a |..+...47J..M(...| -00000280 af 53 14 3b 02 54 a3 0b 5f 10 ff 5d 20 1c 19 ae |.S.;.T.._..] ...| -00000290 6b 8a 99 a5 8f e0 ac dd c1 ba 1f 85 56 a3 94 bc |k...........V...| -000002a0 79 03 5f d5 dd e1 8e 62 b7 82 fa 92 c3 d5 8a fc |y._....b........| -000002b0 6b 17 24 d9 af db 3d 9c 0f 51 82 3d a2 ec 5f 9c |k.$...=..Q.=.._.| -000002c0 dc 69 a5 ce db d8 8b 87 17 03 03 00 a3 69 cd 7b |.i...........i.{| -000002d0 9f ac ad 72 11 b2 5d ee 19 63 d0 35 12 6d 5e 3f |...r..]..c.5.m^?| -000002e0 81 a8 18 4a d4 09 f3 80 38 4a 31 08 3e a0 4c 78 |...J....8J1.>.Lx| -000002f0 48 08 e9 90 ba e7 2a b4 73 2e 2b 2b 15 60 ce 09 |H.....*.s.++.`..| -00000300 7d df 49 31 e1 9d ff 92 1d b4 af 2e 8c f8 a6 2e |}.I1............| -00000310 93 d7 b9 10 69 10 7f 04 0d 8d e2 37 09 a7 d0 2a |....i......7...*| -00000320 ac ea 51 49 50 1d 1c 54 7f b9 15 ad 8c 77 ef 1d |..QIP..T.....w..| -00000330 a6 59 a3 bf b2 53 f7 6c 21 92 e0 36 c5 0d 61 94 |.Y...S.l!..6..a.| -00000340 be 61 5e 77 25 35 df e4 5f 67 c1 c6 af 51 e4 ce |.a^w%5.._g...Q..| -00000350 c4 28 c5 4e bc f6 c6 ba 32 dc 8e c7 45 f3 4d a1 |.(.N....2...E.M.| -00000360 70 53 98 46 8f 39 c2 cc b7 fc f7 24 11 97 72 b3 |pS.F.9.....$..r.| -00000370 17 03 03 00 35 76 be b6 7a 3f e3 08 7a a2 65 25 |....5v..z?..z.e%| -00000380 fd 0b c3 87 be ba eb ca cb 3d c1 25 10 e0 7b 00 |.........=.%..{.| -00000390 37 7a 52 9e d6 b2 e7 ba 8e 51 de 15 c4 e8 16 eb |7zR......Q......| -000003a0 c6 21 92 42 b1 62 f4 ce 27 ba 17 03 03 00 8b 54 |.!.B.b..'......T| -000003b0 03 de d7 a7 85 2f 4b 23 2d d5 3a b4 3d 3d f6 00 |...../K#-.:.==..| -000003c0 ac ab bd 6f dd bf 9f 24 fb 1b d4 01 39 3e c0 87 |...o...$....9>..| -000003d0 bb 32 ca f6 61 b2 ef 5d 9c 2c 1b a5 10 66 7b fd |.2..a..].,...f{.| -000003e0 4b d0 03 dc 53 a9 0d 5a d5 c4 4c 25 9c 55 e6 f8 |K...S..Z..L%.U..| -000003f0 d1 d8 49 dc 36 a1 92 ae f1 3e 2f 11 66 87 93 69 |..I.6....>/.f..i| -00000400 24 2e 5d 6c f6 79 15 68 a8 99 2e 1a 9c e2 85 4e |$.]l.y.h.......N| -00000410 5f d6 a8 3c 70 e1 67 cb df b2 1b ab 2b ed dc b6 |_....b...O| +000000a0 e8 17 03 03 02 22 3f 87 53 63 dc 59 f7 32 60 4b |....."?.Sc.Y.2`K| +000000b0 bd 9f e1 fc 4c 9a 98 18 94 e1 c1 07 ab 11 33 dc |....L.........3.| +000000c0 f1 48 67 e6 66 83 3c 88 53 c7 dc af e2 87 bc 0b |.Hg.f.<.S.......| +000000d0 d7 60 99 83 29 a1 1c 30 09 ba 4a e1 a9 c2 0e 34 |.`..)..0..J....4| +000000e0 cb a6 f2 8b 1b a0 b0 e6 21 27 3d b8 b4 90 0c 61 |........!'=....a| +000000f0 af 38 db fe fe 9c 34 09 1e 1a c8 f2 e9 05 68 ee |.8....4.......h.| +00000100 9c ec 74 b8 10 25 29 3d 52 71 87 c6 88 22 5a e9 |..t..%)=Rq..."Z.| +00000110 33 d0 d3 75 a8 94 b2 6d 48 4f 63 d1 32 f2 a3 70 |3..u...mHOc.2..p| +00000120 f1 a5 0b 4c 5d 7c 91 9b 04 d4 c3 9e 37 dd 67 a1 |...L]|......7.g.| +00000130 aa 23 6f 2b d0 42 b9 30 5c ed ae 12 36 f1 7c b3 |.#o+.B.0\...6.|.| +00000140 92 de 02 3a 99 c3 98 91 a3 09 43 ef 24 8d 67 e7 |...:......C.$.g.| +00000150 0d 68 22 e1 cc 99 8e 8e 64 09 be 50 f7 4a 37 0a |.h".....d..P.J7.| +00000160 02 af 88 db 8b a0 68 0d 7e 97 d9 9c 48 c3 bd aa |......h.~...H...| +00000170 db 01 69 2b 2b e6 f5 4b 66 c0 7a 8c fe 4d 8f 7b |..i++..Kf.z..M.{| +00000180 94 be 58 b5 44 67 df 26 3f 79 ee 55 bf bf aa 52 |..X.Dg.&?y.U...R| +00000190 95 ec 6b 7f 2b 68 f0 5a 81 4e 13 25 91 bd 9a df |..k.+h.Z.N.%....| +000001a0 dd 2c ae 6d c3 47 27 c2 3f 51 98 a3 b7 06 ec 2f |.,.m.G'.?Q...../| +000001b0 d6 c0 7f 1f e5 5e 3c 50 d3 6e 82 33 be 07 48 0b |.....^..}.....HUB..| +00000260 d0 b2 8e 4d c6 26 bb 77 9e 3f e0 0a 90 a4 3b eb |...M.&.w.?....;.| +00000270 37 94 c4 e8 39 12 82 24 b3 8b 6d 0d ed 9c 31 f0 |7...9..$..m...1.| +00000280 d0 5a cb b0 79 9b d2 ed ab 08 8b 9d ad 25 7a ce |.Z..y........%z.| +00000290 d7 6d c8 11 0a 60 f4 81 e9 e3 e3 42 7b 3d 95 67 |.m...`.....B{=.g| +000002a0 c2 4e 3e 80 11 2e 09 53 94 03 c0 88 cb 23 7e d2 |.N>....S.....#~.| +000002b0 ad e2 dc e7 e2 0b ba 74 9c 04 ad 75 e6 7f 5a fb |.......t...u..Z.| +000002c0 53 5a 98 14 18 4f 1d 2b 17 03 03 00 a4 7a ce c7 |SZ...O.+.....z..| +000002d0 9c de bc 27 04 f7 8b 4b a1 73 7d 0d fa b5 a1 e2 |...'...K.s}.....| +000002e0 fe 8b 33 8d 48 64 65 13 68 e2 5d e2 d7 3e 67 f2 |..3.Hde.h.]..>g.| +000002f0 db bd ff f9 e5 3e 4c b1 56 e3 22 95 88 23 48 fe |.....>L.V."..#H.| +00000300 0f 80 4c 5c 1c 74 0e 26 d4 7c 17 83 65 d6 a3 51 |..L\.t.&.|..e..Q| +00000310 5a 01 a5 12 9c db 0b c9 0b 8b 53 c7 03 75 b9 04 |Z.........S..u..| +00000320 a0 62 df 11 75 ae ff 33 7b 98 6b 7b 35 3e 41 4c |.b..u..3{.k{5>AL| +00000330 9b 16 12 b4 39 ce 9a d5 e9 83 78 b3 4b 3e d6 82 |....9.....x.K>..| +00000340 75 66 bf 73 e4 26 e6 22 8e 2f fe 4d 49 e4 b5 03 |uf.s.&."./.MI...| +00000350 04 a6 65 59 c2 aa e2 e6 ec f0 e2 99 b5 c4 55 75 |..eY..........Uu| +00000360 e1 90 a4 73 cb 21 78 df 4e 96 e2 99 75 15 77 59 |...s.!x.N...u.wY| +00000370 db 17 03 03 00 35 bc 1c 15 d7 b0 62 21 d4 dd 09 |.....5.....b!...| +00000380 1d aa 05 3c e3 ea 0a 9d 89 1f aa 2f f7 75 93 86 |...<......./.u..| +00000390 35 ee 5f 06 20 99 17 ca 4c 05 65 07 f7 56 9f 62 |5._. ...L.e..V.b| +000003a0 2a ea e2 05 f0 be fe bf d6 09 46 17 03 03 00 8b |*.........F.....| +000003b0 37 1a 6c e5 ea 27 e7 b2 d7 87 9a 1a a1 41 b5 64 |7.l..'.......A.d| +000003c0 61 8b bb 1c 64 a2 37 de 39 b3 5b f4 5b 9f bf d8 |a...d.7.9.[.[...| +000003d0 e7 3d be ad 96 6c 69 19 ce 8e a8 12 14 5d 1e 79 |.=...li......].y| +000003e0 c5 12 53 c3 13 81 5a 22 44 e5 6e c4 97 cc 18 19 |..S...Z"D.n.....| +000003f0 c4 04 08 cf 16 dd df 3d 4f 13 40 5f 33 38 f5 0f |.......=O.@_38..| +00000400 4f bb 41 e2 85 85 43 de d0 b5 7a 61 d8 3a 53 41 |O.A...C...za.:SA| +00000410 d2 ad 7b e4 bf 02 d2 14 7d f7 0c 05 b8 bb 21 90 |..{.....}.....!.| +00000420 a5 61 76 7e 07 5d bf e2 a1 f8 1a a6 77 42 2a 7c |.av~.]......wB*|| +00000430 7a 41 a7 5e 04 c2 49 02 45 a8 f5 |zA.^..I.E..| >>> Flow 3 (client to server) -00000000 14 03 03 00 01 01 17 03 03 00 35 46 8b fa 42 0d |..........5F..B.| -00000010 fa 3e 9e 80 76 12 ce 73 ae 85 67 ee af 1e 25 6e |.>..v..s..g...%n| -00000020 0b 46 4c bd 5a 46 8e 5c 27 7a 0a 8d d3 9c 3c 29 |.FL.ZF.\'z....<)| -00000030 4c c8 08 78 ac 9f f4 7a 38 8d 49 6a 01 b6 f5 83 |L..x...z8.Ij....| +00000000 14 03 03 00 01 01 17 03 03 00 35 3d f2 27 fe 81 |..........5=.'..| +00000010 4c 6e 61 1f 34 f8 3d 25 1f 33 d6 22 aa 7f ab 08 |Lna.4.=%.3."....| +00000020 2c 48 44 39 74 2c e9 be 78 7f c7 db 27 b0 b0 6d |,HD9t,..x...'..m| +00000030 b2 8b 87 6c e5 5c 38 79 9f ed 3d 4f 92 81 dc ea |...l.\8y..=O....| >>> Flow 4 (server to client) -00000000 17 03 03 00 1e c2 1a dc 0a cb 9a 11 f7 a1 c2 1f |................| -00000010 54 7d 32 6f 0e 13 b6 6b 9f e1 c6 14 63 fc 18 b9 |T}2o...k....c...| -00000020 81 53 44 17 03 03 00 13 c9 72 ae 5e 2b c1 6f 64 |.SD......r.^+.od| -00000030 e0 70 47 15 b1 ec c3 25 00 7f 4e |.pG....%..N| +00000000 17 03 03 00 1e f7 99 f9 d8 a2 00 d9 e3 48 d9 b2 |.............H..| +00000010 35 37 93 6f b0 1f d5 81 b1 16 b1 e4 d8 b4 40 ce |57.o..........@.| +00000020 97 9f 16 17 03 03 00 13 a4 cb 62 61 70 e9 67 c3 |..........bap.g.| +00000030 21 02 19 bc 01 01 5d 9b 15 d4 84 |!.....]....| diff --git a/tls_cf.go b/tls_cf.go index 8160be0..5b74cb4 100644 --- a/tls_cf.go +++ b/tls_cf.go @@ -47,10 +47,6 @@ var supportedSignatureAlgorithmsWithCircl []SignatureScheme // schemes are only included when tls.Config#PQSignatureSchemesEnabled is set // and FIPS-only mode is not enabled. func (c *Config) supportedSignatureAlgorithms() []SignatureScheme { - // If FIPS-only mode is requested, do not add other algos. - if needFIPS() { - return supportedSignatureAlgorithms() - } if c != nil && c.PQSignatureSchemesEnabled { return supportedSignatureAlgorithmsWithCircl } diff --git a/tls_test.go b/tls_test.go index 3bea2a8..4e37c36 100644 --- a/tls_test.go +++ b/tls_test.go @@ -8,6 +8,7 @@ import ( "bytes" "context" "crypto" + "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -28,6 +29,10 @@ import ( "testing" "time" + "golang.org/x/crypto/cryptobyte" + + "github.com/refraction-networking/utls/internal/fips140tls" + "github.com/refraction-networking/utls/internal/hpke" "github.com/refraction-networking/utls/testenv" ) @@ -163,6 +168,7 @@ func TestX509MixedKeyPair(t *testing.T) { } func newLocalListener(t testing.TB) net.Listener { + t.Helper() ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { ln, err = net.Listen("tcp6", "[::1]:0") @@ -173,6 +179,12 @@ func newLocalListener(t testing.TB) net.Listener { return ln } +func skipFIPS(t *testing.T) { + if fips140tls.Required() { + t.Skip("skipping test in FIPS mode") + } +} + func TestDialTimeout(t *testing.T) { if testing.Short() { t.Skip("skipping in short mode") @@ -883,6 +895,10 @@ func TestCloneNonFuncFields(t *testing.T) { f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) case "EncryptedClientHelloConfigList": f.Set(reflect.ValueOf([]byte{'x'})) + case "EncryptedClientHelloKeys": + f.Set(reflect.ValueOf([]EncryptedClientHelloKey{ + {Config: []byte{1}, PrivateKey: []byte{1}}, + })) case "mutex", "autoSessionTicketKeys", "sessionTicketKeys": continue // these are unexported fields that are handled separately case "ApplicationSettings": // [UTLS] ALPS (Application Settings) @@ -1113,6 +1129,8 @@ func TestConnectionStateMarshal(t *testing.T) { } func TestConnectionState(t *testing.T) { + skipFIPS(t) // Test certificates not FIPS compatible. + issuer, err := x509.ParseCertificate(testRSACertificateIssuer) if err != nil { panic(err) @@ -1120,8 +1138,6 @@ func TestConnectionState(t *testing.T) { rootCAs := x509.NewCertPool() rootCAs.AddCert(issuer) - now := func() time.Time { return time.Unix(1476984729, 0) } - const alpnProtocol = "golang" const serverName = "example.golang" var scts = [][]byte{[]byte("dummy sct 1"), []byte("dummy sct 2")} @@ -1137,7 +1153,7 @@ func TestConnectionState(t *testing.T) { } t.Run(name, func(t *testing.T) { config := &Config{ - Time: now, + Time: testTime, Rand: zeroSource{}, Certificates: make([]Certificate, 1), MaxVersion: v, @@ -1253,6 +1269,8 @@ func TestBuildNameToCertificate_doesntModifyCertificates(t *testing.T) { func testingKey(s string) string { return strings.ReplaceAll(s, "TESTING KEY", "PRIVATE KEY") } func TestClientHelloInfo_SupportsCertificate(t *testing.T) { + skipFIPS(t) // Test certificates not FIPS compatible. + rsaCert := &Certificate{ Certificate: [][]byte{testRSACertificate}, PrivateKey: testRSAPrivateKey, @@ -1702,6 +1720,8 @@ func TestPKCS1OnlyCert(t *testing.T) { } func TestVerifyCertificates(t *testing.T) { + skipFIPS(t) // Test certificates not FIPS compatible. + // See https://go.dev/issue/31641. t.Run("TLSv12", func(t *testing.T) { testVerifyCertificates(t, VersionTLS12) }) t.Run("TLSv13", func(t *testing.T) { testVerifyCertificates(t, VersionTLS13) }) @@ -1768,7 +1788,7 @@ func testVerifyCertificates(t *testing.T, version uint16) { var serverVerifyPeerCertificates, clientVerifyPeerCertificates bool clientConfig := testConfig.Clone() - clientConfig.Time = func() time.Time { return time.Unix(1476984729, 0) } + clientConfig.Time = testTime clientConfig.MaxVersion = version clientConfig.MinVersion = version clientConfig.RootCAs = rootCAs @@ -1845,24 +1865,21 @@ func testVerifyCertificates(t *testing.T, version uint16) { } } -func TestHandshakeKyber(t *testing.T) { - if x25519Kyber768Draft00.String() != "X25519Kyber768Draft00" { - t.Fatalf("unexpected CurveID string: %v", x25519Kyber768Draft00.String()) - } - +func TestHandshakeMLKEM(t *testing.T) { + skipFIPS(t) // No X25519MLKEM768 in FIPS var tests = []struct { name string clientConfig func(*Config) serverConfig func(*Config) preparation func(*testing.T) expectClientSupport bool - expectKyber bool + expectMLKEM bool expectHRR bool }{ { name: "Default", expectClientSupport: true, - expectKyber: true, + expectMLKEM: true, expectHRR: false, }, { @@ -1878,7 +1895,7 @@ func TestHandshakeKyber(t *testing.T) { config.CurvePreferences = []CurveID{X25519} }, expectClientSupport: true, - expectKyber: false, + expectMLKEM: false, expectHRR: false, }, { @@ -1887,9 +1904,25 @@ func TestHandshakeKyber(t *testing.T) { config.CurvePreferences = []CurveID{CurveP256} }, expectClientSupport: true, - expectKyber: false, + expectMLKEM: false, expectHRR: true, }, + { + name: "ClientMLKEMOnly", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{X25519MLKEM768} + }, + expectClientSupport: true, + expectMLKEM: true, + }, + { + name: "ClientSortedCurvePreferences", + clientConfig: func(config *Config) { + config.CurvePreferences = []CurveID{CurveP256, X25519MLKEM768} + }, + expectClientSupport: true, + expectMLKEM: true, + }, { name: "ClientTLSv12", clientConfig: func(config *Config) { @@ -1903,8 +1936,17 @@ func TestHandshakeKyber(t *testing.T) { config.MaxVersion = VersionTLS12 }, expectClientSupport: true, - expectKyber: false, + expectMLKEM: false, }, + // [uTLS] SECTION BEGIN + // { + // name: "GODEBUG", + // preparation: func(t *testing.T) { + // t.Setenv("GODEBUG", "tlsmlkem=0") + // }, + // expectClientSupport: false, + // }, + // [uTLS] SECTION END } baseConfig := testConfig.Clone() @@ -1921,10 +1963,10 @@ func TestHandshakeKyber(t *testing.T) { test.serverConfig(serverConfig) } serverConfig.GetConfigForClient = func(hello *ClientHelloInfo) (*Config, error) { - if !test.expectClientSupport && slices.Contains(hello.SupportedCurves, x25519Kyber768Draft00) { - return nil, errors.New("client supports Kyber768Draft00") - } else if test.expectClientSupport && !slices.Contains(hello.SupportedCurves, x25519Kyber768Draft00) { - return nil, errors.New("client does not support Kyber768Draft00") + if !test.expectClientSupport && slices.Contains(hello.SupportedCurves, X25519MLKEM768) { + return nil, errors.New("client supports X25519MLKEM768") + } else if test.expectClientSupport && !slices.Contains(hello.SupportedCurves, X25519MLKEM768) { + return nil, errors.New("client does not support X25519MLKEM768") } return nil, nil } @@ -1936,19 +1978,19 @@ func TestHandshakeKyber(t *testing.T) { if err != nil { t.Fatal(err) } - if test.expectKyber { - if ss.testingOnlyCurveID != x25519Kyber768Draft00 { - t.Errorf("got CurveID %v (server), expected %v", ss.testingOnlyCurveID, x25519Kyber768Draft00) + if test.expectMLKEM { + if ss.testingOnlyCurveID != X25519MLKEM768 { + t.Errorf("got CurveID %v (server), expected %v", ss.testingOnlyCurveID, X25519MLKEM768) } - if cs.testingOnlyCurveID != x25519Kyber768Draft00 { - t.Errorf("got CurveID %v (client), expected %v", cs.testingOnlyCurveID, x25519Kyber768Draft00) + if cs.testingOnlyCurveID != X25519MLKEM768 { + t.Errorf("got CurveID %v (client), expected %v", cs.testingOnlyCurveID, X25519MLKEM768) } } else { - if ss.testingOnlyCurveID == x25519Kyber768Draft00 { - t.Errorf("got CurveID %v (server), expected not Kyber", ss.testingOnlyCurveID) + if ss.testingOnlyCurveID == X25519MLKEM768 { + t.Errorf("got CurveID %v (server), expected not X25519MLKEM768", ss.testingOnlyCurveID) } - if cs.testingOnlyCurveID == x25519Kyber768Draft00 { - t.Errorf("got CurveID %v (client), expected not Kyber", cs.testingOnlyCurveID) + if cs.testingOnlyCurveID == X25519MLKEM768 { + t.Errorf("got CurveID %v (client), expected not X25519MLKEM768", cs.testingOnlyCurveID) } } if test.expectHRR { @@ -2021,6 +2063,120 @@ func TestLargeCertMsg(t *testing.T) { }, } if _, _, err := testHandshake(t, clientConfig, serverConfig); err != nil { - t.Fatalf("unexpected failure :%s", err) + t.Fatalf("unexpected failure: %s", err) + } +} + +func TestECH(t *testing.T) { + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + tmpl := &x509.Certificate{ + SerialNumber: big.NewInt(1), + DNSNames: []string{"public.example"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + } + publicCertDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k) + if err != nil { + t.Fatal(err) + } + publicCert, err := x509.ParseCertificate(publicCertDER) + if err != nil { + t.Fatal(err) + } + tmpl.DNSNames[0] = "secret.example" + secretCertDER, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, k.Public(), k) + if err != nil { + t.Fatal(err) + } + secretCert, err := x509.ParseCertificate(secretCertDER) + if err != nil { + t.Fatal(err) + } + + marshalECHConfig := func(id uint8, pubKey []byte, publicName string, maxNameLen uint8) []byte { + builder := cryptobyte.NewBuilder(nil) + builder.AddUint16(extensionEncryptedClientHello) + builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { + builder.AddUint8(id) + builder.AddUint16(hpke.DHKEM_X25519_HKDF_SHA256) // The only DHKEM we support + builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { + builder.AddBytes(pubKey) + }) + builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { + for _, aeadID := range sortedSupportedAEADs { + builder.AddUint16(hpke.KDF_HKDF_SHA256) // The only KDF we support + builder.AddUint16(aeadID) + } + }) + builder.AddUint8(maxNameLen) + builder.AddUint8LengthPrefixed(func(builder *cryptobyte.Builder) { + builder.AddBytes([]byte(publicName)) + }) + builder.AddUint16(0) // extensions + }) + + return builder.BytesOrPanic() + } + + echKey, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + t.Fatal(err) + } + + echConfig := marshalECHConfig(123, echKey.PublicKey().Bytes(), "public.example", 32) + + builder := cryptobyte.NewBuilder(nil) + builder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) { + builder.AddBytes(echConfig) + }) + echConfigList := builder.BytesOrPanic() + + clientConfig, serverConfig := testConfig.Clone(), testConfig.Clone() + clientConfig.InsecureSkipVerify = false + clientConfig.Rand = rand.Reader + clientConfig.Time = nil + clientConfig.MinVersion = VersionTLS13 + clientConfig.ServerName = "secret.example" + clientConfig.RootCAs = x509.NewCertPool() + clientConfig.RootCAs.AddCert(secretCert) + clientConfig.RootCAs.AddCert(publicCert) + clientConfig.EncryptedClientHelloConfigList = echConfigList + serverConfig.InsecureSkipVerify = false + serverConfig.Rand = rand.Reader + serverConfig.Time = nil + serverConfig.MinVersion = VersionTLS13 + serverConfig.ServerName = "public.example" + serverConfig.Certificates = []Certificate{ + {Certificate: [][]byte{publicCertDER}, PrivateKey: k}, + {Certificate: [][]byte{secretCertDER}, PrivateKey: k}, + } + serverConfig.EncryptedClientHelloKeys = []EncryptedClientHelloKey{ + {Config: echConfig, PrivateKey: echKey.Bytes(), SendAsRetry: true}, + } + + ss, cs, err := testHandshake(t, clientConfig, serverConfig) + if err != nil { + t.Fatalf("unexpected failure: %s", err) + } + if !ss.ECHAccepted { + t.Fatal("server ConnectionState shows ECH not accepted") + } + if !cs.ECHAccepted { + t.Fatal("client ConnectionState shows ECH not accepted") + } + if cs.ServerName != "secret.example" || ss.ServerName != "secret.example" { + t.Fatalf("unexpected ConnectionState.ServerName, want %q, got server:%q, client: %q", "secret.example", ss.ServerName, cs.ServerName) + } + if len(cs.VerifiedChains) != 1 { + t.Fatal("unexpect number of certificate chains") + } + if len(cs.VerifiedChains[0]) != 1 { + t.Fatal("unexpect number of certificates") + } + if !cs.VerifiedChains[0][0].Equal(secretCert) { + t.Fatal("unexpected certificate") } } diff --git a/u_common.go b/u_common.go index 68ce11f..860b925 100644 --- a/u_common.go +++ b/u_common.go @@ -88,7 +88,7 @@ const ( ) const ( - X25519Kyber768Draft00 CurveID = x25519Kyber768Draft00 + // X25519Kyber768Draft00 CurveID = x25519Kyber768Draft00 FakeCurveX25519Kyber512Draft00 CurveID = 0xfe30 FakeCurveX25519Kyber768Draft00Old CurveID = 0xfe31 diff --git a/u_conn.go b/u_conn.go index a696c89..6871a73 100644 --- a/u_conn.go +++ b/u_conn.go @@ -53,7 +53,7 @@ type UConn struct { ech ECHExtension // echCtx is the echContex returned by makeClientHello() - echCtx *echContext + echCtx *echClientContext } // UClient returns a new uTLS client, with behavior depending on clientHelloID. diff --git a/u_handshake_client.go b/u_handshake_client.go index 33543dc..8674caa 100644 --- a/u_handshake_client.go +++ b/u_handshake_client.go @@ -14,7 +14,9 @@ import ( "github.com/andybalholm/brotli" "github.com/klauspost/compress/zstd" + "github.com/refraction-networking/utls/internal/fips140tls" "github.com/refraction-networking/utls/internal/hpke" + "github.com/refraction-networking/utls/internal/tls13" ) // This function is called by (*clientHandshakeStateTLS13).readServerCertificate() @@ -183,7 +185,7 @@ func (hs *clientHandshakeStateTLS13) utlsReadServerParameters(encryptedExtension return nil } -func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivateKeys, *echContext, error) { +func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivateKeys, *echClientContext, error) { config := c.config // [UTLS SECTION START] @@ -286,35 +288,44 @@ func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivat if len(hello.supportedVersions) == 1 { hello.cipherSuites = nil } - if hasAESGCMHardwareSupport { + if fips140tls.Required() { + hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13FIPS...) + } else if hasAESGCMHardwareSupport { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13...) } else { hello.cipherSuites = append(hello.cipherSuites, defaultCipherSuitesTLS13NoAES...) } - // curveID := config.curvePreferences(maxVersion)[0] + if len(hello.supportedCurves) == 0 { + return nil, nil, nil, errors.New("tls: no supported elliptic curves for ECDHE") + } + // curveID := hello.supportedCurves[0] // keyShareKeys = &keySharePrivateKeys{curveID: curveID} - // if curveID == x25519Kyber768Draft00 { + // // Note that if X25519MLKEM768 is supported, it will be first because + // // the preference order is fixed. + // if curveID == X25519MLKEM768 { // keyShareKeys.ecdhe, err = generateECDHEKey(config.rand(), X25519) // if err != nil { // return nil, nil, nil, err // } - // seed := make([]byte, mlkem768.SeedSize) + // seed := make([]byte, mlkem.SeedSize) // if _, err := io.ReadFull(config.rand(), seed); err != nil { // return nil, nil, nil, err // } - // keyShareKeys.kyber, err = mlkem768.NewKeyFromSeed(seed) + // keyShareKeys.mlkem, err = mlkem.NewDecapsulationKey768(seed) // if err != nil { // return nil, nil, nil, err // } - // // For draft-tls-westerbaan-xyber768d00-03, we send both a hybrid - // // and a standard X25519 key share, since most servers will only - // // support the latter. We reuse the same X25519 ephemeral key for - // // both, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. + // mlkemEncapsulationKey := keyShareKeys.mlkem.EncapsulationKey().Bytes() + // x25519EphemeralKey := keyShareKeys.ecdhe.PublicKey().Bytes() // hello.keyShares = []keyShare{ - // {group: x25519Kyber768Draft00, data: append(keyShareKeys.ecdhe.PublicKey().Bytes(), - // keyShareKeys.kyber.EncapsulationKey()...)}, - // {group: X25519, data: keyShareKeys.ecdhe.PublicKey().Bytes()}, + // {group: X25519MLKEM768, data: append(mlkemEncapsulationKey, x25519EphemeralKey...)}, + // } + // // If both X25519MLKEM768 and X25519 are supported, we send both key + // // shares (as a fallback) and we reuse the same X25519 ephemeral + // // key, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. + // if slices.Contains(hello.supportedCurves, X25519) { + // hello.keyShares = append(hello.keyShares, keyShare{group: X25519, data: x25519EphemeralKey}) // } // } else { // if _, ok := curveForCurveID(curveID); !ok { @@ -340,7 +351,7 @@ func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivat // hello.quicTransportParameters = p // } - var ech *echContext + var ech *echClientContext if c.config.EncryptedClientHelloConfigList != nil { if c.config.MinVersion != 0 && c.config.MinVersion < VersionTLS13 { return nil, nil, nil, errors.New("tls: MinVersion must be >= VersionTLS13 if EncryptedClientHelloConfigList is populated") @@ -356,7 +367,7 @@ func (c *Conn) makeClientHelloForApplyPreset() (*clientHelloMsg, *keySharePrivat if echConfig == nil { return nil, nil, nil, errors.New("tls: EncryptedClientHelloConfigList contains no valid configs") } - ech = &echContext{config: echConfig} + ech = &echClientContext{config: echConfig} hello.encryptedClientHello = []byte{1} // indicate inner hello // We need to explicitly set these 1.2 fields to nil, as we do not // marshal them when encoding the inner hello, otherwise transcripts @@ -433,7 +444,7 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) { var ( session *SessionState - earlySecret []byte + earlySecret *tls13.EarlySecret binderKey []byte ) if !sessionIsLocked { @@ -444,7 +455,12 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) { // [uTLS section start] } else { session = c.HandshakeState.Session - earlySecret = c.HandshakeState.State13.EarlySecret + + if c.HandshakeState.State13.EarlySecret != nil && session != nil { + cipherSuite := cipherSuiteTLS13ByID(session.cipherSuite) + earlySecret = tls13.NewEarlySecretFromSecret(cipherSuite.hash.New, c.HandshakeState.State13.EarlySecret) + } + binderKey = c.HandshakeState.State13.BinderKey } // [uTLS section ends] @@ -502,7 +518,7 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) { if err := transcriptMsg(hello, transcript); err != nil { return err } - earlyTrafficSecret := suite.deriveSecret(earlySecret, clientEarlyTrafficLabel, transcript) + earlyTrafficSecret := earlySecret.ClientEarlyTrafficSecret(transcript) c.quicSetWriteSecret(QUICEncryptionLevelEarly, suite.id, earlyTrafficSecret) } @@ -528,6 +544,12 @@ func (c *UConn) clientHandshake(ctx context.Context) (err error) { hs13.serverHello = serverHello hs13.hello = hello hs13.echContext = ech + if c.HandshakeState.State13.EarlySecret != nil && session.cipherSuite != 0 { + hs13.earlySecret = tls13.NewEarlySecretFromSecret(cipherSuiteTLS13ByID(session.cipherSuite).hash.New, c.HandshakeState.State13.EarlySecret) + } + if c.HandshakeState.MasterSecret != nil && session.cipherSuite != 0 { + hs13.masterSecret = tls13.NewMasterSecretFromSecret(cipherSuiteTLS13ByID(session.cipherSuite).hash.New, c.HandshakeState.MasterSecret) + } if !sessionIsLocked { hs13.earlySecret = earlySecret hs13.binderKey = binderKey diff --git a/u_parrots.go b/u_parrots.go index 7e3e576..78b7f6a 100644 --- a/u_parrots.go +++ b/u_parrots.go @@ -5,6 +5,7 @@ package tls import ( + "crypto/mlkem" crand "crypto/rand" "crypto/sha256" "encoding/binary" @@ -18,7 +19,6 @@ import ( "strconv" "github.com/refraction-networking/utls/dicttls" - "github.com/refraction-networking/utls/internal/mlkem768" ) var ErrUnknownClientHelloID = errors.New("tls: unknown ClientHelloID") @@ -618,7 +618,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, &SupportedCurvesExtension{[]CurveID{ GREASE_PLACEHOLDER, - X25519Kyber768Draft00, + X25519MLKEM768, X25519, CurveP256, CurveP384, @@ -642,7 +642,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &SCTExtension{}, &KeyShareExtension{[]KeyShare{ {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, - {Group: X25519Kyber768Draft00}, + {Group: X25519MLKEM768}, {Group: X25519}, }}, &PSKKeyExchangeModesExtension{[]uint8{ @@ -764,7 +764,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, &SupportedCurvesExtension{[]CurveID{ GREASE_PLACEHOLDER, - X25519Kyber768Draft00, + X25519MLKEM768, X25519, CurveP256, CurveP384, @@ -788,7 +788,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &SCTExtension{}, &KeyShareExtension{[]KeyShare{ {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, - {Group: X25519Kyber768Draft00}, + {Group: X25519MLKEM768}, {Group: X25519}, }}, &PSKKeyExchangeModesExtension{[]uint8{ @@ -2495,7 +2495,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &RenegotiationInfoExtension{Renegotiation: RenegotiateOnceAsClient}, &SupportedCurvesExtension{[]CurveID{ GREASE_PLACEHOLDER, - X25519Kyber768Draft00, + X25519MLKEM768, X25519, CurveP256, CurveP384, @@ -2519,7 +2519,7 @@ func utlsIdToSpec(id ClientHelloID) (ClientHelloSpec, error) { &SCTExtension{}, &KeyShareExtension{[]KeyShare{ {Group: CurveID(GREASE_PLACEHOLDER), Data: []byte{0}}, - {Group: X25519Kyber768Draft00}, + {Group: X25519MLKEM768}, {Group: X25519}, }}, &PSKKeyExchangeModesExtension{[]uint8{ @@ -2736,37 +2736,37 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error { continue } - if curveID == x25519Kyber768Draft00 { + if curveID == X25519MLKEM768 { ecdheKey, err := generateECDHEKey(uconn.config.rand(), X25519) if err != nil { return err } - seed := make([]byte, mlkem768.SeedSize) + seed := make([]byte, mlkem.SeedSize) if _, err := io.ReadFull(uconn.config.rand(), seed); err != nil { return err } - kyberKey, err := mlkem768.NewKeyFromSeed(seed) + mlkemKey, err := mlkem.NewDecapsulationKey768(seed) if err != nil { return err } - circlKyberKey, err := kyberGoToCircl(kyberKey, ecdheKey) - if err != nil { - return err - } - uconn.HandshakeState.State13.KeySharesParams.AddKemKeypair(curveID, circlKyberKey, circlKyberKey.Public()) + // circlKyberKey, err := kyberGoToCircl(kyberKey, ecdheKey) + // if err != nil { + // return err + // } + // uconn.HandshakeState.State13.KeySharesParams.AddKemKeypair(curveID, circlKyberKey, circlKyberKey.Public()) - ext.KeyShares[i].Data = append(ecdheKey.PublicKey().Bytes(), kyberKey.EncapsulationKey()...) + ext.KeyShares[i].Data = append(mlkemKey.EncapsulationKey().Bytes(), ecdheKey.PublicKey().Bytes()...) if !preferredCurveIsSet { // only do this once for the first non-grease curve - uconn.HandshakeState.State13.KeyShareKeys.kyber = kyberKey + uconn.HandshakeState.State13.KeyShareKeys.mlkem = mlkemKey preferredCurveIsSet = true } if len(ext.KeyShares) > i+1 && ext.KeyShares[i+1].Group == X25519 { // Reuse the same X25519 ephemeral key for both keyshares, as allowed by draft-ietf-tls-hybrid-design-09, Section 3.2. uconn.HandshakeState.State13.KeyShareKeys.Ecdhe = ecdheKey - uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey()) + // uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey()) ext.KeyShares[i+1].Data = ecdheKey.PublicKey().Bytes() } } else { @@ -2776,7 +2776,7 @@ func (uconn *UConn) ApplyPreset(p *ClientHelloSpec) error { "To mimic it, fill the Data(key) field manually", curveID) } - uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey()) + // uconn.HandshakeState.State13.KeySharesParams.AddEcdheKeypair(curveID, ecdheKey, ecdheKey.PublicKey()) ext.KeyShares[i].Data = ecdheKey.PublicKey().Bytes() if !preferredCurveIsSet { diff --git a/u_public.go b/u_public.go index a763ddd..a9e851c 100644 --- a/u_public.go +++ b/u_public.go @@ -7,14 +7,12 @@ package tls import ( "crypto" "crypto/ecdh" + "crypto/mlkem" "crypto/x509" - "fmt" "hash" "time" "github.com/cloudflare/circl/kem" - "github.com/cloudflare/circl/kem/hybrid" - "github.com/refraction-networking/utls/internal/mlkem768" ) // ClientHandshakeState includes both TLS 1.3-only and TLS 1.2-only states, @@ -64,45 +62,45 @@ type TLS12OnlyState struct { Suite PubCipherSuite } -func mlkemCirclToGo(circlKey kem.PrivateKey) (*mlkem768.DecapsulationKey, *ecdh.PrivateKey, error) { - if circlKey.Scheme().Name() != "Kyber768-X25519" { - return nil, nil, fmt.Errorf("circl key is not Kyber768-X25519") - } +// func mlkemCirclToGo(circlKey kem.PrivateKey) (*mlkem768.DecapsulationKey, *ecdh.PrivateKey, error) { +// if circlKey.Scheme().Name() != "Kyber768-X25519" { +// return nil, nil, fmt.Errorf("circl key is not Kyber768-X25519") +// } - encodedKey, err := circlKey.MarshalBinary() - if err != nil { - return nil, nil, err - } +// encodedKey, err := circlKey.MarshalBinary() +// if err != nil { +// return nil, nil, err +// } - ecdhKey := encodedKey[:x25519PublicKeySize] - kyberKey := encodedKey[x25519PublicKeySize:] +// ecdhKey := encodedKey[:x25519PublicKeySize] +// kyberKey := encodedKey[x25519PublicKeySize:] - goKyberkey, err := mlkem768.NewKeyFromExtendedEncoding(kyberKey) - if err != nil { - return nil, nil, err - } +// goKyberkey, err := mlkem768.NewKeyFromExtendedEncoding(kyberKey) +// if err != nil { +// return nil, nil, err +// } - goEcdhKey, err := ecdh.X25519().NewPrivateKey(ecdhKey) - if err != nil { - return nil, nil, err - } +// goEcdhKey, err := ecdh.X25519().NewPrivateKey(ecdhKey) +// if err != nil { +// return nil, nil, err +// } - return goKyberkey, goEcdhKey, nil -} +// return goKyberkey, goEcdhKey, nil +// } func (chs *TLS13OnlyState) private13KeyShareKeys() *keySharePrivateKeys { if chs.KeyShareKeys != nil { return chs.KeyShareKeys.ToPrivate() } - if chs.KEMKey != nil { - if kyberKey, ecdhKey, err := mlkemCirclToGo(chs.KEMKey.SecretKey); err == nil { - return &keySharePrivateKeys{ - kyber: kyberKey, - ecdhe: ecdhKey, - } - } - } + // if chs.KEMKey != nil { + // if kyberKey, ecdhKey, err := mlkemCirclToGo(chs.KEMKey.SecretKey); err == nil { + // return &keySharePrivateKeys{ + // kyber: kyberKey, + // ecdhe: ecdhKey, + // } + // } + // } if chs.EcdheKey != nil { return &keySharePrivateKeys{ @@ -113,9 +111,9 @@ func (chs *TLS13OnlyState) private13KeyShareKeys() *keySharePrivateKeys { return nil } -func kyberGoToCircl(kyberKey *mlkem768.DecapsulationKey, ecdhKey *ecdh.PrivateKey) (kem.PrivateKey, error) { - return hybrid.Kyber768X25519().UnmarshalBinaryPrivateKey(append(ecdhKey.Bytes(), kyberKey.Bytes()...)) -} +// func kyberGoToCircl(kyberKey *mlkem768.DecapsulationKey, ecdhKey *ecdh.PrivateKey) (kem.PrivateKey, error) { +// return hybrid.Kyber768X25519().UnmarshalBinaryPrivateKey(append(ecdhKey.Bytes(), kyberKey.Bytes()...)) +// } func (chs *PubClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 { if chs == nil { @@ -128,16 +126,14 @@ func (chs *PubClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 { keyShareKeys: chs.State13.private13KeyShareKeys(), keySharesParams: chs.State13.KeySharesParams, - session: chs.Session, - earlySecret: chs.State13.EarlySecret, - binderKey: chs.State13.BinderKey, + session: chs.Session, + binderKey: chs.State13.BinderKey, certReq: chs.State13.CertReq.toPrivate(), usingPSK: chs.State13.UsingPSK, sentDummyCCS: chs.State13.SentDummyCCS, suite: chs.State13.Suite.toPrivate(), transcript: chs.State13.Transcript, - masterSecret: chs.MasterSecret, trafficSecret: chs.State13.TrafficSecret, uconn: chs.uconn, @@ -152,7 +148,7 @@ func (chs13 *clientHandshakeStateTLS13) toPublic13() *PubClientHandshakeState { tls13State := TLS13OnlyState{ KeySharesParams: chs13.keySharesParams, KeyShareKeys: chs13.keyShareKeys.ToPublic(), - EarlySecret: chs13.earlySecret, + EarlySecret: chs13.earlySecret.Secret(), BinderKey: chs13.binderKey, CertReq: chs13.certReq.toPublic(), UsingPSK: chs13.usingPSK, @@ -168,7 +164,7 @@ func (chs13 *clientHandshakeStateTLS13) toPublic13() *PubClientHandshakeState { Session: chs13.session, - MasterSecret: chs13.masterSecret, + MasterSecret: chs13.masterSecret.Secret(), State13: tls13State, @@ -599,22 +595,48 @@ type FinishedHash struct { Buffer []byte Version uint16 - Prf func(result, secret, label, seed []byte) + Prfv2 prfFunc + + // Deprecated: Use Prfv2 instead. Prfv2 will be used if both are set. + Prf prfFuncOld +} + +type prfFuncOld func(result, secret, label, seed []byte) + +func prfFuncV1ToV2(v1 prfFuncOld) prfFunc { + return func(secret []byte, label string, seed []byte, keyLen int) []byte { + res := make([]byte, keyLen) + v1(res, secret, []byte(label), seed) + return res + } +} + +func prfFuncV2ToV1(v2 prfFunc) prfFuncOld { + return func(result, secret, label, seed []byte) { + copy(result, v2(secret, string(label), seed, len(result))) + } } func (fh *FinishedHash) getPrivateObj() finishedHash { if fh == nil { return finishedHash{} } else { - return finishedHash{ + res := finishedHash{ client: fh.Client, server: fh.Server, clientMD5: fh.ClientMD5, serverMD5: fh.ServerMD5, buffer: fh.Buffer, version: fh.Version, - prf: fh.Prf, } + + if fh.Prfv2 != nil { + res.prf = fh.Prfv2 + } else if fh.Prf != nil { + res.prf = prfFuncV1ToV2(fh.Prf) + } + + return res } } @@ -622,14 +644,19 @@ func (fh *finishedHash) getPublicObj() FinishedHash { if fh == nil { return FinishedHash{} } else { - return FinishedHash{ + res := FinishedHash{ Client: fh.client, Server: fh.server, ClientMD5: fh.clientMD5, ServerMD5: fh.serverMD5, Buffer: fh.buffer, Version: fh.version, - Prf: fh.prf} + } + + res.Prfv2 = fh.prf + res.Prf = prfFuncV2ToV1(fh.prf) + + return res } } @@ -894,14 +921,14 @@ func (kpk *kemPrivateKey) ToPublic() *KemPrivateKey { type KeySharePrivateKeys struct { CurveID CurveID Ecdhe *ecdh.PrivateKey - kyber *mlkem768.DecapsulationKey + mlkem *mlkem.DecapsulationKey768 } func (ksp *KeySharePrivateKeys) ToPrivate() *keySharePrivateKeys { return &keySharePrivateKeys{ curveID: ksp.CurveID, ecdhe: ksp.Ecdhe, - kyber: ksp.kyber, + mlkem: ksp.mlkem, } } @@ -909,6 +936,6 @@ func (ksp *keySharePrivateKeys) ToPublic() *KeySharePrivateKeys { return &KeySharePrivateKeys{ CurveID: ksp.curveID, Ecdhe: ksp.ecdhe, - kyber: ksp.kyber, + mlkem: ksp.mlkem, } } diff --git a/u_session_controller.go b/u_session_controller.go index 4c6a79c..1ba87b8 100644 --- a/u_session_controller.go +++ b/u_session_controller.go @@ -3,6 +3,8 @@ package tls import ( "errors" "fmt" + + "github.com/refraction-networking/utls/internal/tls13" ) // Tracking the state of calling conn.loadSession @@ -161,7 +163,7 @@ func (s *sessionController) initSessionTicketExt(session *SessionState, ticket [ // initPSK initializes the PSK extension using a valid session. The PSK extension // should not be initialized previously, and the parameters must not be nil; // otherwise, this function will trigger a panic. -func (s *sessionController) initPskExt(session *SessionState, earlySecret []byte, binderKey []byte, pskIdentities []pskIdentity) { +func (s *sessionController) initPskExt(session *SessionState, earlySecret *tls13.EarlySecret, binderKey []byte, pskIdentities []pskIdentity) { s.assertNotLocked("initPskExt") s.assertHelloNotBuilt("initPskExt") s.assertControllerState("initPskExt", NoSession) @@ -177,7 +179,7 @@ func (s *sessionController) initPskExt(session *SessionState, earlySecret []byte ObfuscatedTicketAge: private.obfuscatedTicketAge, } }) - e.InitializeByUtls(session, earlySecret, binderKey, publicPskIdentities) + e.InitializeByUtls(session, earlySecret.Secret(), binderKey, publicPskIdentities) }) s.state = PskExtInitialized diff --git a/u_tls_extensions.go b/u_tls_extensions.go index f68e7b6..5b3a5b4 100644 --- a/u_tls_extensions.go +++ b/u_tls_extensions.go @@ -886,16 +886,6 @@ func (e *ExtendedMasterSecretExtension) Write(_ []byte) (int, error) { return 0, nil } -// var extendedMasterSecretLabel = []byte("extended master secret") - -// extendedMasterFromPreMasterSecret generates the master secret from the pre-master -// secret and session hash. See https://tools.ietf.org/html/rfc7627#section-4 -func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, sessionHash []byte) []byte { - masterSecret := make([]byte, masterSecretLength) - prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, sessionHash) - return masterSecret -} - // GREASE stinks with dead parrots, have to be super careful, and, if possible, not include GREASE // https://github.com/google/boringssl/blob/1c68fa2350936ca5897a66b430ebaf333a0e43f5/ssl/internal.h const (