Sfoglia il codice sorgente

refactor of crypto packages and implementation of RFC 8009

Jonathan Turner 9 anni fa
parent
commit
ca2b1a90eb
39 ha cambiato i file con 1082 aggiunte e 284 eliminazioni
  1. 6 4
      README.md
  2. 34 0
      client/client_integration_test.go
  3. 20 57
      crypto/aes128-cts-hmac-sha1-96.go
  4. 43 0
      crypto/aes128-cts-hmac-sha1-96_test.go
  5. 39 21
      crypto/aes128-cts-hmac-sha256-128.go
  6. 56 53
      crypto/aes128-cts-hmac-sha256-128_test.go
  7. 16 3
      crypto/aes256-cts-hmac-sha1-96.go
  8. 2 1
      crypto/aes256-cts-hmac-sha1-96_test.go
  9. 112 0
      crypto/aes256-cts-hmac-sha384-192.go
  10. 142 0
      crypto/aes256-cts-hmac-sha384-192_test.go
  11. 6 5
      crypto/aescts/aescts.go
  12. 2 2
      crypto/aescts/aescts_test.go
  13. 13 19
      crypto/common/common.go
  14. 15 3
      crypto/crypto.go
  15. 19 7
      crypto/des3-cbc-sha1-kd.go
  16. 4 1
      crypto/etype/etype.go
  17. 22 7
      crypto/rfc3961/encryption.go
  18. 5 37
      crypto/rfc3961/keyDerivation.go
  19. 35 0
      crypto/rfc3962/keyDerivation.go
  20. 0 1
      crypto/rfc3962/rfc3962.go
  21. 142 0
      crypto/rfc8009/encryption.go
  22. 139 0
      crypto/rfc8009/keyDerivation.go
  23. 0 51
      crypto/rfc8009/rfc8009.go
  24. 1 2
      messages/KDCRep.go
  25. 1 2
      messages/KDCReq.go
  26. 3 5
      pac/pac_type.go
  27. 21 3
      testdata/test_vectors.go
  28. 2 0
      testenv/latest-krb5kdc-vagrant/.gitignore
  29. 12 0
      testenv/latest-krb5kdc-vagrant/Vagrantfile
  30. 33 0
      testenv/latest-krb5kdc-vagrant/bootstrap.sh
  31. 4 0
      testenv/latest-krb5kdc-vagrant/kadm5.acl
  32. 77 0
      testenv/latest-krb5kdc-vagrant/kdc-setup.sh
  33. 14 0
      testenv/latest-krb5kdc-vagrant/kdc.conf
  34. BIN
      testenv/latest-krb5kdc-vagrant/krb5-1.15.1.tar.gz
  35. 32 0
      testenv/latest-krb5kdc-vagrant/krb5.conf
  36. 10 0
      testenv/latest-krb5kdc-vagrant/krb5kdc.service
  37. BIN
      testenv/testuser1.testtab
  38. BIN
      testenv/testuser2.testtab
  39. BIN
      testenv/testuser3.testtab

+ 6 - 4
README.md

@@ -15,10 +15,12 @@
 #### Implemented Encryption & Checksum Types
 The currently implemented encryption types are limited to:
 
-| Implementation | Encryption ID | Checksum ID |
-|-------|-------------|------------|
-| aes128-cts-hmac-sha1-96 | 17 | 15 |
-| aes256-cts-hmac-sha1-96 | 18 | 16 |
+| Implementation | Encryption ID | Checksum ID | RFC |
+|-------|-------------|------------|------|
+| aes128-cts-hmac-sha1-96 | 17 | 15 | 3962 |
+| aes256-cts-hmac-sha1-96 | 18 | 16 | 3962 |
+| aes128-cts-hmac-sha256-128 | 19 | 19 | 8009 |
+| aes256-cts-hmac-sha384-192 | 20 | 20 | 8009 |
 
 
 Currently the following is working/tested:

+ 34 - 0
client/client_integration_test.go

@@ -53,6 +53,40 @@ func TestClient_SuccessfulLogin_OlderKDC(t *testing.T) {
 	}
 }
 
+func TestClient_SuccessfulLogin_ETYPE_19(t *testing.T) {
+	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
+	c.LibDefaults.Default_tkt_enctypes = []string{"aes128-cts-hmac-sha256-128"}
+	c.LibDefaults.Default_tkt_enctype_ids = []int{19}
+	c.LibDefaults.Default_tgs_enctypes = []string{"aes128-cts-hmac-sha256-128"}
+	c.LibDefaults.Default_tgs_enctype_ids = []int{19}
+	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
+	cl.WithConfig(c)
+
+	err = cl.Login()
+	if err != nil {
+		t.Fatalf("Error on login: %v\n", err)
+	}
+}
+
+func TestClient_SuccessfulLogin_ETYPE_20(t *testing.T) {
+	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF_LATESTKDC)
+	c.LibDefaults.Default_tkt_enctypes = []string{"aes256-cts-hmac-sha384-192"}
+	c.LibDefaults.Default_tkt_enctype_ids = []int{20}
+	c.LibDefaults.Default_tgs_enctypes = []string{"aes256-cts-hmac-sha384-192"}
+	c.LibDefaults.Default_tgs_enctype_ids = []int{20}
+	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
+	cl.WithConfig(c)
+
+	err = cl.Login()
+	if err != nil {
+		t.Fatalf("Error on login: %v\n", err)
+	}
+}
+
 func TestClient_SuccessfulLogin_AD(t *testing.T) {
 	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
 	kt, _ := keytab.Parse(b)

+ 20 - 57
crypto/aes128-cts-hmac-sha1-96.go

@@ -2,12 +2,9 @@ package crypto
 
 import (
 	"crypto/aes"
-	"crypto/rand"
+	"crypto/hmac"
 	"crypto/sha1"
-	"errors"
-	"fmt"
-	"github.com/jcmturner/gokrb5/crypto/aescts"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
@@ -79,7 +76,7 @@ func (e Aes128CtsHmacSha96) GetKeySeedBitLength() int {
 	return e.GetKeyByteSize() * 8
 }
 
-func (e Aes128CtsHmacSha96) GetHash() func() hash.Hash {
+func (e Aes128CtsHmacSha96) GetHashFunc() func() hash.Hash {
 	return sha1.New
 }
 
@@ -112,65 +109,19 @@ func (e Aes128CtsHmacSha96) RandomToKey(b []byte) []byte {
 }
 
 func (e Aes128CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
-	ivz := make([]byte, aes.BlockSize)
-	return aescts.EncryptCTS(key, ivz, data)
+	return rfc3961.EncryptData(key, data, e)
 }
 
 func (e Aes128CtsHmacSha96) 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 Aes128CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
-	ivz := make([]byte, aes.BlockSize)
-	return aescts.DecryptCTS(key, ivz, data)
+	return rfc3961.DecryptData(key, data, e)
 }
 
 func (e Aes128CtsHmacSha96) 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 Aes128CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
@@ -182,5 +133,17 @@ func (e Aes128CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, err
 }
 
 func (e Aes128CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
-	return engine.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+	return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+}
+
+func (e Aes128CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
+	return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
+}
+
+func (e Aes128CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
+	c, err := e.GetChecksumHash(protocolKey, data, usage)
+	if err != nil {
+		return false
+	}
+	return hmac.Equal(chksum, c)
 }

+ 43 - 0
crypto/aes128-cts-hmac-sha1-96_test.go

@@ -0,0 +1,43 @@
+package crypto
+
+import (
+	"encoding/hex"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/rfc3961"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAes128CtsHmacSha196_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", "cdedb5281bb2f801565a1122b2563515", "42263c6e89f4fc28b8df68ee09799f15"},
+		{2, "password", "ATHENA.MIT.EDUraeburn", "01dbee7f4a9e243e988b62c73cda935d", "c651bf29e2300ac27fa469d693bdda13"},
+		{1200, "password", "ATHENA.MIT.EDUraeburn", "5c08eb61fdf71e4e4ec3cf6ba1f5512b", "4c01cd46d632d01e6dbe230a01ed642a"},
+		{5, "password", s, "d1daa78615f287e6a1c8b120d7062a49", "e9b23d52273747dd5c35cb55be619d8e"},
+		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase equals block size", "139c30c0966bc32ba55fdbf212530ac9", "59d1bb789a828b1aa54ef9c2883f69ed"},
+		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be61", "cb8005dc5f90179a7f02104c0018751d"},
+		{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39", "f149c1f2e154a73452d43e7fe62a56e5"},
+	}
+	var e Aes128CtsHmacSha96
+	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, common.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")
+
+	}
+}

+ 39 - 21
crypto/aes128-cts-hmac-sha256-128.go

@@ -1,11 +1,11 @@
-// +build disabled
-
 package crypto
 
 import (
 	"crypto/aes"
+	"crypto/hmac"
 	"crypto/sha256"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/rfc8009"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -32,7 +32,7 @@ func (e Aes128CtsHmacSha256128) GetKeySeedBitLength() int {
 	return e.GetKeyByteSize() * 8
 }
 
-func (e Aes128CtsHmacSha256128) GetHash() func() hash.Hash {
+func (e Aes128CtsHmacSha256128) GetHashFunc() func() hash.Hash {
 	return sha256.New
 }
 
@@ -57,38 +57,56 @@ func (e Aes128CtsHmacSha256128) GetCypherBlockBitLength() int {
 }
 
 func (e Aes128CtsHmacSha256128) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
-	saltp := e.getSaltP(salt)
-	return stringToKeySHA2(secret, saltp, s2kparams, e)
+	saltp := rfc8009.GetSaltP(salt, "aes128-cts-hmac-sha256-128")
+	return rfc8009.StringToKey(secret, saltp, s2kparams, e)
 }
 
-func (e Aes128CtsHmacSha256128) getSaltP(salt string) string {
-	b := []byte("aes128-cts-hmac-sha256-128")
-	b = append(b, byte(uint8(0)))
-	b = append(b, []byte(salt)...)
-	return string(b)
+func (e Aes128CtsHmacSha256128) RandomToKey(b []byte) []byte {
+	return rfc8009.RandomToKey(b)
 }
 
-func (e Aes128CtsHmacSha256128) RandomToKey(b []byte) []byte {
-	return randomToKey(b)
+func (e Aes128CtsHmacSha256128) EncryptData(key, data []byte) ([]byte, []byte, error) {
+	return rfc8009.EncryptData(key, data, e)
+}
+
+func (e Aes128CtsHmacSha256128) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
+	return rfc8009.EncryptMessage(key, message, usage, e)
 }
 
-func (e Aes128CtsHmacSha256128) EncryptData(key, message []byte) ([]byte, []byte, error) {
-	ivz := make([]byte, aes.BlockSize)
-	return encryptCTS(key, ivz, message, e)
+func (e Aes128CtsHmacSha256128) DecryptData(key, data []byte) ([]byte, error) {
+	return rfc8009.DecryptData(key, data, e)
 }
 
-func (e Aes128CtsHmacSha256128) DecryptData(key, ciphertext []byte) ([]byte, error) {
-	return decryptCTS(key, ciphertext, e)
+func (e Aes128CtsHmacSha256128) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
+	return rfc8009.DecryptMessage(key, ciphertext, usage, e)
 }
 
 func (e Aes128CtsHmacSha256128) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
-	return deriveKeyKDF_HMAC_SHA2(protocolKey, usage, e), nil
+	return rfc8009.DeriveKey(protocolKey, usage, e), nil
 }
 
 func (e Aes128CtsHmacSha256128) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
-	return deriveRandomKDF_HMAC_SHA2(protocolKey, usage, e)
+	return rfc8009.DeriveRandom(protocolKey, usage, e)
 }
 
+// The HMAC is calculated over the cipher state concatenated with the
+// AES output, instead of being calculated over the confounder and
+// plaintext.  This allows the message receiver to verify the
+// integrity of the message before decrypting the message.
+// Therefore the pt value to this interface method is not use. Pass any []byte.
 func (e Aes128CtsHmacSha256128) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
-	return engine.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+	// We don't need ib just there for the interface
+	return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e)
+}
+
+func (e Aes128CtsHmacSha256128) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
+	return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
+}
+
+func (e Aes128CtsHmacSha256128) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
+	c, err := e.GetChecksumHash(protocolKey, data, usage)
+	if err != nil {
+		return false
+	}
+	return hmac.Equal(chksum, c)
 }

+ 56 - 53
crypto/aes128-cts-hmac-sha256-128_test.go

@@ -1,52 +1,13 @@
-// +build disabled
-
 package crypto
 
 import (
 	"encoding/hex"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/rfc8009"
 	"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 TestAes128CtsHmacSha256128_StringToKey(t *testing.T) {
 	// Test vectors from RFC 8009 Appendix A
 	// Random 16bytes in test vector as string
@@ -63,10 +24,10 @@ func TestAes128CtsHmacSha256128_StringToKey(t *testing.T) {
 	}
 	var e Aes128CtsHmacSha256128
 	for _, test := range tests {
-		saltp := e.getSaltP(test.salt)
+		saltp := rfc8009.GetSaltP(test.salt, "aes128-cts-hmac-sha256-128")
 		assert.Equal(t, test.saltp, hex.EncodeToString(([]byte(saltp))), "SaltP not as expected")
 
-		k, _ := e.StringToKey(test.phrase, test.salt, IterationsToS2kparams(test.iterations))
+		k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2kparams(test.iterations))
 		assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
 
 	}
@@ -77,23 +38,45 @@ func TestAes128CtsHmacSha256128_DeriveKey(t *testing.T) {
 	protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
 	testUsage := uint32(2)
 	var e Aes128CtsHmacSha256128
-	k, err := e.DeriveKey(protocolBaseKey, engine.GetUsageKc(testUsage))
+	k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage))
 	if err != nil {
 		t.Fatalf("Error deriving checksum key: %v", err)
 	}
 	assert.Equal(t, "b31a018a48f54776f403e9a396325dc3", hex.EncodeToString(k), "Checksum derived key not as epxected")
-	k, err = e.DeriveKey(protocolBaseKey, engine.GetUsageKe(testUsage))
+	k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage))
 	if err != nil {
 		t.Fatalf("Error deriving encryption key: %v", err)
 	}
 	assert.Equal(t, "9b197dd1e8c5609d6e67c3e37c62c72e", hex.EncodeToString(k), "Encryption derived key not as epxected")
-	k, err = e.DeriveKey(protocolBaseKey, engine.GetUsageKi(testUsage))
+	k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage))
 	if err != nil {
 		t.Fatalf("Error deriving integrity key: %v", err)
 	}
 	assert.Equal(t, "9fda0e56ab2d85e1569a688696c26a6c", hex.EncodeToString(k), "Integrity derived key not as epxected")
 }
 
+func TestAes128CtsHmacSha256128_VerifyIntegrity(t *testing.T) {
+	// Test vectors from RFC 8009
+	protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
+	testUsage := uint32(2)
+	var e Aes128CtsHmacSha256128
+	var tests = []struct {
+		kc     string
+		pt     string
+		chksum string
+	}{
+		{"b31a018a48f54776f403e9a396325dc3", "000102030405060708090a0b0c0d0e0f1011121314", "d78367186643d67b411cba9139fc1dee"},
+	}
+	for _, test := range tests {
+		p, _ := hex.DecodeString(test.pt)
+		b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage)
+		if err != nil {
+			t.Errorf("Error generating checksum: %v", err)
+		}
+		assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected")
+	}
+}
+
 func TestAes128CtsHmacSha256128_Cypto(t *testing.T) {
 	protocolBaseKey, _ := hex.DecodeString("3705d96080c17728a0e800eab6e0d23c")
 	testUsage := uint32(2)
@@ -109,6 +92,8 @@ func TestAes128CtsHmacSha256128_Cypto(t *testing.T) {
 		// Test vectors from RFC 8009 Appendix A
 		{"", "7e5895eaf2672435bad817f545a37148", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "ef85fb890bb8472f4dab20394dca781d", "ad877eda39d50c870c0d5a0a8e48c718", "ef85fb890bb8472f4dab20394dca781dad877eda39d50c870c0d5a0a8e48c718"},
 		{"000102030405", "7bca285e2fd4130fb55b1a5c83bc5b24", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6", "877ce99e247e52d16ed4421dfdf8976c", "84d7f30754ed987bab0bf3506beb09cfb55402cef7e6877ce99e247e52d16ed4421dfdf8976c"},
+		{"000102030405060708090a0b0c0d0e0f", "56ab21713ff62c0a1457200f6fa9948f", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc2", "95fb4852e7d83e1e7c48c37eebe6b0d3", "3517d640f50ddc8ad3628722b3569d2ae07493fa8263254080ea65c1008e8fc295fb4852e7d83e1e7c48c37eebe6b0d3"},
+		{"000102030405060708090a0b0c0d0e0f1011121314", "a7a4e29a4728ce10664fb64e49ad3fac", "9b197dd1e8c5609d6e67c3e37c62c72e", "9fda0e56ab2d85e1569a688696c26a6c", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c1", "86b39a413c2f92ca9b8334a287ffcbfc", "720f73b18d9859cd6ccb4346115cd336c70f58edc0c4437c5573544c31c813bce1e6d072c186b39a413c2f92ca9b8334a287ffcbfc"},
 	}
 	var e Aes128CtsHmacSha256128
 	for i, test := range tests {
@@ -116,25 +101,43 @@ func TestAes128CtsHmacSha256128_Cypto(t *testing.T) {
 		b, _ := hex.DecodeString(test.encrypted)
 		ke, _ := hex.DecodeString(test.ke)
 		cf, _ := hex.DecodeString(test.confounder)
+		ct, _ := hex.DecodeString(test.cipher)
 		cfm := append(cf, m...)
 
-		_, c, err := e.Encrypt(ke, cfm)
+		// Test encryption to raw encrypted bytes
+		_, c, err := e.EncryptData(ke, cfm)
 		if err != nil {
 			t.Errorf("Encryption failed for test %v: %v", i+1, err)
 		}
 		assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i)
 
-		ivz := make([]byte, e.GetConfounderByteSize())
-		hm := append(ivz, b...)
-		mac, _ := engine.GetIntegrityHash(hm, protocolBaseKey, testUsage, e)
-		assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i)
-
-		p, err := e.Decrypt(ke, b)
+		// Test decryption of raw encrypted bytes
+		p, err := e.DecryptData(ke, b)
 		//Remove the confounder bytes
 		p = p[e.GetConfounderByteSize():]
 		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 - test %v", i)
+
+		// Test integrity check of complete ciphertext message
+		assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed")
+
+		// Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash)
+		_, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage)
+		if err != nil {
+			t.Errorf("Encryption to message failed for test %v: %v", i+1, err)
+		}
+		dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage)
+		if err != nil {
+			t.Errorf("Decrypting complete encrypted message failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err)
+
+		// Test the integrity hash
+		ivz := make([]byte, e.GetConfounderByteSize())
+		hm := append(ivz, b...)
+		mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e)
+		assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i)
 	}
 }

+ 16 - 3
crypto/aes256-cts-hmac-sha1-96.go

@@ -2,8 +2,9 @@ package crypto
 
 import (
 	"crypto/aes"
+	"crypto/hmac"
 	"crypto/sha1"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
@@ -75,7 +76,7 @@ func (e Aes256CtsHmacSha96) GetKeySeedBitLength() int {
 	return e.GetKeyByteSize() * 8
 }
 
-func (e Aes256CtsHmacSha96) GetHash() func() hash.Hash {
+func (e Aes256CtsHmacSha96) GetHashFunc() func() hash.Hash {
 	return sha1.New
 }
 
@@ -132,5 +133,17 @@ func (e Aes256CtsHmacSha96) DeriveRandom(protocolKey, usage []byte) ([]byte, err
 }
 
 func (e Aes256CtsHmacSha96) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
-	return engine.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+	return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+}
+
+func (e Aes256CtsHmacSha96) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
+	return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
+}
+
+func (e Aes256CtsHmacSha96) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
+	c, err := e.GetChecksumHash(protocolKey, data, usage)
+	if err != nil {
+		return false
+	}
+	return hmac.Equal(chksum, c)
 }

+ 2 - 1
crypto/aes256-cts-hmac-sha1-96_test.go

@@ -2,6 +2,7 @@ package crypto
 
 import (
 	"encoding/hex"
+	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/stretchr/testify/assert"
 	"testing"
@@ -32,7 +33,7 @@ func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
 	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))
+		k, err := e.StringToKey(test.phrase, test.salt, common.IterationsToS2kparams(test.iterations))
 		if err != nil {
 			t.Errorf("Error in processing string to key for test %d: %v", i, err)
 		}

+ 112 - 0
crypto/aes256-cts-hmac-sha384-192.go

@@ -0,0 +1,112 @@
+package crypto
+
+import (
+	"crypto/aes"
+	"crypto/hmac"
+	"crypto/sha512"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/rfc8009"
+	"github.com/jcmturner/gokrb5/iana/chksumtype"
+	"github.com/jcmturner/gokrb5/iana/etypeID"
+	"hash"
+)
+
+// RFC https://tools.ietf.org/html/rfc8009
+
+type Aes256CtsHmacSha384192 struct {
+}
+
+func (e Aes256CtsHmacSha384192) GetETypeID() int {
+	return etypeID.AES256_CTS_HMAC_SHA384_192
+}
+
+func (e Aes256CtsHmacSha384192) GetHashID() int {
+	return chksumtype.HMAC_SHA384_192_AES256
+}
+
+func (e Aes256CtsHmacSha384192) GetKeyByteSize() int {
+	return 192 / 8
+}
+
+func (e Aes256CtsHmacSha384192) GetKeySeedBitLength() int {
+	return e.GetKeyByteSize() * 8
+}
+
+func (e Aes256CtsHmacSha384192) GetHashFunc() func() hash.Hash {
+	return sha512.New384
+}
+
+func (e Aes256CtsHmacSha384192) GetMessageBlockByteSize() int {
+	return 1
+}
+
+func (e Aes256CtsHmacSha384192) GetDefaultStringToKeyParams() string {
+	return "00008000"
+}
+
+func (e Aes256CtsHmacSha384192) GetConfounderByteSize() int {
+	return aes.BlockSize
+}
+
+func (e Aes256CtsHmacSha384192) GetHMACBitLength() int {
+	return 192
+}
+
+func (e Aes256CtsHmacSha384192) GetCypherBlockBitLength() int {
+	return aes.BlockSize * 8
+}
+
+func (e Aes256CtsHmacSha384192) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
+	saltp := rfc8009.GetSaltP(salt, "aes256-cts-hmac-sha384-192")
+	return rfc8009.StringToKey(secret, saltp, s2kparams, e)
+}
+
+func (e Aes256CtsHmacSha384192) RandomToKey(b []byte) []byte {
+	return rfc8009.RandomToKey(b)
+}
+
+func (e Aes256CtsHmacSha384192) EncryptData(key, data []byte) ([]byte, []byte, error) {
+	return rfc8009.EncryptData(key, data, e)
+}
+
+func (e Aes256CtsHmacSha384192) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
+	return rfc8009.EncryptMessage(key, message, usage, e)
+}
+
+func (e Aes256CtsHmacSha384192) DecryptData(key, data []byte) ([]byte, error) {
+	return rfc8009.DecryptData(key, data, e)
+}
+
+func (e Aes256CtsHmacSha384192) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
+	return rfc8009.DecryptMessage(key, ciphertext, usage, e)
+}
+
+func (e Aes256CtsHmacSha384192) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
+	return rfc8009.DeriveKey(protocolKey, usage, e), nil
+}
+
+func (e Aes256CtsHmacSha384192) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
+	return rfc8009.DeriveRandom(protocolKey, usage, e)
+}
+
+// The HMAC is calculated over the cipher state concatenated with the
+// AES output, instead of being calculated over the confounder and
+// plaintext.  This allows the message receiver to verify the
+// integrity of the message before decrypting the message.
+// Therefore the pt value to this interface method is not use. Pass any []byte.
+func (e Aes256CtsHmacSha384192) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
+	// We don't need ib just there for the interface
+	return rfc8009.VerifyIntegrity(protocolKey, ct, usage, e)
+}
+
+func (e Aes256CtsHmacSha384192) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
+	return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
+}
+
+func (e Aes256CtsHmacSha384192) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
+	c, err := e.GetChecksumHash(protocolKey, data, usage)
+	if err != nil {
+		return false
+	}
+	return hmac.Equal(chksum, c)
+}

+ 142 - 0
crypto/aes256-cts-hmac-sha384-192_test.go

@@ -0,0 +1,142 @@
+package crypto
+
+import (
+	"encoding/hex"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/rfc8009"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAes256CtsHmacSha384192_StringToKey(t *testing.T) {
+	// Test vectors from RFC 8009 Appendix A
+	// Random 16bytes in test vector as string
+	r, _ := hex.DecodeString("10DF9DD783E5BC8ACEA1730E74355F61")
+	s := string(r)
+	var tests = []struct {
+		iterations int
+		phrase     string
+		salt       string
+		saltp      string
+		key        string
+	}{
+		{32768, "password", s + "ATHENA.MIT.EDUraeburn", "6165733235362d6374732d686d61632d7368613338342d3139320010df9dd783e5bc8acea1730e74355f61415448454e412e4d49542e4544557261656275726e", "45bd806dbf6a833a9cffc1c94589a222367a79bc21c413718906e9f578a78467"},
+	}
+	var e Aes256CtsHmacSha384192
+	for _, test := range tests {
+		saltp := rfc8009.GetSaltP(test.salt, "aes256-cts-hmac-sha384-192")
+		assert.Equal(t, test.saltp, hex.EncodeToString(([]byte(saltp))), "SaltP not as expected")
+
+		k, _ := e.StringToKey(test.phrase, test.salt, common.IterationsToS2kparams(test.iterations))
+		assert.Equal(t, test.key, hex.EncodeToString(k), "String to Key not as expected")
+	}
+}
+
+func TestAes256CtsHmacSha384192_DeriveKey(t *testing.T) {
+	// Test vectors from RFC 8009 Appendix A
+	protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
+	testUsage := uint32(2)
+	var e Aes256CtsHmacSha384192
+	k, err := e.DeriveKey(protocolBaseKey, common.GetUsageKc(testUsage))
+	if err != nil {
+		t.Fatalf("Error deriving checksum key: %v", err)
+	}
+	assert.Equal(t, "ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", hex.EncodeToString(k), "Checksum derived key not as epxected")
+	k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKe(testUsage))
+	if err != nil {
+		t.Fatalf("Error deriving encryption key: %v", err)
+	}
+	assert.Equal(t, "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", hex.EncodeToString(k), "Encryption derived key not as epxected")
+	k, err = e.DeriveKey(protocolBaseKey, common.GetUsageKi(testUsage))
+	if err != nil {
+		t.Fatalf("Error deriving integrity key: %v", err)
+	}
+	assert.Equal(t, "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", hex.EncodeToString(k), "Integrity derived key not as epxected")
+}
+
+func TestAes256CtsHmacSha384192_Cypto(t *testing.T) {
+	protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
+	testUsage := uint32(2)
+	var tests = []struct {
+		plain      string
+		confounder string
+		ke         string
+		ki         string
+		encrypted  string // AESOutput
+		hash       string // TruncatedHMACOutput
+		cipher     string // Ciphertext(AESOutput|HMACOutput)
+	}{
+		// Test vectors from RFC 8009 Appendix A
+		{"", "f764e9fa15c276478b2c7d0c4e5f58e4", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "41f53fa5bfe7026d91faf9be959195a0", "58707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c", "41f53fa5bfe7026d91faf9be959195a058707273a96a40f0a01960621ac612748b9bbfbe7eb4ce3c"},
+		{"000102030405", "b80d3251c1f6471494256ffe712d0b9a", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9", "f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2", "4ed7b37c2bcac8f74f23c1cf07e62bc7b75fb3f637b9f559c7f664f69eab7b6092237526ea0d1f61cb20d69d10f2"},
+		{"000102030405060708090a0b0c0d0e0f", "53bf8a0d105265d4e276428624ce5e63", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f6", "8cf51f14d798c2273f35df574d1f932e40c4ff255b36a266", "bc47ffec7998eb91e8115cf8d19dac4bbbe2e163e87dd37f49beca92027764f68cf51f14d798c2273f35df574d1f932e40c4ff255b36a266"},
+		{"000102030405060708090a0b0c0d0e0f1011121314", "763e65367e864f02f55153c7e3b58af1", "56ab22bee63d82d7bc5227f6773f8ea7a5eb1c825160c38312980c442e5c7e49", "69b16514e3cd8e56b82010d5c73012b622c4d00ffc23ed1f", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86", "fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62", "40013e2df58e8751957d2878bcd2d6fe101ccfd556cb1eae79db3c3ee86429f2b2a602ac86fef6ecb647d6295fae077a1feb517508d2c16b4192e01f62"},
+	}
+	var e Aes256CtsHmacSha384192
+	for i, test := range tests {
+		m, _ := hex.DecodeString(test.plain)
+		b, _ := hex.DecodeString(test.encrypted)
+		ke, _ := hex.DecodeString(test.ke)
+		cf, _ := hex.DecodeString(test.confounder)
+		ct, _ := hex.DecodeString(test.cipher)
+		cfm := append(cf, m...)
+
+		// Test encryption to raw encrypted bytes
+		_, c, err := e.EncryptData(ke, cfm)
+		if err != nil {
+			t.Errorf("Encryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.encrypted, hex.EncodeToString(c), "Encrypted result not as expected - test %v", i)
+
+		// Test decryption of raw encrypted bytes
+		p, err := e.DecryptData(ke, b)
+		//Remove the confounder bytes
+		p = p[e.GetConfounderByteSize():]
+		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 - test %v", i)
+
+		// Test integrity check of complete ciphertext message
+		assert.True(t, e.VerifyIntegrity(protocolBaseKey, ct, ct, testUsage), "Integrity check of cipher text failed")
+
+		// Test encrypting and decrypting a complete cipertext message (with confounder, integrity hash)
+		_, cm, err := e.EncryptMessage(protocolBaseKey, m, testUsage)
+		if err != nil {
+			t.Errorf("Encryption to message failed for test %v: %v", i+1, err)
+		}
+		dm, err := e.DecryptMessage(protocolBaseKey, cm, testUsage)
+		if err != nil {
+			t.Errorf("Decrypting complete encrypted message failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, m, dm, "Message not as expected after encrypting and decrypting for test %v: %v", i+1, err)
+
+		// Test the integrity hash
+		ivz := make([]byte, e.GetConfounderByteSize())
+		hm := append(ivz, b...)
+		mac, _ := common.GetIntegrityHash(hm, protocolBaseKey, testUsage, e)
+		assert.Equal(t, test.hash, hex.EncodeToString(mac), "HMAC result not as expected - test %v", i)
+	}
+}
+
+func TestAes256CtsHmacSha384192_VerifyIntegrity(t *testing.T) {
+	// Test vectors from RFC 8009
+	protocolBaseKey, _ := hex.DecodeString("6d404d37faf79f9df0d33568d320669800eb4836472ea8a026d16b7182460c52")
+	testUsage := uint32(2)
+	var e Aes256CtsHmacSha384192
+	var tests = []struct {
+		kc     string
+		pt     string
+		chksum string
+	}{
+		{"ef5718be86cc84963d8bbb5031e9f5c4ba41f28faf69e73d", "000102030405060708090a0b0c0d0e0f1011121314", "45ee791567eefca37f4ac1e0222de80d43c3bfa06699672a"},
+	}
+	for _, test := range tests {
+		p, _ := hex.DecodeString(test.pt)
+		b, err := e.GetChecksumHash(protocolBaseKey, p, testUsage)
+		if err != nil {
+			t.Errorf("Error generating checksum: %v", err)
+		}
+		assert.Equal(t, test.chksum, hex.EncodeToString(b), "Checksum not as expected")
+	}
+}

+ 6 - 5
crypto/aescts/aescts.go

@@ -1,3 +1,4 @@
+// AES CipherText Stealing encryption and decryption methods
 package aescts
 
 import (
@@ -5,10 +6,10 @@ import (
 	"crypto/cipher"
 	"errors"
 	"fmt"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
 )
 
-func EncryptCTS(key, iv, message []byte) ([]byte, []byte, error) {
+func Encrypt(key, iv, message []byte) ([]byte, []byte, error) {
 	l := len(message)
 
 	block, err := aes.NewCipher(key)
@@ -29,7 +30,7 @@ func EncryptCTS(key, iv, message []byte) ([]byte, []byte, error) {
 	subsequent encryption is the next-to-last block of the encryption
 	output; this is the encrypted form of the last plaintext block.*/
 	if l <= aes.BlockSize {
-		m, _ = engine.ZeroPad(m, aes.BlockSize)
+		m, _ = common.ZeroPad(m, aes.BlockSize)
 		mode.CryptBlocks(m, m)
 		return m, m, nil
 	}
@@ -39,7 +40,7 @@ func EncryptCTS(key, iv, message []byte) ([]byte, []byte, error) {
 		rb, _ := swapLastTwoBlocks(m, aes.BlockSize)
 		return iv, rb, nil
 	}
-	m, _ = engine.ZeroPad(m, aes.BlockSize)
+	m, _ = common.ZeroPad(m, aes.BlockSize)
 	rb, pb, lb, err := tailBlocks(m, aes.BlockSize)
 	var ct []byte
 	if rb != nil {
@@ -60,7 +61,7 @@ func EncryptCTS(key, iv, message []byte) ([]byte, []byte, error) {
 	return lb, ct[:l], nil
 }
 
-func DecryptCTS(key, iv, ciphertext []byte) ([]byte, error) {
+func Decrypt(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)

+ 2 - 2
crypto/aescts/aescts_test.go

@@ -24,7 +24,7 @@ func TestAesCts_Encrypt_Decrypt(t *testing.T) {
 	}
 	for i, test := range tests {
 		m, _ := hex.DecodeString(test.plain)
-		niv, c, err := EncryptCTS(key, iv, m)
+		niv, c, err := Encrypt(key, iv, m)
 		if err != nil {
 			t.Errorf("Encryption failed for test %v: %v", i+1, err)
 		}
@@ -34,7 +34,7 @@ func TestAesCts_Encrypt_Decrypt(t *testing.T) {
 	//t.Log("AES CTS Encryption tests finished")
 	for i, test := range tests {
 		b, _ := hex.DecodeString(test.cipher)
-		p, err := DecryptCTS(key, iv, b)
+		p, err := Decrypt(key, iv, b)
 		if err != nil {
 			t.Errorf("Decryption failed for test %v: %v", i+1, err)
 		}

+ 13 - 19
crypto/engine/engine.go → crypto/common/common.go

@@ -1,9 +1,11 @@
-package engine
+// Encryption methods common across encryption types
+package common
 
 import (
 	"bytes"
 	"crypto/hmac"
 	"encoding/binary"
+	"encoding/hex"
 	"errors"
 	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/etype"
@@ -64,12 +66,12 @@ func PKCS7Unpad(b []byte, m int) ([]byte, error) {
 	return b[:len(b)-n], nil
 }
 
-func getHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) {
+func GetHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) {
 	k, err := etype.DeriveKey(key, usage)
 	if err != nil {
 		return nil, fmt.Errorf("Unable to derive key for checksum: %v", err)
 	}
-	mac := hmac.New(etype.GetHash(), k)
+	mac := hmac.New(etype.GetHashFunc(), k)
 	p := make([]byte, len(pt))
 	copy(p, pt)
 	mac.Write(p)
@@ -78,26 +80,12 @@ func getHash(pt, key []byte, usage []byte, etype etype.EType) ([]byte, error) {
 
 // Get a keyed checksum hash of bytes b.
 func GetChecksumHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) {
-	return getHash(b, key, GetUsageKc(usage), etype)
+	return GetHash(b, key, GetUsageKc(usage), etype)
 }
 
 // Get a keyed integrity hash of bytes b.
 func GetIntegrityHash(b, key []byte, usage uint32, etype etype.EType) ([]byte, error) {
-	return getHash(b, key, GetUsageKi(usage), etype)
-}
-
-// Verify the integrity of cipertext bytes ct.
-func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool {
-	//The ciphertext output is the concatenation of the output of the basic
-	//encryption function E and a (possibly truncated) HMAC using the
-	//specified hash function H, both applied to the plaintext with a
-	//random confounder prefix and sufficient padding to bring it to a
-	//multiple of the message block size.  When the HMAC is computed, the
-	//key is used in the protocol key form.
-	h := make([]byte, etype.GetHMACBitLength()/8)
-	copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
-	expectedMAC, _ := GetIntegrityHash(pt, key, usage, etype)
-	return hmac.Equal(h, expectedMAC)
+	return GetHash(b, key, GetUsageKi(usage), etype)
 }
 
 // Verify the checksum of the msg bytes is the same as the checksum provided.
@@ -144,3 +132,9 @@ func getUsage(un uint32, o byte) []byte {
 	binary.Write(&buf, binary.BigEndian, un)
 	return append(buf.Bytes(), o)
 }
+
+func IterationsToS2kparams(i int) string {
+	b := make([]byte, 4, 4)
+	binary.BigEndian.PutUint32(b, uint32(i))
+	return hex.EncodeToString(b)
+}

+ 15 - 3
crypto/crypto.go

@@ -19,9 +19,12 @@ func GetEtype(id int) (etype.EType, error) {
 	case etypeID.AES256_CTS_HMAC_SHA1_96:
 		var et Aes256CtsHmacSha96
 		return et, nil
-	//case etypeID.AES128_CTS_HMAC_SHA256_128:
-	//	var et aes.Aes128CtsHmacSha256128
-	//	return et, nil
+	case etypeID.AES128_CTS_HMAC_SHA256_128:
+		var et Aes128CtsHmacSha256128
+		return et, nil
+	case etypeID.AES256_CTS_HMAC_SHA384_192:
+		var et Aes256CtsHmacSha384192
+		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported EType: %d", id)
 	}
@@ -35,6 +38,12 @@ func GetChksumEtype(id int) (etype.EType, error) {
 	case chksumtype.HMAC_SHA1_96_AES256:
 		var et Aes256CtsHmacSha96
 		return et, nil
+	case chksumtype.HMAC_SHA256_128_AES128:
+		var et Aes128CtsHmacSha256128
+		return et, nil
+	case chksumtype.HMAC_SHA384_192_AES256:
+		var et Aes256CtsHmacSha384192
+		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported checksum type: %d", id)
 	}
@@ -133,6 +142,9 @@ func DecryptEncPart(ed types.EncryptedData, key types.EncryptionKey, usage uint3
 
 func DecryptMessage(ciphertext []byte, key types.EncryptionKey, usage uint32) ([]byte, error) {
 	et, err := GetEtype(key.KeyType)
+	if err != nil {
+		return []byte{}, fmt.Errorf("Error decrypting: %v", err)
+	}
 	b, err := et.DecryptMessage(key.KeyValue, ciphertext, usage)
 	if err != nil {
 		return nil, fmt.Errorf("Error decrypting: %v", err)

+ 19 - 7
crypto/des3-cbc-sha1-kd.go

@@ -1,14 +1,14 @@
-// DES3 Kerberos Encryption Types.
 package crypto
 
 import (
 	"crypto/cipher"
 	"crypto/des"
+	"crypto/hmac"
 	"crypto/rand"
 	"crypto/sha1"
 	"errors"
 	"fmt"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
@@ -71,7 +71,7 @@ func (e Des3CbcSha1Kd) GetKeySeedBitLength() int {
 	return 21 * 8
 }
 
-func (e Des3CbcSha1Kd) GetHash() func() hash.Hash {
+func (e Des3CbcSha1Kd) GetHashFunc() func() hash.Hash {
 	return sha1.New
 }
 
@@ -90,7 +90,7 @@ func (e Des3CbcSha1Kd) GetConfounderByteSize() int {
 }
 
 func (e Des3CbcSha1Kd) GetHMACBitLength() int {
-	return e.GetHash()().Size()
+	return e.GetHashFunc()().Size()
 }
 
 func (e Des3CbcSha1Kd) GetCypherBlockBitLength() int {
@@ -125,7 +125,7 @@ func (e Des3CbcSha1Kd) EncryptData(key, data []byte) ([]byte, []byte, error) {
 		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
 
 	}
-	data, _ = engine.ZeroPad(data, e.GetMessageBlockByteSize())
+	data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize())
 
 	block, err := des.NewTripleDESCipher(key)
 	if err != nil {
@@ -156,7 +156,7 @@ func (e Des3CbcSha1Kd) EncryptMessage(key, message []byte, usage uint32) ([]byte
 	}
 
 	// Generate and append integrity hash
-	ih, err := engine.GetIntegrityHash(plainBytes, key, usage, e)
+	ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
 	if err != nil {
 		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
 	}
@@ -198,5 +198,17 @@ func (e Des3CbcSha1Kd) DecryptMessage(key, ciphertext []byte, usage uint32) (mes
 }
 
 func (e Des3CbcSha1Kd) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {
-	return engine.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+	return rfc3961.VerifyIntegrity(protocolKey, ct, pt, usage, e)
+}
+
+func (e Des3CbcSha1Kd) GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error) {
+	return common.GetHash(data, protocolKey, common.GetUsageKc(usage), e)
+}
+
+func (e Des3CbcSha1Kd) VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool {
+	c, err := e.GetChecksumHash(protocolKey, data, usage)
+	if err != nil {
+		return false
+	}
+	return hmac.Equal(chksum, c)
 }

+ 4 - 1
crypto/etype/etype.go

@@ -1,3 +1,4 @@
+// The Kerberos Encryption Type interface
 package etype
 
 import "hash"
@@ -22,5 +23,7 @@ type EType interface {
 	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
+	GetChecksumHash(protocolKey, data []byte, usage uint32) ([]byte, error)
+	VerifyChecksum(protocolKey, data, chksum []byte, usage uint32) bool
+	GetHashFunc() func() hash.Hash
 }

+ 22 - 7
crypto/rfc3961/encryption.go

@@ -1,13 +1,14 @@
-// AES Kerberos Encryption Types.
+// Encryption and checksum methods as specified in RFC 3961
 package rfc3961
 
 import (
 	"crypto/aes"
+	"crypto/hmac"
 	"crypto/rand"
 	"errors"
 	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/aescts"
-	"github.com/jcmturner/gokrb5/crypto/engine"
+	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/etype"
 )
 
@@ -16,7 +17,7 @@ func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
 		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)
+	return aescts.Encrypt(key, ivz, data)
 }
 
 func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
@@ -34,7 +35,7 @@ func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, [
 	// Derive key for encryption from usage
 	var k []byte
 	if usage != 0 {
-		k, err = e.DeriveKey(key, engine.GetUsageKe(usage))
+		k, err = e.DeriveKey(key, common.GetUsageKe(usage))
 		if err != nil {
 			return []byte{}, []byte{}, fmt.Errorf("Error deriving key for encryption: %v", err)
 		}
@@ -47,7 +48,7 @@ func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, [
 	}
 
 	// Generate and append integrity hash
-	ih, err := engine.GetIntegrityHash(plainBytes, key, usage, e)
+	ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
 	if err != nil {
 		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
 	}
@@ -60,12 +61,12 @@ func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
 		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)
+	return aescts.Decrypt(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))
+	k, err := e.DeriveKey(key, common.GetUsageKe(usage))
 	if err != nil {
 		return nil, fmt.Errorf("Error deriving key: %v", err)
 	}
@@ -81,3 +82,17 @@ func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte
 	//Remove the confounder bytes
 	return b[e.GetConfounderByteSize():], nil
 }
+
+// Verify the integrity of cipertext bytes ct.
+func VerifyIntegrity(key, ct, pt []byte, usage uint32, etype etype.EType) bool {
+	//The ciphertext output is the concatenation of the output of the basic
+	//encryption function E and a (possibly truncated) HMAC using the
+	//specified hash function H, both applied to the plaintext with a
+	//random confounder prefix and sufficient padding to bring it to a
+	//multiple of the message block size.  When the HMAC is computed, the
+	//key is used in the protocol key form.
+	h := make([]byte, etype.GetHMACBitLength()/8)
+	copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
+	expectedMAC, _ := common.GetIntegrityHash(pt, key, usage, etype)
+	return hmac.Equal(h, expectedMAC)
+}

+ 5 - 37
crypto/rfc3961/keyDerivation.go

@@ -1,16 +1,13 @@
 package rfc3961
 
 import (
-	"encoding/binary"
-	"encoding/hex"
-	"errors"
 	"github.com/jcmturner/gokrb5/crypto/etype"
+	"github.com/jcmturner/gokrb5/crypto/rfc3962"
 	"golang.org/x/crypto/pbkdf2"
 )
 
 const (
-	s2kParamsZero = 4294967296
-	prfconstant   = "prf"
+	prfconstant = "prf"
 )
 
 // RFC 3961: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
@@ -65,38 +62,15 @@ func RandomToKey(b []byte) []byte {
 }
 
 func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
-	i, err := S2KparamsToItertions(s2kparams)
+	i, err := rfc3962.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())
+	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), e.GetHashFunc())
 }
 
 func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
@@ -104,14 +78,8 @@ func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte
 	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 := e.GetHashFunc()()
 	h.Write(b)
 	tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
 	k, err := DeriveKey(key, []byte(prfconstant), e)

+ 35 - 0
crypto/rfc3962/keyDerivation.go

@@ -0,0 +1,35 @@
+// Encryption and checksum methods as specified in RFC 3962
+package rfc3962
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+)
+
+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
+}

+ 0 - 1
crypto/rfc3962/rfc3962.go

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

+ 142 - 0
crypto/rfc8009/encryption.go

@@ -0,0 +1,142 @@
+// Encryption and checksum methods as specified in RFC 8009
+package rfc8009
+
+import (
+	"crypto/aes"
+	"crypto/hmac"
+	"crypto/rand"
+	"errors"
+	"fmt"
+	"github.com/jcmturner/gokrb5/crypto/aescts"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+	"github.com/jcmturner/gokrb5/iana/etypeID"
+)
+
+func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
+	kl := e.GetKeyByteSize()
+	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
+		kl = 32
+	}
+	if len(key) != kl {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	ivz := make([]byte, aes.BlockSize)
+	return aescts.Encrypt(key, ivz, data)
+}
+
+//encryption function: as follows, where E() is AES encryption in
+//CBC-CS3 mode, and h is the size of truncated HMAC (128 bits or 192
+//bits as described above).
+//
+//N = random value of length 128 bits (the AES block size)
+//IV = cipher state
+//C = E(Ke, N | plaintext, IV)
+//H = HMAC(Ki, IV | C)
+//ciphertext = C | H[1..h]
+//
+//Steps to compute the 128-bit cipher state:
+//L = length of C in bits
+//portion C into 128-bit blocks, placing any remainder of less
+//than 128 bits into a final block
+//if L == 128: cipher state = C
+//else if L mod 128 > 0: cipher state = last full (128-bit) block
+//of C (the next-to-last
+//block)
+//else if L mod 128 == 0: cipher state = next-to-last block of C
+//
+//(Note that L will never be less than 128 because of the
+//presence of N in the encryption input.)
+func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
+	kl := e.GetKeyByteSize()
+	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
+		kl = 32
+	}
+	if len(key) != kl {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", kl, len(key))
+	}
+	if len(key) != e.GetKeyByteSize() {
+	}
+	//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, common.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)
+	}
+
+	ivz := make([]byte, e.GetConfounderByteSize())
+	ih, err := GetIntegityHash(ivz, b, 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) {
+	kl := e.GetKeyByteSize()
+	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
+		kl = 32
+	}
+	if len(key) != kl {
+		return []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", kl, len(key))
+	}
+	ivz := make([]byte, aes.BlockSize)
+	return aescts.Decrypt(key, ivz, data)
+}
+
+func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
+	//Derive the key
+	k, err := e.DeriveKey(key, common.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
+}
+
+func GetIntegityHash(iv, c, key []byte, usage uint32, e etype.EType) ([]byte, error) {
+	// Generate and append integrity hash
+	// The HMAC is calculated over the cipher state concatenated with the
+	// AES output, instead of being calculated over the confounder and
+	// plaintext.  This allows the message receiver to verify the
+	// integrity of the message before decrypting the message.
+	// H = HMAC(Ki, IV | C)
+	ib := append(iv, c...)
+	return common.GetIntegrityHash(ib, key, usage, e)
+}
+
+// Verify the integrity of cipertext bytes ct.
+func VerifyIntegrity(key, ct []byte, usage uint32, etype etype.EType) bool {
+	h := make([]byte, etype.GetHMACBitLength()/8)
+	copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
+	ivz := make([]byte, etype.GetConfounderByteSize())
+	ib := append(ivz, ct[:len(ct)-(etype.GetHMACBitLength()/8)]...)
+	expectedMAC, _ := common.GetIntegrityHash(ib, key, usage, etype)
+	return hmac.Equal(h, expectedMAC)
+}

+ 139 - 0
crypto/rfc8009/keyDerivation.go

@@ -0,0 +1,139 @@
+package rfc8009
+
+import (
+	"crypto/hmac"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+	"github.com/jcmturner/gokrb5/iana/etypeID"
+	"golang.org/x/crypto/pbkdf2"
+)
+
+const (
+	s2kParamsZero = 32768
+)
+
+func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
+	h := e.GetHashFunc()()
+	return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil
+}
+
+// https://tools.ietf.org/html/rfc8009#section-5
+//
+// If the enctype is aes128-cts-hmac-sha256-128:
+// Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 128)
+// Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 128)
+// Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 128)
+//
+// If the enctype is aes256-cts-hmac-sha384-192:
+// Kc = KDF-HMAC-SHA2(base-key, usage | 0x99, 192)
+// Ke = KDF-HMAC-SHA2(base-key, usage | 0xAA, 256)
+// Ki = KDF-HMAC-SHA2(base-key, usage | 0x55, 192)
+func DeriveKey(protocolKey, label []byte, e etype.EType) []byte {
+	var context []byte
+	var kl int
+	// Key length is longer for aes256-cts-hmac-sha384-192 is it is a Ke or from StringToKey (where label is "kerberos")
+	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
+		switch label[len(label)-1] {
+		case 0x73:
+			// 0x73 is "s" so label could be kerberos meaning StringToKey so now check if the label is "kerberos"
+			kerblabel := []byte("kerberos")
+			if len(label) != len(kerblabel) {
+				break
+			}
+			for i, b := range label {
+				if b != kerblabel[i] {
+					kl = e.GetKeySeedBitLength()
+					break
+				}
+			}
+			if kl == 0 {
+				// This is StringToKey
+				kl = 256
+			}
+		case 0xAA:
+			// This is a Ke
+			kl = 256
+		}
+	}
+	if kl == 0 {
+		kl = e.GetKeySeedBitLength()
+	}
+	return RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e))
+}
+
+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), nil
+}
+
+func StringToKeyIter(secret, salt string, iterations int, e etype.EType) []byte {
+	tkey := RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
+	return DeriveKey(tkey, []byte("kerberos"), e)
+}
+
+func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
+	kl := e.GetKeyByteSize()
+	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
+		kl = 32
+	}
+	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, kl, e.GetHashFunc())
+}
+
+// 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.GetHashFunc(), protocolKey)
+	mac.Write(c)
+	return mac.Sum(nil)[:(kl / 8)]
+}
+
+func GetSaltP(salt, ename string) string {
+	b := []byte(ename)
+	b = append(b, byte(uint8(0)))
+	b = append(b, []byte(salt)...)
+	return string(b)
+}
+
+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
+}

+ 0 - 51
crypto/rfc8009/rfc8009.go

@@ -1,51 +0,0 @@
-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)
-}

+ 1 - 2
messages/KDCRep.go

@@ -9,7 +9,6 @@ import (
 	"github.com/jcmturner/gokrb5/config"
 	"github.com/jcmturner/gokrb5/credentials"
 	"github.com/jcmturner/gokrb5/crypto"
-	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
 	"github.com/jcmturner/gokrb5/iana/flags"
 	"github.com/jcmturner/gokrb5/iana/keyusage"
@@ -239,7 +238,7 @@ func (k *ASRep) IsValid(cfg *config.Config, creds *credentials.Credentials, asRe
 					return false, krberror.Errorf(err, krberror.CHKSUM_ERROR, "KDC FAST negotiation response error")
 				}
 				ab, _ := asReq.Marshal()
-				if !engine.VerifyChecksum(key.KeyValue, pafast.Chksum, ab, keyusage.KEY_USAGE_AS_REQ, etype) {
+				if !etype.VerifyChecksum(key.KeyValue, ab, pafast.Chksum, keyusage.KEY_USAGE_AS_REQ) {
 					return false, krberror.Errorf(err, krberror.CHKSUM_ERROR, "KDC FAST negotiation response checksum invalid")
 				}
 			}

+ 1 - 2
messages/KDCReq.go

@@ -9,7 +9,6 @@ import (
 	"github.com/jcmturner/gokrb5/asn1tools"
 	"github.com/jcmturner/gokrb5/config"
 	"github.com/jcmturner/gokrb5/crypto"
-	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana"
 	"github.com/jcmturner/gokrb5/iana/asnAppTag"
 	"github.com/jcmturner/gokrb5/iana/flags"
@@ -172,7 +171,7 @@ func NewTGSReq(cname types.PrincipalName, c *config.Config, tkt Ticket, sessionK
 	if err != nil {
 		return a, krberror.Errorf(err, krberror.ENCRYPTING_ERROR, "Error getting etype to encrypt authenticator")
 	}
-	cb, err := engine.GetChecksumHash(b, sessionKey.KeyValue, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM, etype)
+	cb, err := etype.GetChecksumHash(sessionKey.KeyValue, b, keyusage.TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM)
 	auth.Cksum = types.Checksum{
 		CksumType: etype.GetHashID(),
 		Checksum:  cb,

+ 3 - 5
pac/pac_type.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"fmt"
 	"github.com/jcmturner/gokrb5/crypto"
-	"github.com/jcmturner/gokrb5/crypto/engine"
 	"github.com/jcmturner/gokrb5/iana/keyusage"
 	"github.com/jcmturner/gokrb5/ndr"
 	"github.com/jcmturner/gokrb5/types"
@@ -192,11 +191,10 @@ func (pac *PACType) validate(key types.EncryptionKey) (bool, error) {
 	if err != nil {
 		return false, err
 	}
-	if ok := engine.VerifyChecksum(key.KeyValue,
-		pac.ServerChecksum.Signature,
+	if ok := etype.VerifyChecksum(key.KeyValue,
 		pac.ZeroSigData,
-		keyusage.KERB_NON_KERB_CKSUM_SALT,
-		etype); !ok {
+		pac.ServerChecksum.Signature,
+		keyusage.KERB_NON_KERB_CKSUM_SALT); !ok {
 		return false, errors.New("PAC service checksum verification failed")
 	}
 

+ 21 - 3
testdata/test_vectors.go

@@ -109,10 +109,10 @@ var TestVectors = map[string]string{
 }
 
 const (
-	TESTUSER1_KEYTAB      = "0502000000370001000b544553542e474f4b524235000974657374757365723100000001590d92c90100110010698c4df8e9f60e7eea5a21bf4526ad25000000470001000b544553542e474f4b524235000974657374757365723100000001590d92c90100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000370001000b544553542e474f4b524235000974657374757365723100000001590d92ca0200110010698c4df8e9f60e7eea5a21bf4526ad25000000470001000b544553542e474f4b524235000974657374757365723100000001590d92ca0200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9"
+	TESTUSER1_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0100110010698c4df8e9f60e7eea5a21bf4526ad25000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0200110010698c4df8e9f60e7eea5a21bf4526ad25000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000020000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba01001300102eb8501967a7886e1f0c63ac9be8c4a0000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba02001300102eb8501967a7886e1f0c63ac9be8c4a0000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba01001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba02001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee00000002"
 	TESTUSER1_WRONGPASSWD = "0502000000370001000b544553542e474f4b52423500097465737475736572310000000158ef4bc5010011001039a9a382153105f8708e80f93382654e000000470001000b544553542e474f4b52423500097465737475736572310000000158ef4bc60100120020fc5bb940d6075214e0c6fc0456ce68c33306094198a927b4187d7cf3f4aea50d"
-	TESTUSER2_KEYTAB      = "0502000000370001000b544553542e474f4b524235000974657374757365723200000001590d9311010011001086824c55ff5de30386dd83dc62b44bb7000000470001000b544553542e474f4b524235000974657374757365723200000001590d93120100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000370001000b544553542e474f4b524235000974657374757365723200000001590d9312020011001086824c55ff5de30386dd83dc62b44bb7000000470001000b544553542e474f4b524235000974657374757365723200000001590d93120200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92"
-	TESTUSER3_KEYTAB      = "05020000003b0001000b544553542e474f4b52423500097465737475736572330000000158ee76010100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b52423500097465737475736572330000000158ee760101001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b058000000001"
+	TESTUSER2_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723200000001592c0644010011001086824c55ff5de30386dd83dc62b44bb7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592c06440100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592c0644020011001086824c55ff5de30386dd83dc62b44bb7000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592c06440200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000020000003b0001000b544553542e474f4b524235000974657374757365723200000001592c064401001300106ccff358aaa8a4a41c444e173b1463c2000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592c064402001300106ccff358aaa8a4a41c444e173b1463c2000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592c064401001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592c064402001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd700000002"
+	TESTUSER3_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d01001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0200110010220789f5cf68e44a852c73b1e3729efc000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d02001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000020000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0100130010d2509e2bf9c08f73aafe08745c85e8fd000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0200130010d2509e2bf9c08f73aafe08745c85e8fd000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d010014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d020014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e00000002"
 	HTTP_KEYTAB           = "0502000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc010011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc01001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc020011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc02001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51"
 	SYSHTTP_KEYTAB        = "0502000000450001000b544553542e474f4b52423500077379734854545000000001590dc5af020012002043763702868978d1b6d91a36704b987e27e517250055bdfc40b8a6b3848d9aae"
 	TEST_AS_REQ           = "6a81a63081a3a103020105a20302010aa30e300c300aa10402020095a2020400a48186308183a00703050040000010a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235a320301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a511180f32303137303232303134323530315aa70602040f6755a6a814301202011202011102011002011702011902011a"
@@ -175,6 +175,24 @@ const (
   default_domain = test.gokrb5
  }
 
+[domain_realm]
+ .test.gokrb5 = TEST.GOKRB5
+ test.gokrb5 = TEST.GOKRB5
+ `
+	TEST_KRB5CONF_LATESTKDC = `[libdefaults]
+  default_realm = TEST.GOKRB5
+  dns_lookup_realm = false
+  dns_lookup_kdc = false
+  ticket_lifetime = 24h
+  forwardable = yes
+
+[realms]
+ TEST.GOKRB5 = {
+  kdc = 10.80.88.108:88
+  admin_server = 10.80.88.108:749
+  default_domain = test.gokrb5
+ }
+
 [domain_realm]
  .test.gokrb5 = TEST.GOKRB5
  test.gokrb5 = TEST.GOKRB5

+ 2 - 0
testenv/latest-krb5kdc-vagrant/.gitignore

@@ -0,0 +1,2 @@
+.vagrant
+.vagrant/*

+ 12 - 0
testenv/latest-krb5kdc-vagrant/Vagrantfile

@@ -0,0 +1,12 @@
+Vagrant.configure("2") do |config|
+  config.vm.provider "virtualbox" do |v|
+    v.memory = 1024
+    v.cpus = 1
+  end
+  config.vm.define "krb5kdc", primary: true do |krb5kdc|
+    krb5kdc.vm.hostname = "kdc.test.gokrb5"
+    krb5kdc.vm.box = "centos/7"
+    krb5kdc.vm.network "private_network", ip: "10.80.88.108", netmask: "255.255.0.0"
+    krb5kdc.vm.provision :shell, path: "bootstrap.sh"
+  end
+end

+ 33 - 0
testenv/latest-krb5kdc-vagrant/bootstrap.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+
+rm /etc/localtime
+ln -s /usr/share/zoneinfo/Europe/London /etc/localtime
+setenforce 0
+sed -i "s/SELINUX=enforcing/SELINUX=permissive/g" /etc/sysconfig/selinux
+
+yum update -y && yum clean all
+yum install -y tcpdump ntp vim ncurses telnet ncurses-devel tcl net-tools
+yum groupinstall "Development Tools" -y
+
+cd /vagrant
+tar -xvzf krb5-1.15.1.tar.gz && cd krb5-1.15.1/src && \
+./configure && make && make install
+
+ln -s /usr/local/var/krb5kdc /var/kerberos/krb5kdc
+cp /vagrant/krb5kdc.service /etc/systemd/system/
+systemctl enable krb5kdc
+
+systemctl stop firewalld
+systemctl disable firewalld
+systemctl enable ntpd
+
+cat <<EOF >> /etc/sysctl.conf
+net.ipv6.conf.all.disable_ipv6 = 1
+net.ipv6.conf.default.disable_ipv6 = 1
+net.ipv6.conf.lo.disable_ipv6 = 1
+EOF
+
+echo "10.80.88.89 client.test.gokrb5" >> /etc/hosts
+
+sh /vagrant/kdc-setup.sh
+

+ 4 - 0
testenv/latest-krb5kdc-vagrant/kadm5.acl

@@ -0,0 +1,4 @@
+__ADMIN_USER__@__REALM__	*
+*/admin@__REALM__	*
+*/*@__REALM__		i
+*@__REALM__		i

+ 77 - 0
testenv/latest-krb5kdc-vagrant/kdc-setup.sh

@@ -0,0 +1,77 @@
+#!/bin/bash
+
+systemctl start krb5kdc
+
+REALM=TEST.GOKRB5
+DOMAIN=test.gokrb5
+SERVER_HOST=kdc.test.gokrb5
+ADMIN_USERNAME=adminuser
+HOST_PRINCIPALS="kdc.test.gokrb5 host.test.gokrb5"
+SPNs="HTTP/host.test.gokrb5"
+KEYTABS="http.testtab!0:48!HTTP/host.test.gokrb5"
+INITIAL_USERS="testuser1 testuser2 testuser3"
+
+cp /vagrant/krb5.conf /etc/krb5.conf
+cp /var/kerberos/krb5kdc/kdc.conf /var/kerberos/krb5kdc/kdc.conf-old
+cp /vagrant/kdc.conf /var/kerberos/krb5kdc/kdc.conf
+cp /vagrant/kadm5.acl /var/kerberos/krb5kdc/kadm5.acl
+
+sed -i "s/__ADMIN_USER__/${ADMIN_USERNAME}/g" /var/kerberos/krb5kdc/kadm5.acl
+sed -i "s/__REALM__/${REALM}/g" /var/kerberos/krb5kdc/kadm5.acl
+sed -i "s/__REALM__/${REALM}/g" /var/kerberos/krb5kdc/kdc.conf
+sed -i "s/__REALM__/${REALM}/g" /etc/krb5.conf
+sed -i "s/__DOMAIN__/${DOMAIN}/g" /etc/krb5.conf
+sed -i "s/__SERVER_HOST__/${SERVER_HOST}/g" /etc/krb5.conf
+
+create_entropy() {
+   while true
+   do
+     sleep $(( ( RANDOM % 10 )  + 1 ))
+     echo "Generating Entropy... $RANDOM"
+   done
+}
+
+create_entropy &
+
+  echo "Kerberos initialisation required. Creating database for ${REALM} ..."
+  echo "This can take a long time if there is little entropy. A process has been started to create some."
+  MASTER_PASSWORD=$(echo $RANDOM$RANDOM$RANDOM | md5sum | awk '{print $1}')
+  /usr/local/sbin/kdb5_util create -r ${REALM} -s -P ${MASTER_PASSWORD}
+  echo "Kerberos database created."
+  /usr/local/sbin/kadmin.local -q "add_principal -randkey ${ADMIN_USERNAME}/admin"
+  echo "Kerberos admin user created: ${ADMIN_USERNAME} To update password: sudo /usr/local/sbin/kadmin.local -q \"change_password ${ADMIN_USERNAME}/admin\""
+
+  KEYTAB_DIR="/opt/krb5/data/keytabs"
+  mkdir -p $KEYTAB_DIR
+
+  if [ ! -z "${HOST_PRINCIPALS}" ]; then
+    for host in ${HOST_PRINCIPALS}
+    do
+      /usr/local/sbin/kadmin.local -q "add_principal -pw hostpasswordvalue -kvno 1 host/$host"
+      #/usr/sbin/kadmin.local -q "ktadd -k ${KEYTAB_DIR}/${host}.keytab host/$host"
+      #chmod 600 ${KEYTAB_DIR}/${host}.keytab
+      echo "Created host principal host/$host"
+    done
+  fi
+
+  if [ ! -z "${SPNs}" ]; then
+    for service in ${SPNs}
+    do
+      /usr/local/sbin/kadmin.local -q "add_principal -pw spnpasswordvalue -kvno 1 ${service}"
+      #/usr/sbin/kadmin.local -q "cpw -pw passwordvalue ${service}"
+      echo "Created principal for service $service"
+    done
+  fi
+
+  if [ ! -z "$INITIAL_USERS" ]; then
+    for user in $INITIAL_USERS
+    do
+      /usr/local/sbin/kadmin.local -q "add_principal -pw passwordvalue -kvno 1 $user"
+      #/usr/sbin/kadmin.local -q "ktadd -k ${KEYTAB_DIR}/${user}.testtab $user"
+      echo "User $user added to kerberos database. To update password: sudo /usr/local/sbin/kadmin.local -q \"change_password $user\""
+    done
+  fi
+
+  echo "Kerberos initialisation complete"
+
+systemctl restart krb5kdc

+ 14 - 0
testenv/latest-krb5kdc-vagrant/kdc.conf

@@ -0,0 +1,14 @@
+[kdcdefaults]
+ kdc_ports = 88
+ kdc_tcp_ports = 88
+
+[realms]
+ __REALM__ = {
+  master_key_type = aes256-cts
+  max_life = 12h 0m 0s
+  max_renewable_life = 7d 0h 0m 0s
+  acl_file = /var/kerberos/krb5kdc/kadm5.acl
+  dict_file = /usr/share/dict/words
+  admin_keytab = /opt/krb5/data/kadm5.keytab
+  supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal aes128-sha2:normal aes256-sha2:normal
+ }

BIN
testenv/latest-krb5kdc-vagrant/krb5-1.15.1.tar.gz


+ 32 - 0
testenv/latest-krb5kdc-vagrant/krb5.conf

@@ -0,0 +1,32 @@
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+
+[libdefaults]
+  default_realm = __REALM__
+  dns_lookup_realm = false
+  dns_lookup_kdc = false
+  ticket_lifetime = 24h
+  forwardable = yes
+
+[realms]
+ __REALM__ = {
+  kdc = __SERVER_HOST__:88
+  admin_server = __SERVER_HOST__:749
+  default_domain = __DOMAIN__
+ }
+
+[domain_realm]
+ .__DOMAIN__ = __REALM__
+ __DOMAIN__ = __REALM__
+
+[appdefaults]
+ pam = {
+   debug = false
+   ticket_lifetime = 36000
+   renew_lifetime = 36000
+   forwardable = true
+   krb4_convert = false
+ }

+ 10 - 0
testenv/latest-krb5kdc-vagrant/krb5kdc.service

@@ -0,0 +1,10 @@
+[Unit]
+Description=Kerberos 5 KDC
+After=syslog.target network.target
+
+[Service]
+Type=forking
+ExecStart=/usr/local/sbin/krb5kdc 
+
+[Install]
+WantedBy=multi-user.target

BIN
testenv/testuser1.testtab


BIN
testenv/testuser2.testtab


BIN
testenv/testuser3.testtab