Jonathan Turner 9 лет назад
Родитель
Сommit
9104cc4f2a

+ 0 - 79
crypto/aes/aes_test.go

@@ -1,79 +0,0 @@
-package aes
-
-import (
-	"encoding/hex"
-	"github.com/stretchr/testify/assert"
-	"testing"
-)
-
-func TestAesCtsHmacSha196_Encrypt_Decrypt(t *testing.T) {
-	iv := make([]byte, 16)
-	key, _ := hex.DecodeString("636869636b656e207465726979616b69")
-	var tests = []struct {
-		plain  string
-		cipher string
-		nextIV string
-	}{
-		//Test vectors from RFC 3962 Appendix B
-		{"4920776f756c64206c696b652074686520", "c6353568f2bf8cb4d8a580362da7ff7f97", "c6353568f2bf8cb4d8a580362da7ff7f"},
-		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320", "fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5", "fc00783e0efdb2c1d445d4c8eff7ed22"},
-		{"4920776f756c64206c696b65207468652047656e6572616c2047617527732043", "39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584", "39312523a78662d5be7fcbcc98ebf5a8"},
-		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c", "97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5", "b3fffd940c16a18c1b5549d2f838029e"},
-		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20", "97687268d6ecccc0c07b25e25ecfe5849dad8bbb96c4cdc03bc103e1a194bbd839312523a78662d5be7fcbcc98ebf5a8", "9dad8bbb96c4cdc03bc103e1a194bbd8"},
-		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a84807efe836ee89a526730dbc2f7bc8409dad8bbb96c4cdc03bc103e1a194bbd8", "4807efe836ee89a526730dbc2f7bc840"},
-	}
-	var e Aes128CtsHmacSha96
-	for i, test := range tests {
-		m, _ := hex.DecodeString(test.plain)
-		niv, c, err := encryptCTS(key, iv, m, e)
-		if err != nil {
-			t.Errorf("Encryption failed for test %v: %v", i+1, err)
-		}
-		assert.Equal(t, test.cipher, hex.EncodeToString(c), "Encrypted result not as expected")
-		assert.Equal(t, test.nextIV, hex.EncodeToString(niv), "Next state IV not as expected")
-	}
-	//t.Log("AES CTS Encryption tests finished")
-	for i, test := range tests {
-		b, _ := hex.DecodeString(test.cipher)
-		p, err := decryptCTS(key, b, e)
-		if err != nil {
-			t.Errorf("Decryption failed for test %v: %v", i+1, err)
-		}
-		assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected")
-	}
-	//t.Log("AES CTS Decryption tests finished")
-}
-
-func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
-	// Test vectors from RFC 3962 Appendix B
-	b, _ := hex.DecodeString("1234567878563412")
-	s := string(b)
-	b, _ = hex.DecodeString("f09d849e")
-	s2 := string(b)
-	var tests = []struct {
-		iterations int
-		phrase     string
-		salt       string
-		pbkdf2     string
-		key        string
-	}{
-		{1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837", "fe697b52bc0d3ce14432ba036a92e65bbb52280990a2fa27883998d72af30161"},
-		{2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86", "a2e16d16b36069c135d5e9d2e25f896102685618b95914b467c67622225824ff"},
-		{1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13", "55a6ac740ad17b4846941051e1e8b0a7548d93b0ab30a8bc3ff16280382b8c2a"},
-		{5, "password", s, "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee", "97a4e786be20d81a382d5ebc96d5909cabcdadc87ca48f574504159f16c36e31"},
-		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1", "89adee3608db8bc71f1bfbfe459486b05618b70cbae22092534e56c553ba4b34"},
-		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"},
-		{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"},
-	}
-	var e Aes256CtsHmacSha96
-	for i, test := range tests {
-
-		assert.Equal(t, test.pbkdf2, hex.EncodeToString(stringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected")
-		k, err := stringToKeyIter(test.phrase, test.salt, test.iterations, e)
-		if err != nil {
-			t.Errorf("Error in processing string to key for test %d: %v", i, err)
-		}
-		assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
-
-	}
-}

+ 10 - 7
crypto/aes/aes128-cts-hmac-sha1-96.go → crypto/aes128-cts-hmac-sha1-96.go

@@ -1,4 +1,4 @@
-package aes
+package crypto
 
 import (
 	"crypto/aes"
@@ -6,7 +6,9 @@ import (
 	"crypto/sha1"
 	"errors"
 	"fmt"
+	"github.com/jcmturner/gokrb5/crypto/aescts"
 	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -102,16 +104,16 @@ func (e Aes128CtsHmacSha96) GetCypherBlockBitLength() int {
 }
 
 func (e Aes128CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
-	return stringToKey(secret, salt, s2kparams, e)
+	return rfc3961.StringToKey(secret, salt, s2kparams, e)
 }
 
 func (e Aes128CtsHmacSha96) RandomToKey(b []byte) []byte {
-	return randomToKey(b)
+	return rfc3961.RandomToKey(b)
 }
 
 func (e Aes128CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
 	ivz := make([]byte, aes.BlockSize)
-	return encryptCTS(key, ivz, data, e)
+	return aescts.EncryptCTS(key, ivz, data)
 }
 
 func (e Aes128CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
@@ -148,7 +150,8 @@ func (e Aes128CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([
 }
 
 func (e Aes128CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
-	return decryptCTS(key, data, e)
+	ivz := make([]byte, aes.BlockSize)
+	return aescts.DecryptCTS(key, ivz, data)
 }
 
 func (e Aes128CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
@@ -171,11 +174,11 @@ func (e Aes128CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32)
 }
 
 func (e Aes128CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
-	return deriveKey(protocolKey, usage, e)
+	return rfc3961.DeriveKey(protocolKey, usage, e)
 }
 
 func (e Aes128CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
-	return deriveRandom(protocolKey, usage, e)
+	return rfc3961.DeriveRandom(protocolKey, usage, e)
 }
 
 func (e Aes128CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {

+ 1 - 1
crypto/aes/aes128-cts-hmac-sha256-128.go → crypto/aes128-cts-hmac-sha256-128.go

@@ -1,6 +1,6 @@
 // +build disabled
 
-package aes
+package crypto
 
 import (
 	"crypto/aes"

+ 1 - 1
crypto/aes/aes128-cts-hmac-sha256-128_test.go → crypto/aes128-cts-hmac-sha256-128_test.go

@@ -1,6 +1,6 @@
 // +build disabled
 
-package aes
+package crypto
 
 import (
 	"encoding/hex"

+ 10 - 57
crypto/aes/aes256-cts-hmac-sha1-96.go → crypto/aes256-cts-hmac-sha1-96.go

@@ -1,12 +1,10 @@
-package aes
+package crypto
 
 import (
 	"crypto/aes"
-	"crypto/rand"
 	"crypto/sha1"
-	"errors"
-	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -102,80 +100,35 @@ func (e Aes256CtsHmacSha96) GetCypherBlockBitLength() int {
 }
 
 func (e Aes256CtsHmacSha96) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
-	return stringToKey(secret, salt, s2kparams, e)
+	return rfc3961.StringToKey(secret, salt, s2kparams, e)
 }
 
 func (e Aes256CtsHmacSha96) RandomToKey(b []byte) []byte {
-	return randomToKey(b)
+	return rfc3961.RandomToKey(b)
 }
 
 func (e Aes256CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
-	ivz := make([]byte, aes.BlockSize)
-	return encryptCTS(key, ivz, data, e)
+	return rfc3961.EncryptData(key, data, e)
 }
 
 func (e Aes256CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
-	//confounder
-	c := make([]byte, e.GetConfounderByteSize())
-	_, err := rand.Read(c)
-	if err != nil {
-		return []byte{}, []byte{}, fmt.Errorf("Could not generate random confounder: %v", err)
-	}
-	plainBytes := append(c, message...)
-
-	// Derive key for encryption from usage
-	var k []byte
-	if usage != 0 {
-		k, err = e.DeriveKey(key, engine.GetUsageKe(usage))
-		if err != nil {
-			return []byte{}, []byte{}, fmt.Errorf("Error deriving key for encryption: %v", err)
-		}
-	}
-
-	// Encrypt the data
-	iv, b, err := e.EncryptData(k, plainBytes)
-	if err != nil {
-		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
-	}
-
-	// Generate and append integrity hash
-	ih, err := engine.GetIntegrityHash(plainBytes, key, usage, e)
-	if err != nil {
-		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
-	}
-	b = append(b, ih...)
-	return iv, b, nil
+	return rfc3961.EncryptMessage(key, message, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
-	return decryptCTS(key, data, e)
+	return rfc3961.DecryptData(key, data, e)
 }
 
 func (e Aes256CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
-	//Derive the key
-	k, err := e.DeriveKey(key, engine.GetUsageKe(usage))
-	if err != nil {
-		return nil, fmt.Errorf("Error deriving key: %v", err)
-	}
-	// Strip off the checksum from the end
-	b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
-	if err != nil {
-		return nil, err
-	}
-	//Verify checksum
-	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
-		return nil, errors.New("Integrity verification failed")
-	}
-	//Remove the confounder bytes
-	return b[e.GetConfounderByteSize():], nil
+	return rfc3961.DecryptMessage(key, ciphertext, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
-	return deriveKey(protocolKey, usage, e)
+	return rfc3961.DeriveKey(protocolKey, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
-	return deriveRandom(protocolKey, usage, e)
+	return rfc3961.DeriveRandom(protocolKey, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {

+ 42 - 0
crypto/aes256-cts-hmac-sha1-96_test.go

@@ -0,0 +1,42 @@
+package crypto
+
+import (
+	"encoding/hex"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
+	// Test vectors from RFC 3962 Appendix B
+	b, _ := hex.DecodeString("1234567878563412")
+	s := string(b)
+	b, _ = hex.DecodeString("f09d849e")
+	s2 := string(b)
+	var tests = []struct {
+		iterations int
+		phrase     string
+		salt       string
+		pbkdf2     string
+		key        string
+	}{
+		{1, "password", "ATHENA.MIT.EDUraeburn", "cdedb5281bb2f801565a1122b25635150ad1f7a04bb9f3a333ecc0e2e1f70837", "fe697b52bc0d3ce14432ba036a92e65bbb52280990a2fa27883998d72af30161"},
+		{2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935da05378b93244ec8f48a99e61ad799d86", "a2e16d16b36069c135d5e9d2e25f896102685618b95914b467c67622225824ff"},
+		{1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512ba7e52ddbc5e5142f708a31e2e62b1e13", "55a6ac740ad17b4846941051e1e8b0a7548d93b0ab30a8bc3ff16280382b8c2a"},
+		{5, "password", s, "d1daa78615f287e6a1c8b120d7062a493f98d203e6be49a6adf4fa574b6e64ee", "97a4e786be20d81a382d5ebc96d5909cabcdadc87ca48f574504159f16c36e31"},
+		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9c5ec59f1a452f5cc9ad940fea0598ed1", "89adee3608db8bc71f1bfbfe459486b05618b70cbae22092534e56c553ba4b34"},
+		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"},
+		{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"},
+	}
+	var e Aes256CtsHmacSha96
+	for i, test := range tests {
+
+		assert.Equal(t, test.pbkdf2, hex.EncodeToString(rfc3961.StringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected")
+		k, err := e.StringToKey(test.phrase, test.salt, rfc3961.IterationsToS2kparams(test.iterations))
+		if err != nil {
+			t.Errorf("Error in processing string to key for test %d: %v", i, err)
+		}
+		assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
+
+	}
+}

+ 14 - 139
crypto/aes/aes.go → crypto/aescts/aescts.go

@@ -1,134 +1,14 @@
-// AES Kerberos Encryption Types.
-package aes
+package aescts
 
 import (
 	"crypto/aes"
 	"crypto/cipher"
-	"crypto/hmac"
-	"encoding/binary"
-	"encoding/hex"
 	"errors"
 	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/engine"
-	"github.com/jcmturner/gokrb5/crypto/etype"
-	"golang.org/x/crypto/pbkdf2"
 )
 
-const (
-	s2kParamsZero = 4294967296
-)
-
-func s2kparamsToItertions(s2kparams string) (int, error) {
-	//process s2kparams string
-	//The parameter string is four octets indicating an unsigned
-	//number in big-endian order.  This is the number of iterations to be
-	//performed.  If the value is 00 00 00 00, the number of iterations to
-	//be performed is 4,294,967,296 (2**32).
-	var i uint32
-	if len(s2kparams) != 8 {
-		return s2kParamsZero, errors.New("Invalid s2kparams length")
-	}
-	b, err := hex.DecodeString(s2kparams)
-	if err != nil {
-		return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes")
-	}
-	i = binary.BigEndian.Uint32(b)
-	//buf := bytes.NewBuffer(b)
-	//err = binary.Read(buf, binary.BigEndian, &i)
-	if err != nil {
-		return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32")
-	}
-	return int(i), nil
-}
-
-func IterationsToS2kparams(i int) string {
-	b := make([]byte, 4, 4)
-	binary.BigEndian.PutUint32(b, uint32(i))
-	return hex.EncodeToString(b)
-}
-
-func stringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
-	i, err := s2kparamsToItertions(s2kparams)
-	if err != nil {
-		return nil, err
-	}
-	return stringToKeyIter(secret, salt, int(i), e)
-}
-
-func stringToKeySHA2(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
-	i, err := s2kparamsToItertions(s2kparams)
-	if err != nil {
-		return nil, err
-	}
-	return stringToKeySHA2Iter(secret, salt, int(i), e), nil
-}
-
-func stringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
-	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), e.GetHash())
-}
-
-func stringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
-	tkey := randomToKey(stringToPBKDF2(secret, salt, iterations, e))
-	return deriveKey(tkey, []byte("kerberos"), e)
-}
-
-func stringToKeySHA2Iter(secret, salt string, iterations int, e etype.EType) []byte {
-	tkey := randomToKey(stringToPBKDF2(secret, salt, iterations, e))
-	return deriveKeyKDF_HMAC_SHA2(tkey, []byte("kerberos"), e)
-}
-
-//https://tools.ietf.org/html/rfc8009#section-3
-func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte {
-	//k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes.
-	k := make([]byte, 4, 4)
-	binary.BigEndian.PutUint32(k, uint32(kl))
-
-	c := make([]byte, 4, 4)
-	binary.BigEndian.PutUint32(c, uint32(1))
-	c = append(c, label...)
-	c = append(c, byte(uint8(0)))
-	if len(context) > 0 {
-		c = append(c, context...)
-	}
-	c = append(c, k...)
-
-	mac := hmac.New(e.GetHash(), protocolKey)
-	mac.Write(c)
-	return mac.Sum(nil)[:(kl / 8)]
-}
-
-func deriveKeyKDF_HMAC_SHA2(protocolKey, label []byte, e etype.EType) []byte {
-	var context []byte
-	return KDF_HMAC_SHA2(protocolKey, label, context, e.GetKeySeedBitLength(), e)
-}
-
-func deriveRandomKDF_HMAC_SHA2(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
-	h := e.GetHash()()
-	return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil
-}
-
-func randomToKey(b []byte) []byte {
-	return b
-}
-
-func deriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
-	r, err := engine.DeriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e)
-	return r, err
-}
-
-func deriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
-	r, err := deriveRandom(protocolKey, usage, e)
-	if err != nil {
-		return nil, err
-	}
-	return randomToKey(r), nil
-}
-
-func encryptCTS(key, iv, message []byte, e etype.EType) ([]byte, []byte, error) {
-	if len(key) != e.GetKeyByteSize() {
-		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
-	}
-
+func EncryptCTS(key, iv, message []byte) ([]byte, []byte, error) {
 	l := len(message)
 
 	block, err := aes.NewCipher(key)
@@ -180,14 +60,10 @@ func encryptCTS(key, iv, message []byte, e etype.EType) ([]byte, []byte, error)
 	return lb, ct[:l], nil
 }
 
-func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
+func DecryptCTS(key, iv, ciphertext []byte) ([]byte, error) {
 	// Copy the cipher text as golang slices even when passed by value to this method can result in the backing arrays of the calling code value being updated.
 	ct := make([]byte, len(ciphertext))
 	copy(ct, ciphertext)
-	if len(key) != e.GetKeyByteSize() {
-		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
-
-	}
 	if len(ct) < aes.BlockSize {
 		return nil, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
 	}
@@ -197,8 +73,6 @@ func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
 		return nil, fmt.Errorf("Error creating cipher: %v", err)
 	}
 	var mode cipher.BlockMode
-	//iv full of zeros
-	ivz := make([]byte, e.GetConfounderByteSize())
 
 	//If ciphertext is multiple of blocksize we just need to swap back the last two blocks and then do CBC
 	//If the ciphertext is just one block we can't swap so we just decrypt
@@ -206,7 +80,7 @@ func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
 		if len(ct) > aes.BlockSize {
 			ct, _ = swapLastTwoBlocks(ct, aes.BlockSize)
 		}
-		mode = cipher.NewCBCDecrypter(block, ivz)
+		mode = cipher.NewCBCDecrypter(block, iv)
 		message := make([]byte, len(ct))
 		mode.CryptBlocks(message, ct)
 		return message[:len(ct)], nil
@@ -215,21 +89,22 @@ func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
 	// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
 	// Get ciphertext of the 2nd to last (penultimate) block (cpb), the last block (clb) and the rest (crb)
 	crb, cpb, clb, _ := tailBlocks(ct, aes.BlockSize)
-	iv := ivz
+	v := make([]byte, len(iv), len(iv))
+	copy(v, iv)
 	var message []byte
 	if crb != nil {
 		//If there is more than just the last and the penultimate block we decrypt it and the last bloc of this becomes the iv for later
 		rb := make([]byte, len(crb))
-		mode = cipher.NewCBCDecrypter(block, ivz)
-		iv = crb[len(crb)-aes.BlockSize:]
+		mode = cipher.NewCBCDecrypter(block, v)
+		v = crb[len(crb)-aes.BlockSize:]
 		mode.CryptBlocks(rb, crb)
 		message = append(message, rb...)
 	}
 
 	// We need to modify the cipher text
-	// Decryt the 2nd to last (penultimate) block with a zero iv
+	// Decryt the 2nd to last (penultimate) block with a the original iv
 	pb := make([]byte, aes.BlockSize)
-	mode = cipher.NewCBCDecrypter(block, ivz)
+	mode = cipher.NewCBCDecrypter(block, iv)
 	mode.CryptBlocks(pb, cpb)
 	// number of byte needed to pad
 	npb := aes.BlockSize - len(ct)%aes.BlockSize
@@ -239,13 +114,13 @@ func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
 	// Now decrypt the last block in the penultimate position (iv will be from the crb, if the is no crb it's zeros)
 	// iv for the penultimate block decrypted in the last position becomes the modified last block
 	lb := make([]byte, aes.BlockSize)
-	mode = cipher.NewCBCDecrypter(block, iv)
-	iv = clb
+	mode = cipher.NewCBCDecrypter(block, v)
+	v = clb
 	mode.CryptBlocks(lb, clb)
 	message = append(message, lb...)
 
 	// Now decrypt the penultimate block in the last position (iv will be from the modified last block)
-	mode = cipher.NewCBCDecrypter(block, iv)
+	mode = cipher.NewCBCDecrypter(block, v)
 	mode.CryptBlocks(cpb, cpb)
 	message = append(message, cpb...)
 
@@ -255,7 +130,7 @@ func decryptCTS(key, ciphertext []byte, e etype.EType) ([]byte, error) {
 
 func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
 	if len(b) <= c {
-		return nil, nil, nil, errors.New("bytes not larger than one block so cannot tail")
+		return nil, nil, nil, errors.New("bytes slice is not larger than one block so cannot tail")
 	}
 	// Get size of last block
 	var lbs int

+ 44 - 0
crypto/aescts/aescts_test.go

@@ -0,0 +1,44 @@
+package aescts
+
+import (
+	"encoding/hex"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAesCts_Encrypt_Decrypt(t *testing.T) {
+	iv := make([]byte, 16)
+	key, _ := hex.DecodeString("636869636b656e207465726979616b69")
+	var tests = []struct {
+		plain  string
+		cipher string
+		nextIV string
+	}{
+		//Test vectors from RFC 3962 Appendix B
+		{"4920776f756c64206c696b652074686520", "c6353568f2bf8cb4d8a580362da7ff7f97", "c6353568f2bf8cb4d8a580362da7ff7f"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320", "fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5", "fc00783e0efdb2c1d445d4c8eff7ed22"},
+		{"4920776f756c64206c696b65207468652047656e6572616c2047617527732043", "39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584", "39312523a78662d5be7fcbcc98ebf5a8"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c", "97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5", "b3fffd940c16a18c1b5549d2f838029e"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20", "97687268d6ecccc0c07b25e25ecfe5849dad8bbb96c4cdc03bc103e1a194bbd839312523a78662d5be7fcbcc98ebf5a8", "9dad8bbb96c4cdc03bc103e1a194bbd8"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a84807efe836ee89a526730dbc2f7bc8409dad8bbb96c4cdc03bc103e1a194bbd8", "4807efe836ee89a526730dbc2f7bc840"},
+	}
+	for i, test := range tests {
+		m, _ := hex.DecodeString(test.plain)
+		niv, c, err := EncryptCTS(key, iv, m)
+		if err != nil {
+			t.Errorf("Encryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.cipher, hex.EncodeToString(c), "Encrypted result not as expected")
+		assert.Equal(t, test.nextIV, hex.EncodeToString(niv), "Next state IV not as expected")
+	}
+	//t.Log("AES CTS Encryption tests finished")
+	for i, test := range tests {
+		b, _ := hex.DecodeString(test.cipher)
+		p, err := DecryptCTS(key, iv, b)
+		if err != nil {
+			t.Errorf("Decryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected")
+	}
+	//t.Log("AES CTS Decryption tests finished")
+}

+ 4 - 5
crypto/crypto.go

@@ -4,7 +4,6 @@ package crypto
 import (
 	"encoding/hex"
 	"fmt"
-	"github.com/jcmturner/gokrb5/crypto/aes"
 	"github.com/jcmturner/gokrb5/crypto/etype"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
@@ -15,10 +14,10 @@ import (
 func GetEtype(id int) (etype.EType, error) {
 	switch id {
 	case etypeID.AES128_CTS_HMAC_SHA1_96:
-		var et aes.Aes128CtsHmacSha96
+		var et Aes128CtsHmacSha96
 		return et, nil
 	case etypeID.AES256_CTS_HMAC_SHA1_96:
-		var et aes.Aes256CtsHmacSha96
+		var et Aes256CtsHmacSha96
 		return et, nil
 	//case etypeID.AES128_CTS_HMAC_SHA256_128:
 	//	var et aes.Aes128CtsHmacSha256128
@@ -31,10 +30,10 @@ func GetEtype(id int) (etype.EType, error) {
 func GetChksumEtype(id int) (etype.EType, error) {
 	switch id {
 	case chksumtype.HMAC_SHA1_96_AES128:
-		var et aes.Aes128CtsHmacSha96
+		var et Aes128CtsHmacSha96
 		return et, nil
 	case chksumtype.HMAC_SHA1_96_AES256:
-		var et aes.Aes256CtsHmacSha96
+		var et Aes256CtsHmacSha96
 		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported checksum type: %d", id)

+ 3 - 2
crypto/des3/des3-cbc-sha1-kd.go → crypto/des3-cbc-sha1-kd.go

@@ -1,5 +1,5 @@
 // DES3 Kerberos Encryption Types.
-package des3
+package crypto
 
 import (
 	"crypto/cipher"
@@ -9,6 +9,7 @@ import (
 	"errors"
 	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -107,7 +108,7 @@ func (e Des3CbcSha1Kd) RandomToKey(b []byte) (protocolKey []byte) {
 }
 
 func (e Des3CbcSha1Kd) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
-	r, err := engine.DeriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e)
+	r, err := rfc3961.DeriveRandom(protocolKey, usage, e)
 	return r, err
 }
 

+ 1 - 1
crypto/des3/des3-cbc-sha1-kd_test.go → crypto/des3-cbc-sha1-kd_test.go

@@ -1,4 +1,4 @@
-package des3
+package crypto
 
 import (
 	"encoding/hex"

+ 0 - 38
crypto/engine/engine.go

@@ -1,4 +1,3 @@
-// Cryptography methods for Kerberos.
 package engine
 
 import (
@@ -10,43 +9,6 @@ import (
 	"github.com/jcmturner/gokrb5/crypto/etype"
 )
 
-// RFC 3961: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
-//
-// key: base key or protocol key. Likely to be a key from a keytab file.
-//
-// usage: a constant.
-//
-// n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
-//
-// k: key length / key seed length in bits. Eg. for AES256 this value is 256.
-//
-// e: the encryption etype function to use.
-func DeriveRandom(key, usage []byte, n, k int, e etype.EType) ([]byte, error) {
-	//Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be.
-	nFoldUsage := Nfold(usage, n)
-	//k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
-	out := make([]byte, k/8)
-
-	/*If the output	of E is shorter than k bits, it is fed back into the encryption as many times as necessary.
-	The construct is as follows (where | indicates concatentation):
-
-	K1 = E(Key, n-fold(Constant), initial-cipher-state)
-	K2 = E(Key, K1, initial-cipher-state)
-	K3 = E(Key, K2, initial-cipher-state)
-	K4 = ...
-
-	DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/
-	_, K, err := e.EncryptData(key, nFoldUsage)
-	if err != nil {
-		return out, err
-	}
-	for i := copy(out, K); i < len(out); {
-		_, K, _ = e.EncryptData(key, K)
-		i = i + copy(out[i:], K)
-	}
-	return out, nil
-}
-
 // Pad bytes b with zeros to nearest multiple of message size m.
 func ZeroPad(b []byte, m int) ([]byte, error) {
 	if m <= 0 {

+ 9 - 10
crypto/etype/etype.go

@@ -1,9 +1,8 @@
-// Kerberos EType interface.
 package etype
 
 import "hash"
 
-// Interface defining the Encryption Algorithm Profile from RFC 3961.
+// Interface defining the Encryption Type.
 type EType interface {
 	GetETypeID() int
 	GetHashID() int
@@ -14,14 +13,14 @@ type EType interface {
 	RandomToKey(b []byte) []byte                                // random-to-key (bitstring[K])->(protocol-key)
 	GetHMACBitLength() int                                      // HMAC output size, h
 	GetMessageBlockByteSize() int                               // message block size, m
-	EncryptData(key, data []byte) ([]byte, []byte, error)
-	EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) // E function - encrypt (specific-key, state, octet string)->(state, octet string)
-	DecryptData(key, data []byte) ([]byte, error)
-	DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) // D function
-	GetCypherBlockBitLength() int                                        // cipher block size, c
-	GetConfounderByteSize() int                                          // This is the same as the cipher block size but in bytes.
-	DeriveKey(protocolKey, usage []byte) ([]byte, error)                 // DK key-derivation (protocol-key, integer)->(specific-key)
-	DeriveRandom(protocolKey, usage []byte) ([]byte, error)              // DR pseudo-random (protocol-key, octet-string)->(octet-string)
+	EncryptData(key, data []byte) ([]byte, []byte, error)       // E function - encrypt (specific-key, state, octet string)->(state, octet string)
+	EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error)
+	DecryptData(key, data []byte) ([]byte, error) // D function
+	DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error)
+	GetCypherBlockBitLength() int                           // cipher block size, c
+	GetConfounderByteSize() int                             // This is the same as the cipher block size but in bytes.
+	DeriveKey(protocolKey, usage []byte) ([]byte, error)    // DK key-derivation (protocol-key, integer)->(specific-key)
+	DeriveRandom(protocolKey, usage []byte) ([]byte, error) // DR pseudo-random (protocol-key, octet-string)->(octet-string)
 	VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool
 	GetHash() func() hash.Hash
 }

+ 83 - 0
crypto/rfc3961/encryption.go

@@ -0,0 +1,83 @@
+// AES Kerberos Encryption Types.
+package rfc3961
+
+import (
+	"crypto/aes"
+	"crypto/rand"
+	"errors"
+	"fmt"
+	"github.com/jcmturner/gokrb5/crypto/aescts"
+	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+)
+
+func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	ivz := make([]byte, aes.BlockSize)
+	return aescts.EncryptCTS(key, ivz, data)
+}
+
+func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	//confounder
+	c := make([]byte, e.GetConfounderByteSize())
+	_, err := rand.Read(c)
+	if err != nil {
+		return []byte{}, []byte{}, fmt.Errorf("Could not generate random confounder: %v", err)
+	}
+	plainBytes := append(c, message...)
+
+	// Derive key for encryption from usage
+	var k []byte
+	if usage != 0 {
+		k, err = e.DeriveKey(key, engine.GetUsageKe(usage))
+		if err != nil {
+			return []byte{}, []byte{}, fmt.Errorf("Error deriving key for encryption: %v", err)
+		}
+	}
+
+	// Encrypt the data
+	iv, b, err := e.EncryptData(k, plainBytes)
+	if err != nil {
+		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
+	}
+
+	// Generate and append integrity hash
+	ih, err := engine.GetIntegrityHash(plainBytes, key, usage, e)
+	if err != nil {
+		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
+	}
+	b = append(b, ih...)
+	return iv, b, nil
+}
+
+func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	ivz := make([]byte, aes.BlockSize)
+	return aescts.DecryptCTS(key, ivz, data)
+}
+
+func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
+	//Derive the key
+	k, err := e.DeriveKey(key, engine.GetUsageKe(usage))
+	if err != nil {
+		return nil, fmt.Errorf("Error deriving key: %v", err)
+	}
+	// Strip off the checksum from the end
+	b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
+	if err != nil {
+		return nil, err
+	}
+	//Verify checksum
+	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
+		return nil, errors.New("Integrity verification failed")
+	}
+	//Remove the confounder bytes
+	return b[e.GetConfounderByteSize():], nil
+}

+ 126 - 0
crypto/rfc3961/keyDerivation.go

@@ -0,0 +1,126 @@
+package rfc3961
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+	"golang.org/x/crypto/pbkdf2"
+)
+
+const (
+	s2kParamsZero = 4294967296
+	prfconstant   = "prf"
+)
+
+// RFC 3961: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
+//
+// key: base key or protocol key. Likely to be a key from a keytab file.
+//
+// usage: a constant.
+//
+// n: block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
+//
+// k: key length / key seed length in bits. Eg. for AES256 this value is 256.
+//
+// e: the encryption etype function to use.
+func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) {
+	n := e.GetCypherBlockBitLength()
+	k := e.GetKeySeedBitLength()
+	//Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be.
+	nFoldUsage := Nfold(usage, n)
+	//k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
+	out := make([]byte, k/8)
+
+	/*If the output	of E is shorter than k bits, it is fed back into the encryption as many times as necessary.
+	The construct is as follows (where | indicates concatentation):
+
+	K1 = E(Key, n-fold(Constant), initial-cipher-state)
+	K2 = E(Key, K1, initial-cipher-state)
+	K3 = E(Key, K2, initial-cipher-state)
+	K4 = ...
+
+	DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/
+	_, K, err := e.EncryptData(key, nFoldUsage)
+	if err != nil {
+		return out, err
+	}
+	for i := copy(out, K); i < len(out); {
+		_, K, _ = e.EncryptData(key, K)
+		i = i + copy(out[i:], K)
+	}
+	return out, nil
+}
+
+func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
+	r, err := DeriveRandom(protocolKey, usage, e)
+	if err != nil {
+		return nil, err
+	}
+	return RandomToKey(r), nil
+}
+
+func RandomToKey(b []byte) []byte {
+	return b
+}
+
+func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
+	i, err := S2KparamsToItertions(s2kparams)
+	if err != nil {
+		return nil, err
+	}
+	return StringToKeyIter(secret, salt, int(i), e)
+}
+
+func S2KparamsToItertions(s2kparams string) (int, error) {
+	//process s2kparams string
+	//The parameter string is four octets indicating an unsigned
+	//number in big-endian order.  This is the number of iterations to be
+	//performed.  If the value is 00 00 00 00, the number of iterations to
+	//be performed is 4,294,967,296 (2**32).
+	var i uint32
+	if len(s2kparams) != 8 {
+		return s2kParamsZero, errors.New("Invalid s2kparams length")
+	}
+	b, err := hex.DecodeString(s2kparams)
+	if err != nil {
+		return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes")
+	}
+	i = binary.BigEndian.Uint32(b)
+	//buf := bytes.NewBuffer(b)
+	//err = binary.Read(buf, binary.BigEndian, &i)
+	if err != nil {
+		return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32")
+	}
+	return int(i), nil
+}
+
+func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
+	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), e.GetHash())
+}
+
+func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
+	tkey := RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
+	return DeriveKey(tkey, []byte("kerberos"), e)
+}
+
+func IterationsToS2kparams(i int) string {
+	b := make([]byte, 4, 4)
+	binary.BigEndian.PutUint32(b, uint32(i))
+	return hex.EncodeToString(b)
+}
+
+func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) {
+	h := e.GetHash()()
+	h.Write(b)
+	tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
+	k, err := DeriveKey(key, []byte(prfconstant), e)
+	if err != nil {
+		return []byte{}, err
+	}
+	_, prf, err := EncryptData(k, tmp, e)
+	if err != nil {
+		return []byte{}, err
+	}
+	return prf, nil
+}

+ 1 - 1
crypto/engine/nfold.go → crypto/rfc3961/nfold.go

@@ -1,4 +1,4 @@
-package engine
+package rfc3961
 
 /*
 Implementation of the n-fold algorithm as defined in RFC 3961.

+ 1 - 1
crypto/engine/nfold_test.go → crypto/rfc3961/nfold_test.go

@@ -1,4 +1,4 @@
-package engine
+package rfc3961
 
 import (
 	"encoding/hex"

+ 1 - 0
crypto/rfc3962/rfc3962.go

@@ -0,0 +1 @@
+package rfc3962

+ 51 - 0
crypto/rfc8009/rfc8009.go

@@ -0,0 +1,51 @@
+package rfc8009
+
+import (
+	"crypto/hmac"
+	"encoding/binary"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
+)
+
+func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
+	h := e.GetHash()()
+	return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil
+}
+
+func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
+	i, err := rfc3961.S2KparamsToItertions(s2kparams)
+	if err != nil {
+		return nil, err
+	}
+	return stringToKeySHA2Iter(secret, salt, int(i), e), nil
+}
+
+func stringToKeySHA2Iter(secret, salt string, iterations int, e etype.EType) []byte {
+	tkey := rfc3961.RandomToKey(rfc3961.StringToPBKDF2(secret, salt, iterations, e))
+	return deriveKeyKDF_HMAC_SHA2(tkey, []byte("kerberos"), e)
+}
+
+//https://tools.ietf.org/html/rfc8009#section-3
+func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte {
+	//k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes.
+	k := make([]byte, 4, 4)
+	binary.BigEndian.PutUint32(k, uint32(kl))
+
+	c := make([]byte, 4, 4)
+	binary.BigEndian.PutUint32(c, uint32(1))
+	c = append(c, label...)
+	c = append(c, byte(uint8(0)))
+	if len(context) > 0 {
+		c = append(c, context...)
+	}
+	c = append(c, k...)
+
+	mac := hmac.New(e.GetHash(), protocolKey)
+	mac.Write(c)
+	return mac.Sum(nil)[:(kl / 8)]
+}
+
+func deriveKeyKDF_HMAC_SHA2(protocolKey, label []byte, e etype.EType) []byte {
+	var context []byte
+	return KDF_HMAC_SHA2(protocolKey, label, context, e.GetKeySeedBitLength(), e)
+}