package prf
import (
ellipticStdlib "crypto/elliptic"
"crypto/hmac"
"encoding/binary"
"errors"
"fmt"
"hash"
"math"
"github.com/pion/dtls/v3/pkg/crypto/elliptic"
"github.com/pion/dtls/v3/pkg/protocol"
"golang.org/x/crypto/curve25519"
)
const (
masterSecretLabel = "master secret"
extendedMasterSecretLabel = "extended master secret"
keyExpansionLabel = "key expansion"
verifyDataClientLabel = "client finished"
verifyDataServerLabel = "server finished"
)
type HashFunc func () hash .Hash
type EncryptionKeys struct {
MasterSecret []byte
ClientMACKey []byte
ServerMACKey []byte
ClientWriteKey []byte
ServerWriteKey []byte
ClientWriteIV []byte
ServerWriteIV []byte
}
var errInvalidNamedCurve = &protocol .FatalError {Err : errors .New ("invalid named curve" )}
func (e *EncryptionKeys ) String () string {
return fmt .Sprintf (`encryptionKeys:
- masterSecret: %#v
- clientMACKey: %#v
- serverMACKey: %#v
- clientWriteKey: %#v
- serverWriteKey: %#v
- clientWriteIV: %#v
- serverWriteIV: %#v
` ,
e .MasterSecret ,
e .ClientMACKey ,
e .ServerMACKey ,
e .ClientWriteKey ,
e .ServerWriteKey ,
e .ClientWriteIV ,
e .ServerWriteIV )
}
func PSKPreMasterSecret (psk []byte ) []byte {
pskLen := uint16 (len (psk ))
out := append (make ([]byte , 2 +pskLen +2 ), psk ...)
binary .BigEndian .PutUint16 (out , pskLen )
binary .BigEndian .PutUint16 (out [2 +pskLen :], pskLen )
return out
}
func EcdhePSKPreMasterSecret (psk , publicKey , privateKey []byte , curve elliptic .Curve ) ([]byte , error ) {
preMasterSecret , err := PreMasterSecret (publicKey , privateKey , curve )
if err != nil {
return nil , err
}
out := make ([]byte , 2 +len (preMasterSecret )+2 +len (psk ))
offset := 0
binary .BigEndian .PutUint16 (out [offset :], uint16 (len (preMasterSecret )))
offset += 2
copy (out [offset :], preMasterSecret )
offset += len (preMasterSecret )
binary .BigEndian .PutUint16 (out [offset :], uint16 (len (psk )))
offset += 2
copy (out [offset :], psk )
return out , nil
}
func PreMasterSecret (publicKey , privateKey []byte , curve elliptic .Curve ) ([]byte , error ) {
switch curve {
case elliptic .X25519 :
return curve25519 .X25519 (privateKey , publicKey )
case elliptic .P256 :
return ellipticCurvePreMasterSecret (publicKey , privateKey , ellipticStdlib .P256 (), ellipticStdlib .P256 ())
case elliptic .P384 :
return ellipticCurvePreMasterSecret (publicKey , privateKey , ellipticStdlib .P384 (), ellipticStdlib .P384 ())
default :
return nil , errInvalidNamedCurve
}
}
func ellipticCurvePreMasterSecret(publicKey , privateKey []byte , c1 , c2 ellipticStdlib .Curve ) ([]byte , error ) {
x , y := ellipticStdlib .Unmarshal (c1 , publicKey )
if x == nil || y == nil {
return nil , errInvalidNamedCurve
}
result , _ := c2 .ScalarMult (x , y , privateKey )
preMasterSecret := make ([]byte , (c2 .Params ().BitSize +7 )>>3 )
resultBytes := result .Bytes ()
copy (preMasterSecret [len (preMasterSecret )-len (resultBytes ):], resultBytes )
return preMasterSecret , nil
}
func PHash (secret , seed []byte , requestedLength int , hashFunc HashFunc ) ([]byte , error ) {
hmacSHA256 := func (key , data []byte ) ([]byte , error ) {
mac := hmac .New (hashFunc , key )
if _ , err := mac .Write (data ); err != nil {
return nil , err
}
return mac .Sum (nil ), nil
}
var err error
lastRound := seed
out := []byte {}
iterations := int (math .Ceil (float64 (requestedLength ) / float64 (hashFunc ().Size ())))
for i := 0 ; i < iterations ; i ++ {
lastRound , err = hmacSHA256 (secret , lastRound )
if err != nil {
return nil , err
}
withSecret , err := hmacSHA256 (secret , append (lastRound , seed ...))
if err != nil {
return nil , err
}
out = append (out , withSecret ...)
}
return out [:requestedLength ], nil
}
func ExtendedMasterSecret (preMasterSecret , sessionHash []byte , h HashFunc ) ([]byte , error ) {
seed := append ([]byte (extendedMasterSecretLabel ), sessionHash ...)
return PHash (preMasterSecret , seed , 48 , h )
}
func MasterSecret (preMasterSecret , clientRandom , serverRandom []byte , h HashFunc ) ([]byte , error ) {
seed := append (append ([]byte (masterSecretLabel ), clientRandom ...), serverRandom ...)
return PHash (preMasterSecret , seed , 48 , h )
}
func GenerateEncryptionKeys (
masterSecret , clientRandom , serverRandom []byte ,
macLen , keyLen , ivLen int ,
h HashFunc ,
) (*EncryptionKeys , error ) {
seed := append (append ([]byte (keyExpansionLabel ), serverRandom ...), clientRandom ...)
keyMaterial , err := PHash (masterSecret , seed , (2 *macLen )+(2 *keyLen )+(2 *ivLen ), h )
if err != nil {
return nil , err
}
clientMACKey := keyMaterial [:macLen ]
keyMaterial = keyMaterial [macLen :]
serverMACKey := keyMaterial [:macLen ]
keyMaterial = keyMaterial [macLen :]
clientWriteKey := keyMaterial [:keyLen ]
keyMaterial = keyMaterial [keyLen :]
serverWriteKey := keyMaterial [:keyLen ]
keyMaterial = keyMaterial [keyLen :]
clientWriteIV := keyMaterial [:ivLen ]
keyMaterial = keyMaterial [ivLen :]
serverWriteIV := keyMaterial [:ivLen ]
return &EncryptionKeys {
MasterSecret : masterSecret ,
ClientMACKey : clientMACKey ,
ServerMACKey : serverMACKey ,
ClientWriteKey : clientWriteKey ,
ServerWriteKey : serverWriteKey ,
ClientWriteIV : clientWriteIV ,
ServerWriteIV : serverWriteIV ,
}, nil
}
func prfVerifyData(masterSecret , handshakeBodies []byte , label string , hashFunc HashFunc ) ([]byte , error ) {
h := hashFunc ()
if _ , err := h .Write (handshakeBodies ); err != nil {
return nil , err
}
seed := append ([]byte (label ), h .Sum (nil )...)
return PHash (masterSecret , seed , 12 , hashFunc )
}
func VerifyDataClient (masterSecret , handshakeBodies []byte , h HashFunc ) ([]byte , error ) {
return prfVerifyData (masterSecret , handshakeBodies , verifyDataClientLabel , h )
}
func VerifyDataServer (masterSecret , handshakeBodies []byte , h HashFunc ) ([]byte , error ) {
return prfVerifyData (masterSecret , handshakeBodies , verifyDataServerLabel , h )
}
The pages are generated with Golds v0.8.2 . (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 @zigo_101 (reachable from the left QR code) to get the latest news of Golds .