package tls
import (
"crypto"
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"fmt"
"hash"
)
func splitPreMasterSecret (secret []byte ) (s1 , s2 []byte ) {
s1 = secret [0 : (len (secret )+1 )/2 ]
s2 = secret [len (secret )/2 :]
return
}
func pHash (result , secret , seed []byte , hash func () hash .Hash ) {
h := hmac .New (hash , secret )
h .Write (seed )
a := h .Sum (nil )
j := 0
for j < len (result ) {
h .Reset ()
h .Write (a )
h .Write (seed )
b := h .Sum (nil )
copy (result [j :], b )
j += len (b )
h .Reset ()
h .Write (a )
a = h .Sum (nil )
}
}
func prf10 (result , secret , label , seed []byte ) {
hashSHA1 := sha1 .New
hashMD5 := md5 .New
labelAndSeed := make ([]byte , len (label )+len (seed ))
copy (labelAndSeed , label )
copy (labelAndSeed [len (label ):], seed )
s1 , s2 := splitPreMasterSecret (secret )
pHash (result , s1 , labelAndSeed , hashMD5 )
result2 := make ([]byte , len (result ))
pHash (result2 , s2 , labelAndSeed , hashSHA1 )
for i , b := range result2 {
result [i ] ^= b
}
}
func prf12 (hashFunc func () hash .Hash ) func (result , secret , label , seed []byte ) {
return func (result , secret , label , seed []byte ) {
labelAndSeed := make ([]byte , len (label )+len (seed ))
copy (labelAndSeed , label )
copy (labelAndSeed [len (label ):], seed )
pHash (result , secret , labelAndSeed , hashFunc )
}
}
const (
masterSecretLength = 48
finishedVerifyLength = 12
)
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" )
func prfAndHashForVersion (version uint16 , suite *cipherSuite ) (func (result , secret , label , seed []byte ), crypto .Hash ) {
switch version {
case VersionTLS10 , VersionTLS11 :
return prf10 , crypto .Hash (0 )
case VersionTLS12 :
if suite .flags &suiteSHA384 != 0 {
return prf12 (sha512 .New384 ), crypto .SHA384
}
return prf12 (sha256 .New ), crypto .SHA256
default :
panic ("unknown version" )
}
}
func prfForVersion (version uint16 , suite *cipherSuite ) func (result , secret , label , seed []byte ) {
prf , _ := prfAndHashForVersion (version , suite )
return prf
}
func masterFromPreMasterSecret (version uint16 , suite *cipherSuite , preMasterSecret , clientRandom , serverRandom []byte ) []byte {
seed := make ([]byte , 0 , len (clientRandom )+len (serverRandom ))
seed = append (seed , clientRandom ...)
seed = append (seed , serverRandom ...)
masterSecret := make ([]byte , masterSecretLength )
prfForVersion (version , suite )(masterSecret , preMasterSecret , masterSecretLabel , seed )
return masterSecret
}
func extMasterFromPreMasterSecret (version uint16 , suite *cipherSuite , preMasterSecret , transcript []byte ) []byte {
masterSecret := make ([]byte , masterSecretLength )
prfForVersion (version , suite )(masterSecret , preMasterSecret , extendedMasterSecretLabel , transcript )
return masterSecret
}
func keysFromMasterSecret (version uint16 , suite *cipherSuite , masterSecret , clientRandom , serverRandom []byte , macLen , keyLen , ivLen int ) (clientMAC , serverMAC , clientKey , serverKey , clientIV , serverIV []byte ) {
seed := make ([]byte , 0 , len (serverRandom )+len (clientRandom ))
seed = append (seed , serverRandom ...)
seed = append (seed , clientRandom ...)
n := 2 *macLen + 2 *keyLen + 2 *ivLen
keyMaterial := make ([]byte , n )
prfForVersion (version , suite )(keyMaterial , masterSecret , keyExpansionLabel , seed )
clientMAC = keyMaterial [:macLen ]
keyMaterial = keyMaterial [macLen :]
serverMAC = keyMaterial [:macLen ]
keyMaterial = keyMaterial [macLen :]
clientKey = keyMaterial [:keyLen ]
keyMaterial = keyMaterial [keyLen :]
serverKey = keyMaterial [:keyLen ]
keyMaterial = keyMaterial [keyLen :]
clientIV = keyMaterial [:ivLen ]
keyMaterial = keyMaterial [ivLen :]
serverIV = keyMaterial [:ivLen ]
return
}
func newFinishedHash (version uint16 , cipherSuite *cipherSuite ) finishedHash {
var buffer []byte
if version >= VersionTLS12 {
buffer = []byte {}
}
prf , hash := prfAndHashForVersion (version , cipherSuite )
if hash != 0 {
return finishedHash {hash .New (), hash .New (), nil , nil , buffer , version , prf }
}
return finishedHash {sha1 .New (), sha1 .New (), md5 .New (), md5 .New (), buffer , version , prf }
}
type finishedHash struct {
client hash .Hash
server hash .Hash
clientMD5 hash .Hash
serverMD5 hash .Hash
buffer []byte
version uint16
prf func (result, secret, label, seed []byte )
}
func (h *finishedHash ) Write (msg []byte ) (n int , err error ) {
h .client .Write (msg )
h .server .Write (msg )
if h .version < VersionTLS12 {
h .clientMD5 .Write (msg )
h .serverMD5 .Write (msg )
}
if h .buffer != nil {
h .buffer = append (h .buffer , msg ...)
}
return len (msg ), nil
}
func (h finishedHash ) Sum () []byte {
if h .version >= VersionTLS12 {
return h .client .Sum (nil )
}
out := make ([]byte , 0 , md5 .Size +sha1 .Size )
out = h .clientMD5 .Sum (out )
return h .client .Sum (out )
}
func (h finishedHash ) clientSum (masterSecret []byte ) []byte {
out := make ([]byte , finishedVerifyLength )
h .prf (out , masterSecret , clientFinishedLabel , h .Sum ())
return out
}
func (h finishedHash ) serverSum (masterSecret []byte ) []byte {
out := make ([]byte , finishedVerifyLength )
h .prf (out , masterSecret , serverFinishedLabel , h .Sum ())
return out
}
func (h finishedHash ) hashForClientCertificate (sigType uint8 , hashAlg crypto .Hash ) []byte {
if (h .version >= VersionTLS12 || sigType == signatureEd25519 ) && h .buffer == nil {
panic ("tls: handshake hash for a client certificate requested after discarding the handshake buffer" )
}
if sigType == signatureEd25519 {
return h .buffer
}
if h .version >= VersionTLS12 {
hash := hashAlg .New ()
hash .Write (h .buffer )
return hash .Sum (nil )
}
if sigType == signatureECDSA {
return h .server .Sum (nil )
}
return h .Sum ()
}
func (h *finishedHash ) discardHandshakeBuffer () {
h .buffer = nil
}
func noExportedKeyingMaterial (label string , context []byte , length int ) ([]byte , error ) {
return nil , errors .New ("crypto/tls: ExportKeyingMaterial is unavailable when renegotiation is enabled" )
}
func ekmFromMasterSecret (version uint16 , suite *cipherSuite , masterSecret , clientRandom , serverRandom []byte ) func (string , []byte , int ) ([]byte , error ) {
return func (label string , context []byte , length int ) ([]byte , error ) {
switch label {
case "client finished" , "server finished" , "master secret" , "key expansion" :
return nil , fmt .Errorf ("crypto/tls: reserved ExportKeyingMaterial label: %s" , label )
}
seedLen := len (serverRandom ) + len (clientRandom )
if context != nil {
seedLen += 2 + len (context )
}
seed := make ([]byte , 0 , seedLen )
seed = append (seed , clientRandom ...)
seed = append (seed , serverRandom ...)
if context != nil {
if len (context ) >= 1 <<16 {
return nil , fmt .Errorf ("crypto/tls: ExportKeyingMaterial context too long" )
}
seed = append (seed , byte (len (context )>>8 ), byte (len (context )))
seed = append (seed , context ...)
}
keyMaterial := make ([]byte , length )
prfForVersion (version , suite )(keyMaterial , masterSecret , []byte (label ), seed )
return keyMaterial , nil
}
}
The pages are generated with Golds v0.6.7 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds .