// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package tls

import (
	
	
	
	
	
	
	
	
	
)

// Split a premaster secret in two as specified in RFC 4346, Section 5.
func splitPreMasterSecret( []byte) (,  []byte) {
	 = [0 : (len()+1)/2]
	 = [len()/2:]
	return
}

// pHash implements the P_hash function, as defined in RFC 4346, Section 5.
func pHash(, ,  []byte,  func() hash.Hash) {
	 := hmac.New(, )
	.Write()
	 := .Sum(nil)

	 := 0
	for  < len() {
		.Reset()
		.Write()
		.Write()
		 := .Sum(nil)
		copy([:], )
		 += len()

		.Reset()
		.Write()
		 = .Sum(nil)
	}
}

// prf10 implements the TLS 1.0 pseudo-random function, as defined in RFC 2246, Section 5.
func prf10(, , ,  []byte) {
	 := sha1.New
	 := md5.New

	 := make([]byte, len()+len())
	copy(, )
	copy([len():], )

	,  := splitPreMasterSecret()
	pHash(, , , )
	 := make([]byte, len())
	pHash(, , , )

	for ,  := range  {
		[] ^= 
	}
}

// prf12 implements the TLS 1.2 pseudo-random function, as defined in RFC 5246, Section 5.
func prf12( func() hash.Hash) func(, , ,  []byte) {
	return func(, , ,  []byte) {
		 := make([]byte, len()+len())
		copy(, )
		copy([len():], )

		pHash(, , , )
	}
}

const (
	masterSecretLength   = 48 // Length of a master secret in TLS 1.1.
	finishedVerifyLength = 12 // Length of verify_data in a Finished message.
)

var masterSecretLabel = []byte("master secret")
var keyExpansionLabel = []byte("key expansion")
var clientFinishedLabel = []byte("client finished")
var serverFinishedLabel = []byte("server finished")

func prfAndHashForVersion( uint16,  *cipherSuite) (func(, , ,  []byte), crypto.Hash) {
	switch  {
	case VersionTLS10, VersionTLS11:
		return prf10, crypto.Hash(0)
	case VersionTLS12:
		if .flags&suiteSHA384 != 0 {
			return prf12(sha512.New384), crypto.SHA384
		}
		return prf12(sha256.New), crypto.SHA256
	default:
		panic("unknown version")
	}
}

func prfForVersion( uint16,  *cipherSuite) func(, , ,  []byte) {
	,  := prfAndHashForVersion(, )
	return 
}

// masterFromPreMasterSecret generates the master secret from the pre-master
// secret. See RFC 5246, Section 8.1.
func masterFromPreMasterSecret( uint16,  *cipherSuite, , ,  []byte) []byte {
	 := make([]byte, 0, len()+len())
	 = append(, ...)
	 = append(, ...)

	 := make([]byte, masterSecretLength)
	prfForVersion(, )(, , masterSecretLabel, )
	return 
}

// keysFromMasterSecret generates the connection keys from the master
// secret, given the lengths of the MAC key, cipher key and IV, as defined in
// RFC 2246, Section 6.3.
func keysFromMasterSecret( uint16,  *cipherSuite, , ,  []byte, , ,  int) (, , , , ,  []byte) {
	 := make([]byte, 0, len()+len())
	 = append(, ...)
	 = append(, ...)

	 := 2* + 2* + 2*
	 := make([]byte, )
	prfForVersion(, )(, , keyExpansionLabel, )
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	 = [:]
	return
}

func newFinishedHash( uint16,  *cipherSuite) finishedHash {
	var  []byte
	if  >= VersionTLS12 {
		 = []byte{}
	}

	,  := prfAndHashForVersion(, )
	if  != 0 {
		return finishedHash{.New(), .New(), nil, nil, , , }
	}

	return finishedHash{sha1.New(), sha1.New(), md5.New(), md5.New(), , , }
}

// A finishedHash calculates the hash of a set of handshake messages suitable
// for including in a Finished message.
type finishedHash struct {
	client hash.Hash
	server hash.Hash

	// Prior to TLS 1.2, an additional MD5 hash is required.
	clientMD5 hash.Hash
	serverMD5 hash.Hash

	// In TLS 1.2, a full buffer is sadly required.
	buffer []byte

	version uint16
	prf     func(result, secret, label, seed []byte)
}

func ( *finishedHash) ( []byte) ( int,  error) {
	.client.Write()
	.server.Write()

	if .version < VersionTLS12 {
		.clientMD5.Write()
		.serverMD5.Write()
	}

	if .buffer != nil {
		.buffer = append(.buffer, ...)
	}

	return len(), nil
}

func ( finishedHash) () []byte {
	if .version >= VersionTLS12 {
		return .client.Sum(nil)
	}

	 := make([]byte, 0, md5.Size+sha1.Size)
	 = .clientMD5.Sum()
	return .client.Sum()
}

// clientSum returns the contents of the verify_data member of a client's
// Finished message.
func ( finishedHash) ( []byte) []byte {
	 := make([]byte, finishedVerifyLength)
	.prf(, , clientFinishedLabel, .Sum())
	return 
}

// serverSum returns the contents of the verify_data member of a server's
// Finished message.
func ( finishedHash) ( []byte) []byte {
	 := make([]byte, finishedVerifyLength)
	.prf(, , serverFinishedLabel, .Sum())
	return 
}

// hashForClientCertificate returns the handshake messages so far, pre-hashed if
// necessary, suitable for signing by a TLS client certificate.
func ( finishedHash) ( uint8,  crypto.Hash,  []byte) []byte {
	if (.version >= VersionTLS12 ||  == signatureEd25519) && .buffer == nil {
		panic("tls: handshake hash for a client certificate requested after discarding the handshake buffer")
	}

	if  == signatureEd25519 {
		return .buffer
	}

	if .version >= VersionTLS12 {
		 := .New()
		.Write(.buffer)
		return .Sum(nil)
	}

	if  == signatureECDSA {
		return .server.Sum(nil)
	}

	return .Sum()
}

// discardHandshakeBuffer is called when there is no more need to
// buffer the entirety of the handshake messages.
func ( *finishedHash) () {
	.buffer = nil
}

// noExportedKeyingMaterial is used as a value of
// ConnectionState.ekm when renegotiation is enabled and thus
// we wish to fail all key-material export requests.
func noExportedKeyingMaterial( string,  []byte,  int) ([]byte, error) {
	return nil, errors.New("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled")
}

// ekmFromMasterSecret generates exported keying material as defined in RFC 5705.
func ekmFromMasterSecret( uint16,  *cipherSuite, , ,  []byte) func(string, []byte, int) ([]byte, error) {
	return func( string,  []byte,  int) ([]byte, error) {
		switch  {
		case "client finished", "server finished", "master secret", "key expansion":
			// These values are reserved and may not be used.
			return nil, fmt.Errorf("crypto/tls: reserved ExportKeyingMaterial label: %s", )
		}

		 := len() + len()
		if  != nil {
			 += 2 + len()
		}
		 := make([]byte, 0, )

		 = append(, ...)
		 = append(, ...)

		if  != nil {
			if len() >= 1<<16 {
				return nil, fmt.Errorf("crypto/tls: ExportKeyingMaterial context too long")
			}
			 = append(, byte(len()>>8), byte(len()))
			 = append(, ...)
		}

		 := make([]byte, )
		prfForVersion(, )(, , []byte(), )
		return , nil
	}
}