Jonathan Turner пре 9 година
родитељ
комит
7b8270afc8

+ 2 - 1
README.md

@@ -14,4 +14,5 @@ This is work in progress and does not yet work...
 * RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
 * RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
 [text](https://www.ietf.org/rfc/rfc3962.txt) [html](https://tools.ietf.org/html/rfc3962)
 [text](https://www.ietf.org/rfc/rfc3962.txt) [html](https://tools.ietf.org/html/rfc3962)
 
 
-## Useful Links
+## Useful Links
+* https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing

+ 26 - 11
krb5crypto/EncryptionEngine.go

@@ -8,21 +8,21 @@ import (
 
 
 type EType interface {
 type EType interface {
 	GetETypeID() int
 	GetETypeID() int
-	GetKeyByteSize() int // See protocol key format for defined values
-	StringToKey(string, salt string, s2kparams []byte) (protocolKey []byte)
-	GetDefaultStringToKeyParams() string // s2kparams
+	GetKeyByteSize() int // See "protocol key format" for defined values
 	GetKeySeedBitLength() int            // key-generation seed length, k
 	GetKeySeedBitLength() int            // key-generation seed length, k
-	RandomToKey(b []byte) (protocolKey []byte)
+	GetDefaultStringToKeyParams() string // default string-to-key parameters (s2kparams)
+	StringToKey(string, salt, s2kparams string) ([]byte, error) // string-to-key (UTF-8 string, UTF-8 string, opaque)->(protocol-key)
+	RandomToKey(b []byte) []byte		// random-to-key (bitstring[K])->(protocol-key)
 	GetHMACBitLength() int                                      // HMAC output size, h
 	GetHMACBitLength() int                                      // HMAC output size, h
 	GetMessageBlockByteSize() int                               // message block size, m
 	GetMessageBlockByteSize() int                               // message block size, m
-	Encrypt(key, message []byte) (ct []byte, err error)         // E function
-	Decrypt(key, ciphertext []byte) (message []byte, err error) // D function
+	Encrypt(key, message []byte) ([]byte, []byte, error)         // E function - encrypt (specific-key, state, octet string)->(state, octet string)
+	Decrypt(key, ciphertext []byte) ([]byte, error) // D function
 	GetCypherBlockBitLength() int                               // cipher block size, c
 	GetCypherBlockBitLength() int                               // cipher block size, c
 	GetConfounderByteSize() int                                 // This is the same as the cipher block size but in bytes.
 	GetConfounderByteSize() int                                 // This is the same as the cipher block size but in bytes.
-	DeriveKey(protocolKey, usage []byte) (specificKey []byte)   // DK
-	DeriveRandom(protocolKey, usage []byte) ([]byte, error)     // DR
+	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)
 }
 }
-type encryptFunc func([]byte, []byte) ([]byte, error)
+type encryptFunc func([]byte, []byte) ([]byte, []byte, error)
 
 
 // RFC3961: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state))
 // RFC3961: 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
 // key - base key or protocol key. Likely to be a key from a keytab file
@@ -45,17 +45,32 @@ func deriveRandom(key, usage []byte, n, k int, encrypt encryptFunc) ([]byte, err
 	K4 = ...
 	K4 = ...
 
 
 	DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/
 	DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/
-	K, err := encrypt(key, nFoldUsage)
+	_, K, err := encrypt(key, nFoldUsage)
 	if err != nil {
 	if err != nil {
 		return out, err
 		return out, err
 	}
 	}
 	for i := copy(out, K); i < len(out); {
 	for i := copy(out, K); i < len(out); {
-		K, _ = encrypt(key, K)
+		_, K, _ = encrypt(key, K)
 		i = i + copy(out[i:], K)
 		i = i + copy(out[i:], K)
 	}
 	}
 	return out, nil
 	return out, nil
 }
 }
 
 
+func zeroPad(b []byte, m int) ([]byte, error) {
+	if m <= 0 {
+		return nil, errors.New("Invalid message block size when padding")
+	}
+	if b == nil || len(b) == 0 {
+		return nil, errors.New("Data not valid to pad: Zero size")
+	}
+	if l := len(b) %m; l != 0{
+		n := m - l
+		z := make([]byte, n)
+		b = append(b, z...)
+	}
+	return b, nil
+}
+
 func pkcs7Pad(b []byte, m int) ([]byte, error) {
 func pkcs7Pad(b []byte, m int) ([]byte, error) {
 	if m <= 0 {
 	if m <= 0 {
 		return nil, errors.New("Invalid message block size when padding")
 		return nil, errors.New("Invalid message block size when padding")

+ 273 - 0
krb5crypto/aes-cts-hmac-sha1-96.go

@@ -0,0 +1,273 @@
+package krb5crypto
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/sha1"
+	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"golang.org/x/crypto/pbkdf2"
+	"strings"
+	"os"
+)
+
+const (
+	s2kParamsZero = 4294967296
+)
+
+func AESStringToKey(secret string, salt string, s2kparams string, e EType) ([]byte, 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 int
+	if s2kparams == "00 00 00 00" {
+		i = s2kParamsZero
+	} else {
+		s := strings.Replace(s2kparams, " ", "", -1)
+		if len(s) != 8 {
+			return nil, errors.New("Invalid s2kparams")
+		}
+		b, err := hex.DecodeString(s)
+		if err != nil {
+			return nil, errors.New("Invalid s2kparams")
+		}
+		buf := bytes.NewBuffer(b)
+		binary.Read(buf, binary.BigEndian, &i)
+		if i == 0 {
+			i = s2kParamsZero
+		}
+	}
+
+	return AESStringToKeyIter(secret, salt, i, e)
+}
+
+func AESStringToPBKDF2(secret string, salt string, iterations int, e EType) []byte {
+	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), sha1.New)
+}
+
+func AESStringToKeyIter(secret string, salt string, iterations int, e EType) ([]byte, error) {
+	tkey := AESRandomToKey(AESStringToPBKDF2(secret, salt, iterations, e))
+	key, err := AESDeriveKey(tkey, []byte("kerberos"), e)
+	return key, err
+}
+
+func AESRandomToKey(b []byte) []byte {
+	return b
+}
+
+func AESDeriveRandom(protocolKey, usage []byte, e EType) ([]byte, error) {
+	r, err := deriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e.Encrypt)
+	return r, err
+}
+
+func AESDeriveKey(protocolKey, usage []byte, e EType) ([]byte, error) {
+	r, err := AESDeriveRandom(protocolKey, usage, e)
+	if err != nil {
+		return nil, err
+	}
+	return AESRandomToKey(r), nil
+}
+
+func AESEncrypt(key, iv, message []byte, e EType) ([]byte, []byte, error){
+	if len(key) != e.GetKeyByteSize() {
+		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
+	}
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
+	}
+	mode := cipher.NewCBCEncrypter(block, iv)
+
+	//last block size
+	lbs := len(message)%aes.BlockSize
+	m := message
+	message, _ = zeroPad(message, aes.BlockSize)
+	var ct []byte
+ 	if lbs != 0 {
+		// Will need to cycle through each block to achieve CTS
+		var c []byte
+		cb := make([]byte, aes.BlockSize)
+		for len(m) > 0 {
+			var tm []byte
+			if len(m) >= aes.BlockSize{
+				tm = m[:aes.BlockSize]
+			} else {
+				tm, _ = zeroPad(m, aes.BlockSize)
+			}
+			//Encrypt one block of the message
+			fmt.Fprintf(os.Stderr, "JT: tm %v\n", hex.EncodeToString(tm))
+
+			mode.CryptBlocks(cb, tm)
+			fmt.Fprintf(os.Stderr, "JT: cb %v\n", hex.EncodeToString(cb))
+			//Append to the overall cipher text
+			c = append(c, cb...)
+			//iv = cb[:e.GetConfounderByteSize()]
+			mode = cipher.NewCBCEncrypter(block, cb)
+			if len(m) < 16{
+				break
+			}
+			m = m[aes.BlockSize:]
+		}
+		//ct = append(ct, iv...)
+		ct = append(ct, c...)
+	} else {
+		c := make([]byte, len(message))
+		mode.CryptBlocks(c, message)
+		ct = append(ct, c...)
+	}
+	//Ref: https://tools.ietf.org/html/rfc3962 section 5
+	/*For consistency, ciphertext stealing is always used for the last two
+	blocks of the data to be encrypted, as in [RC5].  If the data length
+	is a multiple of the block size, this is equivalent to plain CBC mode
+	with the last two ciphertext blocks swapped.*/
+	//Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
+	// Swap the last two cipher blocks
+	// Get 2nd to last (penultimate) block
+	cpb := ct[len(ct)-aes.BlockSize-aes.BlockSize : len(ct)-aes.BlockSize]
+	// Get last block
+	clb := ct[len(ct)-aes.BlockSize:]
+	// Swap
+	ct = ct[:len(ct)-aes.BlockSize-aes.BlockSize]
+	ct = append(ct, clb...)
+	ct = append(ct, cpb...)
+	// Truncate the ciphertext to the length of the original plaintext
+	return ct[:e.GetConfounderByteSize()], ct[e.GetConfounderByteSize():], nil
+}
+
+func AESCTSEncrypt(key, message []byte, e EType) ([]byte, []byte, error) {
+	ivz := make([]byte, 16)
+	return AESEncrypt(key, ivz, message, e)
+	l := len(message)
+	//last block size
+	lbs := len(message)%aes.BlockSize
+	if len(key) != e.GetKeyByteSize() {
+		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
+	}
+
+	if lbs != 0 {
+		message, _ = zeroPad(message, aes.BlockSize)
+	}
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
+	}
+	//RFC 3961: initial cipher state      All bits zero
+	iv := make([]byte, e.GetConfounderByteSize())
+	ct := make([]byte, l + e.GetConfounderByteSize())
+	mode := cipher.NewCBCEncrypter(block, iv)
+	mode.CryptBlocks(ct, message)
+	iv = ct[:aes.BlockSize]
+	ct = ct[aes.BlockSize:]
+	fmt.Fprintf(os.Stderr, "JT: len ct %v\n", len(ct))
+	ct ,_ = zeroPad(ct, aes.BlockSize)
+	fmt.Fprintf(os.Stderr, "JT: ct %v\n", hex.EncodeToString(ct))
+
+
+	if len(message) == aes.BlockSize {
+		//Ref: https://tools.ietf.org/html/rfc3962 section 5
+		//If exactly one block is to be encrypted, that block is simply encrypted with AES (also known as ECBmode).
+		return ct[e.GetConfounderByteSize():], ct[:l], nil
+	}
+	//Ref: https://tools.ietf.org/html/rfc3962 section 5
+	/*For consistency, ciphertext stealing is always used for the last two
+	blocks of the data to be encrypted, as in [RC5].  If the data length
+	is a multiple of the block size, this is equivalent to plain CBC mode
+	with the last two ciphertext blocks swapped.*/
+	//Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
+	// Swap the last two cipher blocks
+	// Get 2nd to last (penultimate) block
+	cpb := ct[len(ct)-aes.BlockSize-aes.BlockSize : len(ct)-aes.BlockSize]
+	// Get last block
+	clb := ct[len(ct)-aes.BlockSize:]
+	// Swap
+	ct = ct[:len(ct)-aes.BlockSize-aes.BlockSize]
+	ct = append(ct, clb...)
+	ct = append(ct, cpb...)
+	// Truncate the ciphertext to the length of the original plaintext
+	return iv, ct, nil
+	//TODO do we need to add the hash to the beginning?
+}
+
+func AESCTSDecrypt(key, ciphertext []byte, e EType) ([]byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
+
+	}
+
+	// Take the iv off the beginning and the hash block off the end
+	iv := ciphertext[:e.GetConfounderByteSize()]
+	cipherMsg := ciphertext[e.GetConfounderByteSize() : len(ciphertext)-(e.GetHMACBitLength()/8)]
+	//cipherHash := ciphertext[len(ciphertext)-(e.GetHMACBitLength()/8):]
+
+	if len(cipherMsg) < 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(cipherMsg))
+	}
+
+	// Configure the CBC
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, fmt.Errorf("Error creating cipher: %v", err)
+	}
+	mode := cipher.NewCBCDecrypter(block, iv)
+
+	if len(cipherMsg) > aes.BlockSize {
+		// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
+		// Get 2nd to last (penultimate) block
+		cpb := cipherMsg[len(cipherMsg)-(len(cipherMsg)%aes.BlockSize)-aes.BlockSize : len(cipherMsg)-(len(cipherMsg)%aes.BlockSize)]
+		// Get last block
+		clb := cipherMsg[len(cipherMsg)-(len(cipherMsg)%aes.BlockSize):]
+		//Decryt the 2nd to last (penultimate) block
+		pb := make([]byte, aes.BlockSize)
+		mode.CryptBlocks(pb, cpb)
+		// number of byte needed to pad
+		npb := aes.BlockSize - len(cipherMsg)%aes.BlockSize
+		//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
+		clb = append(clb, pb[len(pb)-npb:]...)
+		// Swap the last two cipher blocks
+		cipherMsg = cipherMsg[:len(cipherMsg)-aes.BlockSize-(len(cipherMsg)%aes.BlockSize)]
+		cipherMsg = append(cipherMsg, clb...)
+		cipherMsg = append(cipherMsg, cpb...)
+	}
+
+	message := make([]byte, len(cipherMsg))
+	mode.CryptBlocks(message, cipherMsg)
+	//TODO verify checksum here
+	return message, nil
+}
+
+/*func DEwithHMAC(key, message []byte) (ct []byte, err error) {
+	if len(key) != KeySize {
+		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, len(key))
+	}
+	if len(message)%aes.BlockSize != 0 {
+		return nil, errors.New("Plaintext is not a multiple of the block size")
+	}
+
+	iv := make([]byte, NonceSize)
+	_, err = rand.Read(iv)
+	if err != nil {
+		return nil, fmt.Errorf("Error creating random nonce: %v", err)
+	}
+
+	// NewCipher only returns an error with an invalid key size,
+	// but the key size was checked at the beginning of the function.
+	c, err := aes.NewCipher(key[:CipherKeyLength])
+	if err != nil {
+		return nil, fmt.Errorf("Error creating cipher: %v", err)
+	}
+	ctr := cipher.NewCBCEncrypter(c, iv)
+	ctr.CryptBlocks(ct, message)
+
+	h := hmac.New(sha1.New(), key[CipherKeyLength:])
+	ct = append(iv, ct...)
+	h.Write(ct)
+	ct = h.Sum(ct)
+	return ct[:HMACKeyLength], nil
+}*/

+ 27 - 3
krb5crypto/aes256-cts-hmac-sha1-96_test.go → krb5crypto/aes-cts-hmac-sha1-96_test.go

@@ -6,6 +6,29 @@ import (
 	"testing"
 	"testing"
 )
 )
 
 
+func TestAesCtsHmacSha196_Encrypt(t *testing.T) {
+	//iv := make([]byte, 16)
+	key, _ := hex.DecodeString("636869636b656e207465726979616b69")
+	var tests = []struct {
+		input  string
+		output string
+		nextIV string
+	}{
+		{"4920776f756c64206c696b652074686520", "c6353568f2bf8cb4d8a580362da7ff7f97", "c6353568f2bf8cb4d8a580362da7ff7f"},
+	}
+	var e Aes128CtsHmacSha196
+	for i, test := range tests {
+		m, err := hex.DecodeString(test.input)
+		niv, c, err := e.Encrypt(key, m)
+		if err != nil {
+			t.Errorf("Encryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.output, hex.EncodeToString(c), "Encrypted result not as expected")
+		assert.Equal(t, test.nextIV, hex.EncodeToString(niv), "Next state IV not as expected")
+
+	}
+}
+
 func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
 func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
 	// Test vectors from RFC 3962 Appendix B
 	// Test vectors from RFC 3962 Appendix B
 	b, _ := hex.DecodeString("1234567878563412")
 	b, _ := hex.DecodeString("1234567878563412")
@@ -27,10 +50,11 @@ func TestAes256CtsHmacSha196_StringToKey(t *testing.T) {
 		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"},
 		{1200, "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "pass phrase exceeds block size", "9ccad6d468770cd51b10e6a68721be611a8b4d282601db3b36be9246915ec82a", "d78c5c9cb872a8c9dad4697f0bb5b2d21496c82beb2caeda2112fceea057401b"},
 		{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"},
 		{50, s2, "EXAMPLE.COMpianist", "6b9cf26d45455a43a5b8bb276a403b39e7fe37a0c41e02c281ff3069e1e94f52", "4b6d9839f84406df1f09cc166db4b83c571848b784a3d6bdc346589a3e393f9e"},
 	}
 	}
+	var e Aes256CtsHmacSha196
 	for i, test := range tests {
 	for i, test := range tests {
-		var e Aes256CtsHmacSha196
-		assert.Equal(t, test.pbkdf2, hex.EncodeToString(e.StringToPBKDF2(test.phrase, test.salt, test.iterations)), "PBKDF2 not as expected")
-		k, err := e.StringToKeyIter(test.phrase, test.salt, test.iterations)
+
+		assert.Equal(t, test.pbkdf2, hex.EncodeToString(AESStringToPBKDF2(test.phrase, test.salt, test.iterations, e)), "PBKDF2 not as expected")
+		k, err := AESStringToKeyIter(test.phrase, test.salt, test.iterations, e)
 		if err != nil {
 		if err != nil {
 			t.Errorf("Error in processing string to key for test %d: %v", i, err)
 			t.Errorf("Error in processing string to key for test %d: %v", i, err)
 		}
 		}

+ 116 - 0
krb5crypto/aes128-cts-hmac-sha1-96.go

@@ -0,0 +1,116 @@
+package krb5crypto
+
+import (
+	"crypto/aes"
+	"crypto/sha1"
+	"hash"
+)
+
+// RFC 3962
+//+--------------------------------------------------------------------+
+//|               protocol key format        128- or 256-bit string    |
+//|                                                                    |
+//|            string-to-key function        PBKDF2+DK with variable   |
+//|                                          iteration count (see      |
+//|                                          above)                    |
+//|                                                                    |
+//|  default string-to-key parameters        00 00 10 00               |
+//|                                                                    |
+//|        key-generation seed length        key size                  |
+//|                                                                    |
+//|            random-to-key function        identity function         |
+//|                                                                    |
+//|                  hash function, H        SHA-1                     |
+//|                                                                    |
+//|               HMAC output size, h        12 octets (96 bits)       |
+//|                                                                    |
+//|             message block size, m        1 octet                   |
+//|                                                                    |
+//|  encryption/decryption functions,        AES in CBC-CTS mode       |
+//|  E and D                                 (cipher block size 16     |
+//|                                          octets), with next-to-    |
+//|                                          last block (last block    |
+//|                                          if only one) as CBC-style |
+//|                                          ivec                      |
+//+--------------------------------------------------------------------+
+//
+//+--------------------------------------------------------------------+
+//|                         encryption types                           |
+//+--------------------------------------------------------------------+
+//|         type name                  etype value          key size   |
+//+--------------------------------------------------------------------+
+//|   aes128-cts-hmac-sha1-96              17                 128      |
+//|   aes256-cts-hmac-sha1-96              18                 256      |
+//+--------------------------------------------------------------------+
+//
+//+--------------------------------------------------------------------+
+//|                          checksum types                            |
+//+--------------------------------------------------------------------+
+//|        type name                 sumtype value           length    |
+//+--------------------------------------------------------------------+
+//|    hmac-sha1-96-aes128                15                   96      |
+//|    hmac-sha1-96-aes256                16                   96      |
+//+--------------------------------------------------------------------+
+
+type Aes128CtsHmacSha196 struct {
+}
+
+func (e Aes128CtsHmacSha196) GetETypeID() int {
+	return 17
+}
+
+func (e Aes128CtsHmacSha196) GetKeyByteSize() int {
+	return 128 / 8
+}
+
+func (e Aes128CtsHmacSha196) GetKeySeedBitLength() int {
+	return e.GetKeyByteSize() * 8
+}
+
+func (e Aes128CtsHmacSha196) GetHash() hash.Hash {
+	return sha1.New()
+}
+
+func (e Aes128CtsHmacSha196) GetMessageBlockByteSize() int {
+	return 1
+}
+
+func (e Aes128CtsHmacSha196) GetDefaultStringToKeyParams() string {
+	return "00 00 10 00"
+}
+
+func (e Aes128CtsHmacSha196) GetConfounderByteSize() int {
+	return aes.BlockSize
+}
+
+func (e Aes128CtsHmacSha196) GetHMACBitLength() int {
+	return 96
+}
+
+func (e Aes128CtsHmacSha196) GetCypherBlockBitLength() int {
+	return aes.BlockSize * 8
+}
+
+func (e Aes128CtsHmacSha196) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
+	return AESStringToKey(secret, salt, s2kparams, e)
+}
+
+func (e Aes128CtsHmacSha196) RandomToKey(b []byte) []byte {
+	return AESRandomToKey(b)
+}
+
+func (e Aes128CtsHmacSha196) Encrypt(key, message []byte) ([]byte, []byte, error) {
+	return AESCTSEncrypt(key, message, e)
+}
+
+func (e Aes128CtsHmacSha196) Decrypt(key, ciphertext []byte) ([]byte, error) {
+	return AESCTSDecrypt(key, ciphertext, e)
+}
+
+func (e Aes128CtsHmacSha196) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
+	return AESDeriveKey(protocolKey, usage, e)
+}
+
+func (e Aes128CtsHmacSha196) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
+	return AESDeriveRandom(protocolKey, usage, e)
+}

+ 31 - 151
krb5crypto/aes256-cts-hmac-sha1-96.go

@@ -1,17 +1,9 @@
 package krb5crypto
 package krb5crypto
 
 
 import (
 import (
-	"bytes"
 	"crypto/aes"
 	"crypto/aes"
-	"crypto/cipher"
 	"crypto/sha1"
 	"crypto/sha1"
-	"encoding/binary"
-	"encoding/hex"
-	"errors"
-	"fmt"
-	"golang.org/x/crypto/pbkdf2"
 	"hash"
 	"hash"
-	"strings"
 )
 )
 
 
 // RFC 3962
 // RFC 3962
@@ -60,177 +52,65 @@ import (
 //|    hmac-sha1-96-aes256                16                   96      |
 //|    hmac-sha1-96-aes256                16                   96      |
 //+--------------------------------------------------------------------+
 //+--------------------------------------------------------------------+
 
 
-const (
-	s2kParamsZero = 4294967296
-)
-
 type Aes256CtsHmacSha196 struct {
 type Aes256CtsHmacSha196 struct {
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetETypeID() int {
+func (e Aes256CtsHmacSha196) GetETypeID() int {
 	return 18
 	return 18
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetKeyByteSize() int {
+func (e Aes256CtsHmacSha196) GetKeyByteSize() int {
 	return 256 / 8
 	return 256 / 8
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetKeySeedBitLength() int {
+func (e Aes256CtsHmacSha196) GetKeySeedBitLength() int {
 	return e.GetKeyByteSize() * 8
 	return e.GetKeyByteSize() * 8
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetHash() hash.Hash {
+func (e Aes256CtsHmacSha196) GetHash() hash.Hash {
 	return sha1.New()
 	return sha1.New()
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetMessageBlockByteSize() int {
+func (e Aes256CtsHmacSha196) GetMessageBlockByteSize() int {
 	return 1
 	return 1
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetDefaultStringToKeyParams() string {
+func (e Aes256CtsHmacSha196) GetDefaultStringToKeyParams() string {
 	return "00 00 10 00"
 	return "00 00 10 00"
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetConfounderByteSize() int {
+func (e Aes256CtsHmacSha196) GetConfounderByteSize() int {
 	return aes.BlockSize
 	return aes.BlockSize
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetHMACBitLength() int {
+func (e Aes256CtsHmacSha196) GetHMACBitLength() int {
 	return 96
 	return 96
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) GetCypherBlockBitLength() int {
+func (e Aes256CtsHmacSha196) GetCypherBlockBitLength() int {
 	return aes.BlockSize * 8
 	return aes.BlockSize * 8
 }
 }
 
 
-func (e *Aes256CtsHmacSha196) StringToKey(secret string, salt string, s2kparams string) ([]byte, 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 int
-	if s2kparams == "00 00 00 00" {
-		i = s2kParamsZero
-	} else {
-		s := strings.Replace(s2kparams, " ", "", -1)
-		if len(s) != 8 {
-			return nil, errors.New("Invalid s2kparams")
-		}
-		b, err := hex.DecodeString(s)
-		if err != nil {
-			return nil, errors.New("Invalid s2kparams")
-		}
-		buf := bytes.NewBuffer(b)
-		binary.Read(buf, binary.BigEndian, &i)
-		if i == 0 {
-			i = s2kParamsZero
-		}
-	}
-
-	return e.StringToKeyIter(secret, salt, i)
-}
-
-func (e *Aes256CtsHmacSha196) StringToPBKDF2(secret string, salt string, iterations int) []byte {
-	return pbkdf2.Key([]byte(secret), []byte(salt), iterations, e.GetKeyByteSize(), sha1.New)
-}
-
-func (e *Aes256CtsHmacSha196) StringToKeyIter(secret string, salt string, iterations int) ([]byte, error) {
-	tkey := e.RandomToKey(e.StringToPBKDF2(secret, salt, iterations))
-	key, err := e.DeriveKey(tkey, []byte("kerberos"))
-	return key, err
-}
-
-func (e *Aes256CtsHmacSha196) RandomToKey(b []byte) []byte {
-	return b
-}
-
-func (e *Aes256CtsHmacSha196) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
-	r, err := deriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e.Encrypt)
-	return r, err
-}
-
-func (e *Aes256CtsHmacSha196) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
-	r, err := e.DeriveRandom(protocolKey, usage)
-	if err != nil {
-		return nil, err
-	}
-	return e.RandomToKey(r), nil
-}
-
-func (e *Aes256CtsHmacSha196) Encrypt(key, message []byte) ([]byte, error) {
-	if len(key) != e.GetKeyByteSize() {
-		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
-	}
-	if len(message)%aes.BlockSize != 0 {
-		message, _ = pkcs7Pad(message, e.GetMessageBlockByteSize())
-	}
-
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		return nil, fmt.Errorf("Error creating cipher: %v", err)
-	}
-
-	//RFC 3961: initial cipher state      All bits zero
-	iv := make([]byte, e.GetConfounderByteSize())
-	//_, err = rand.Read(iv) //Not needed as all bits need to be zero
-
-	ct := make([]byte, len(message))
-	mode := cipher.NewCBCEncrypter(block, iv)
-	mode.CryptBlocks(ct, message)
-	return ct, nil
-}
-
-func (e *Aes256CtsHmacSha196) Decrypt(key, ciphertext []byte) (message []byte, err error) {
-	if len(key) != e.GetKeySeedBitLength() {
-		err = fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
-		return
-	}
-	if len(ciphertext) < aes.BlockSize || len(ciphertext)%aes.BlockSize != 0 {
-		err = errors.New("Ciphertext is not a multiple of the block size.")
-		return
-	}
-
-	block, err := aes.NewCipher(key)
-	if err != nil {
-		err = fmt.Errorf("Error creating cipher: %v", err)
-		return
-	}
-
-	iv := ciphertext[:aes.BlockSize]
-	ciphertext = ciphertext[aes.BlockSize:]
-
-	mode := cipher.NewCBCDecrypter(block, iv)
-	mode.CryptBlocks(message, ciphertext)
-	return
-}
-
-/*func DEwithHMAC(key, message []byte) (ct []byte, err error) {
-	if len(key) != KeySize {
-		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, len(key))
-	}
-	if len(message)%aes.BlockSize != 0 {
-		return nil, errors.New("Plaintext is not a multiple of the block size")
-	}
-
-	iv := make([]byte, NonceSize)
-	_, err = rand.Read(iv)
-	if err != nil {
-		return nil, fmt.Errorf("Error creating random nonce: %v", err)
-	}
-
-	// NewCipher only returns an error with an invalid key size,
-	// but the key size was checked at the beginning of the function.
-	c, err := aes.NewCipher(key[:CipherKeyLength])
-	if err != nil {
-		return nil, fmt.Errorf("Error creating cipher: %v", err)
-	}
-	ctr := cipher.NewCBCEncrypter(c, iv)
-	ctr.CryptBlocks(ct, message)
+func (e Aes256CtsHmacSha196) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
+	return AESStringToKey(secret, salt, s2kparams, e)
+}
+
+func (e Aes256CtsHmacSha196) RandomToKey(b []byte) []byte {
+	return AESRandomToKey(b)
+}
+
+func (e Aes256CtsHmacSha196) Encrypt(key, message []byte) ([]byte, []byte, error) {
+	return AESCTSEncrypt(key, message, e)
+}
+
+func (e Aes256CtsHmacSha196) Decrypt(key, ciphertext []byte) ([]byte, error) {
+	return AESCTSDecrypt(key, ciphertext, e)
+}
 
 
-	h := hmac.New(sha1.New(), key[CipherKeyLength:])
-	ct = append(iv, ct...)
-	h.Write(ct)
-	ct = h.Sum(ct)
-	return ct[:HMACKeyLength], nil
-}*/
+func (e Aes256CtsHmacSha196) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
+	return AESDeriveKey(protocolKey, usage, e)
+}
+
+func (e Aes256CtsHmacSha196) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
+	return AESDeriveRandom(protocolKey, usage, e)
+}

+ 4 - 4
krb5crypto/des3-cbc-sha1-kd.go

@@ -108,9 +108,9 @@ func (e *Des3CbcSha1Kd) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
 	return e.RandomToKey(r), nil
 	return e.RandomToKey(r), nil
 }
 }
 
 
-func (e *Des3CbcSha1Kd) Encrypt(key, message []byte) ([]byte, error) {
+func (e *Des3CbcSha1Kd) Encrypt(key, message []byte) ([]byte, []byte, error) {
 	if len(key) != e.GetKeyByteSize() {
 	if len(key) != e.GetKeyByteSize() {
-		return nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
+		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
 
 
 	}
 	}
 	if len(message)%e.GetMessageBlockByteSize() != 0 {
 	if len(message)%e.GetMessageBlockByteSize() != 0 {
@@ -119,7 +119,7 @@ func (e *Des3CbcSha1Kd) Encrypt(key, message []byte) ([]byte, error) {
 
 
 	block, err := des.NewTripleDESCipher(key)
 	block, err := des.NewTripleDESCipher(key)
 	if err != nil {
 	if err != nil {
-		return nil, fmt.Errorf("Error creating cipher: %v", err)
+		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
 
 
 	}
 	}
 
 
@@ -130,7 +130,7 @@ func (e *Des3CbcSha1Kd) Encrypt(key, message []byte) ([]byte, error) {
 	ct := make([]byte, len(message))
 	ct := make([]byte, len(message))
 	mode := cipher.NewCBCEncrypter(block, iv)
 	mode := cipher.NewCBCEncrypter(block, iv)
 	mode.CryptBlocks(ct, message)
 	mode.CryptBlocks(ct, message)
-	return ct, nil
+	return ct[:e.GetConfounderByteSize()], ct, nil
 }
 }
 
 
 func (e *Des3CbcSha1Kd) Decrypt(key, ciphertext []byte) (message []byte, err error) {
 func (e *Des3CbcSha1Kd) Decrypt(key, ciphertext []byte) (message []byte, err error) {

+ 2 - 2
krb5types/PAData.go

@@ -39,7 +39,7 @@ type ETypeInfo2Entry struct {
 type ETypeInfo2 []ETypeInfo2Entry
 type ETypeInfo2 []ETypeInfo2Entry
 
 
 func (pa *PAData) GetETypeInfo() (d ETypeInfo, err error) {
 func (pa *PAData) GetETypeInfo() (d ETypeInfo, err error) {
-	dt := krbDictionary.PADataTypesByName["pa-etype-info"]
+	dt := KrbDictionary.PADataTypesByName["pa-etype-info"]
 	if pa.PADataType != dt {
 	if pa.PADataType != dt {
 		err = fmt.Errorf("PAData does not contain PA EType Info data. TypeID Expected: %v; Actual: %v", dt, pa.PADataType)
 		err = fmt.Errorf("PAData does not contain PA EType Info data. TypeID Expected: %v; Actual: %v", dt, pa.PADataType)
 		return
 		return
@@ -49,7 +49,7 @@ func (pa *PAData) GetETypeInfo() (d ETypeInfo, err error) {
 }
 }
 
 
 func (pa *PAData) GetETypeInfo2() (d ETypeInfo2, err error) {
 func (pa *PAData) GetETypeInfo2() (d ETypeInfo2, err error) {
-	dt := krbDictionary.PADataTypesByName["pa-etype-info2"]
+	dt := KrbDictionary.PADataTypesByName["pa-etype-info2"]
 	if pa.PADataType != dt {
 	if pa.PADataType != dt {
 		err = fmt.Errorf("PAData does not contain PA EType Info 2 data. TypeID Expected: %v; Actual: %v", dt, pa.PADataType)
 		err = fmt.Errorf("PAData does not contain PA EType Info 2 data. TypeID Expected: %v; Actual: %v", dt, pa.PADataType)
 		return
 		return

+ 1 - 1
krb5types/dictionary.go

@@ -1,6 +1,6 @@
 package krb5types
 package krb5types
 
 
-var krbDictionary = struct {
+var KrbDictionary = struct {
 	MsgTypesByID      map[int]string
 	MsgTypesByID      map[int]string
 	NameTypesByID     map[int]string
 	NameTypesByID     map[int]string
 	ErrorCodesByID    map[int]string
 	ErrorCodesByID    map[int]string

+ 57 - 0
messages/KDCRep_test.go

@@ -0,0 +1,57 @@
+package messages
+
+import (
+"github.com/stretchr/testify/assert"
+"testing"
+	"io/ioutil"
+	"fmt"
+	"github.com/jcmturner/gokrb5/krb5types"
+	"github.com/jcmturner/gokrb5/keytab"
+	"github.com/jcmturner/gokrb5/krb5crypto"
+)
+
+
+func TestUnmarshalASRep(t *testing.T) {
+	asrepData, _ := ioutil.ReadFile("/home/turnerj/IdeaProjects/golang/src/github.com/jcmturner/gokrb5/AS-REP.raw")
+	asRep, err := UnmarshalASRep(asrepData)
+	if err != nil {
+		t.Fatalf("AS REP Unmarshal error: %v\n", err)
+	}
+	fmt.Printf("AS REP: %+v\n\n", asRep)
+	assert.Equal(t, 5, asRep.PVNO, "PVNO not as expected")
+	assert.Equal(t, 11, asRep.MsgType, "MsgType not as expected")
+	assert.Equal(t, "JTLAN.CO.UK", asRep.CRealm, "Client Realm not as expected")
+	assert.Equal(t, 1, asRep.CName.NameType, "CName NameType not as expected")
+	assert.Equal(t, "turnerj", asRep.CName.NameString[0], "CName NameType not as expected")
+	assert.Equal(t, 19, asRep.PAData[0].PADataType, "PADataType not as expected")
+	assert.Equal(t, 5, asRep.Ticket.TktVNO, "TktVNO not as expected")
+	assert.Equal(t, "JTLAN.CO.UK", asRep.Ticket.Realm, "Ticket Realm not as expected")
+	assert.Equal(t, 2, asRep.Ticket.SName.NameType, "Ticket service nametype not as expected")
+	assert.Equal(t, "krbtgt", asRep.Ticket.SName.NameString[0], "Ticket service name string not as expected")
+	assert.Equal(t, "JTLAN.CO.UK", asRep.Ticket.SName.NameString[1], "Ticket service name string not as expected")
+	assert.Equal(t, krb5types.KrbDictionary.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.Ticket.EncPart.EType, "Etype of ticket encrypted part not as expected")
+	assert.Equal(t, 1, asRep.Ticket.EncPart.KVNO, "Ticket encrypted part KVNO not as expected")
+	assert.Equal(t, krb5types.KrbDictionary.ETypesByName["aes256-cts-hmac-sha1-96"], asRep.EncPart.EType, "Etype of encrypted part not as expected")
+	assert.Equal(t, 0, asRep.EncPart.KVNO, "Encrypted part KVNO not as expected")
+	t.Log("Finished testing unecrypted parts of AS REP")
+
+
+	kt, err := keytab.Load("/home/turnerj/tmp.keytab")
+	if err != nil {
+		fmt.Printf("keytab parse error: %v\n", err)
+	}
+	var etype krb5crypto.Aes256CtsHmacSha196
+	//Derive the key
+	key, err := etype.DeriveKey(kt.Entries[0].Key.KeyMaterial, krb5crypto.GetUsageKe(3))
+	s, err := etype.Decrypt(key, asRep.EncPart.Cipher)
+	if err != nil {
+		t.Fatalf("Error decrypting encrypted part: %v\n", err)
+	}
+	t.Logf("Decypted EncPart %+v", s)
+	s, err = etype.Decrypt(key, asRep.Ticket.EncPart.Cipher)
+	if err != nil {
+		t.Fatalf("Error decrypting ticket encrypted part: %v\n", err)
+	}
+	t.Logf("Decypted Ticket EncPart %+v", s)
+}
+