Просмотр исходного кода

des3-cbc-sha1-kd implementation

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

+ 1 - 1
README.md

@@ -17,6 +17,7 @@ The currently implemented encryption types are limited to:
 
 | Implementation | Encryption ID | Checksum ID | RFC |
 |-------|-------------|------------|------|
+| des3-cbc-sha1-kd | 16 | 12 | 3961 |
 | aes128-cts-hmac-sha1-96 | 17 | 15 | 3962 |
 | aes256-cts-hmac-sha1-96 | 18 | 16 | 3962 |
 | aes128-cts-hmac-sha256-128 | 19 | 19 | 8009 |
@@ -173,7 +174,6 @@ if ok, creds, err := serivce.ValidateAPREQ(mt.APReq, kt, r.RemoteAddr); ok {
 ---
 
 ## References
-### RFCs
 * RFC 4120 The Kerberos Network Authentication Service (V5)
 [text](https://www.ietf.org/rfc/rfc4120.txt) [html](https://tools.ietf.org/html/rfc4120)
 * RFC 3961 Encryption and Checksum Specifications for Kerberos 5

+ 24 - 6
client/client_integration_test.go

@@ -6,6 +6,7 @@ package client
 import (
 	"encoding/hex"
 	"github.com/jcmturner/gokrb5/config"
+	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/testdata"
 	"github.com/stretchr/testify/assert"
@@ -53,14 +54,31 @@ func TestClient_SuccessfulLogin_OlderKDC(t *testing.T) {
 	}
 }
 
-func TestClient_SuccessfulLogin_ETYPE_19(t *testing.T) {
+func TestClient_SuccessfulLogin_ETYPE_DES3_CBC_SHA1_KD(t *testing.T) {
+	b, err := hex.DecodeString(testdata.TESTUSER1_KEYTAB)
+	kt, _ := keytab.Parse(b)
+	c, _ := config.NewConfigFromString(testdata.TEST_KRB5CONF)
+	c.LibDefaults.Default_tkt_enctypes = []string{"des3-cbc-sha1-kd"}
+	c.LibDefaults.Default_tkt_enctype_ids = []int{etypeID.DES3_CBC_SHA1_KD}
+	c.LibDefaults.Default_tgs_enctypes = []string{"des3-cbc-sha1-kd"}
+	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.DES3_CBC_SHA1_KD}
+	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_AES128_CTS_HMAC_SHA256_128(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_tkt_enctype_ids = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
 	c.LibDefaults.Default_tgs_enctypes = []string{"aes128-cts-hmac-sha256-128"}
-	c.LibDefaults.Default_tgs_enctype_ids = []int{19}
+	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.AES128_CTS_HMAC_SHA256_128}
 	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
 	cl.WithConfig(c)
 
@@ -70,14 +88,14 @@ func TestClient_SuccessfulLogin_ETYPE_19(t *testing.T) {
 	}
 }
 
-func TestClient_SuccessfulLogin_ETYPE_20(t *testing.T) {
+func TestClient_SuccessfulLogin_ETYPE_AES256_CTS_HMAC_SHA384_192(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_tkt_enctype_ids = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
 	c.LibDefaults.Default_tgs_enctypes = []string{"aes256-cts-hmac-sha384-192"}
-	c.LibDefaults.Default_tgs_enctype_ids = []int{20}
+	c.LibDefaults.Default_tgs_enctype_ids = []int{etypeID.AES256_CTS_HMAC_SHA384_192}
 	cl := NewClientWithKeytab("testuser1", "TEST.GOKRB5", kt)
 	cl.WithConfig(c)
 

+ 5 - 4
crypto/aes128-cts-hmac-sha1-96.go

@@ -6,6 +6,7 @@ import (
 	"crypto/sha1"
 	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
+	"github.com/jcmturner/gokrb5/crypto/rfc3962"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -109,19 +110,19 @@ func (e Aes128CtsHmacSha96) RandomToKey(b []byte) []byte {
 }
 
 func (e Aes128CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
-	return rfc3961.EncryptData(key, data, e)
+	return rfc3962.EncryptData(key, data, e)
 }
 
 func (e Aes128CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
-	return rfc3961.EncryptMessage(key, message, usage, e)
+	return rfc3962.EncryptMessage(key, message, usage, e)
 }
 
 func (e Aes128CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
-	return rfc3961.DecryptData(key, data, e)
+	return rfc3962.DecryptData(key, data, e)
 }
 
 func (e Aes128CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
-	return rfc3961.DecryptMessage(key, ciphertext, usage, e)
+	return rfc3962.DecryptMessage(key, ciphertext, usage, e)
 }
 
 func (e Aes128CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {

+ 5 - 4
crypto/aes256-cts-hmac-sha1-96.go

@@ -6,6 +6,7 @@ import (
 	"crypto/sha1"
 	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
+	"github.com/jcmturner/gokrb5/crypto/rfc3962"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
 	"github.com/jcmturner/gokrb5/iana/etypeID"
 	"hash"
@@ -109,19 +110,19 @@ func (e Aes256CtsHmacSha96) RandomToKey(b []byte) []byte {
 }
 
 func (e Aes256CtsHmacSha96) EncryptData(key, data []byte) ([]byte, []byte, error) {
-	return rfc3961.EncryptData(key, data, e)
+	return rfc3962.EncryptData(key, data, e)
 }
 
 func (e Aes256CtsHmacSha96) EncryptMessage(key, message []byte, usage uint32) ([]byte, []byte, error) {
-	return rfc3961.EncryptMessage(key, message, usage, e)
+	return rfc3962.EncryptMessage(key, message, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) DecryptData(key, data []byte) ([]byte, error) {
-	return rfc3961.DecryptData(key, data, e)
+	return rfc3962.DecryptData(key, data, e)
 }
 
 func (e Aes256CtsHmacSha96) DecryptMessage(key, ciphertext []byte, usage uint32) ([]byte, error) {
-	return rfc3961.DecryptMessage(key, ciphertext, usage, e)
+	return rfc3962.DecryptMessage(key, ciphertext, usage, e)
 }
 
 func (e Aes256CtsHmacSha96) DeriveKey(protocolKey, usage []byte) ([]byte, error) {

+ 6 - 0
crypto/crypto.go

@@ -25,6 +25,9 @@ func GetEtype(id int) (etype.EType, error) {
 	case etypeID.AES256_CTS_HMAC_SHA384_192:
 		var et Aes256CtsHmacSha384192
 		return et, nil
+	case etypeID.DES3_CBC_SHA1_KD:
+		var et Des3CbcSha1Kd
+		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported EType: %d", id)
 	}
@@ -44,6 +47,9 @@ func GetChksumEtype(id int) (etype.EType, error) {
 	case chksumtype.HMAC_SHA384_192_AES256:
 		var et Aes256CtsHmacSha384192
 		return et, nil
+	case chksumtype.HMAC_SHA1_DES3_KD:
+		var et Des3CbcSha1Kd
+		return et, nil
 	default:
 		return nil, fmt.Errorf("Unknown or unsupported checksum type: %d", id)
 	}

+ 12 - 75
crypto/des3-cbc-sha1-kd.go

@@ -1,13 +1,10 @@
 package crypto
 
 import (
-	"crypto/cipher"
 	"crypto/des"
 	"crypto/hmac"
-	"crypto/rand"
 	"crypto/sha1"
 	"errors"
-	"fmt"
 	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/rfc3961"
 	"github.com/jcmturner/gokrb5/iana/chksumtype"
@@ -90,21 +87,22 @@ func (e Des3CbcSha1Kd) GetConfounderByteSize() int {
 }
 
 func (e Des3CbcSha1Kd) GetHMACBitLength() int {
-	return e.GetHashFunc()().Size()
+	return e.GetHashFunc()().Size() * 8
 }
 
 func (e Des3CbcSha1Kd) GetCypherBlockBitLength() int {
 	return des.BlockSize * 8
 }
 
-func (e Des3CbcSha1Kd) StringToKey(secret string, salt string, s2kparams string) (protocolKey []byte, err error) {
-	//TODO
-	return
+func (e Des3CbcSha1Kd) StringToKey(secret string, salt string, s2kparams string) ([]byte, error) {
+	if s2kparams != "" {
+		return []byte{}, errors.New("s2kparams must be an empty string")
+	}
+	return rfc3961.DES3StringToKey(secret, salt, e)
 }
 
-func (e Des3CbcSha1Kd) RandomToKey(b []byte) (protocolKey []byte) {
-	//TODO
-	return
+func (e Des3CbcSha1Kd) RandomToKey(b []byte) []byte {
+	return rfc3961.DES3RandomToKey(b)
 }
 
 func (e Des3CbcSha1Kd) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
@@ -121,80 +119,19 @@ func (e Des3CbcSha1Kd) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
 }
 
 func (e Des3CbcSha1Kd) EncryptData(key, data []byte) ([]byte, []byte, error) {
-	if len(key) != e.GetKeyByteSize() {
-		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
-
-	}
-	data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize())
-
-	block, err := des.NewTripleDESCipher(key)
-	if err != nil {
-		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
-	}
-
-	//RFC 3961: initial cipher state      All bits zero
-	ivz := make([]byte, e.GetConfounderByteSize())
-
-	ct := make([]byte, len(data))
-	mode := cipher.NewCBCEncrypter(block, ivz)
-	mode.CryptBlocks(ct, data)
-	return ivz, ct, nil
+	return rfc3961.DES3EncryptData(key, data, e)
 }
 
 func (e Des3CbcSha1Kd) 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...)
-
-	iv, b, err := e.EncryptData(key, plainBytes)
-	if err != nil {
-		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
-	}
-
-	// Generate and append integrity hash
-	ih, err := common.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.DES3EncryptMessage(key, message, usage, e)
 }
 
 func (e Des3CbcSha1Kd) DecryptData(key, data []byte) ([]byte, error) {
-	if len(key) != e.GetKeySeedBitLength() {
-		return []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeySeedBitLength(), len(key))
-	}
-
-	if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 {
-		return []byte{}, errors.New("Ciphertext is not a multiple of the block size.")
-	}
-	block, err := des.NewTripleDESCipher(key)
-	if err != nil {
-		return []byte{}, fmt.Errorf("Error creating cipher: %v", err)
-	}
-	pt := make([]byte, len(data))
-	ivz := make([]byte, e.GetConfounderByteSize())
-	mode := cipher.NewCBCDecrypter(block, ivz)
-	mode.CryptBlocks(pt, data)
-	return pt, nil
+	return rfc3961.DES3DecryptData(key, data, e)
 }
 
 func (e Des3CbcSha1Kd) DecryptMessage(key, ciphertext []byte, usage uint32) (message []byte, err error) {
-	// Strip off the checksum from the end
-	b, err := e.DecryptData(key, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
-	if err != nil {
-		return nil, fmt.Errorf("Error decrypting: %v", err)
-	}
-	//Verify checksum
-	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
-		return nil, errors.New("Error decrypting: integrity verification failed")
-	}
-	//Remove the confounder bytes
-	return b[e.GetConfounderByteSize():], nil
+	return rfc3961.DES3DecryptMessage(key, ciphertext, usage, e)
 }
 
 func (e Des3CbcSha1Kd) VerifyIntegrity(protocolKey, ct, pt []byte, usage uint32) bool {

+ 27 - 0
crypto/des3-cbc-sha1-kd_test.go

@@ -34,5 +34,32 @@ func TestDes3CbcSha1Kd_DR_DK(t *testing.T) {
 			t.Fatal(fmt.Sprintf("Error in deriveRandom: %v", err))
 		}
 		assert.Equal(t, test.dr, hex.EncodeToString(derivedRandom), "DR not as expected")
+		derivedKey, err := e.DeriveKey(key, usage)
+		if err != nil {
+			t.Fatal(fmt.Sprintf("Error in deriveKey: %v", err))
+		}
+		assert.Equal(t, test.dk, hex.EncodeToString(derivedKey), "DK not as expected")
+	}
+}
+
+func TestDes3CbcSha1Kd_StringToKey(t *testing.T) {
+	var tests = []struct {
+		salt   string
+		secret string
+		key    string
+	}{
+		{"ATHENA.MIT.EDUraeburn", "password", "850bb51358548cd05e86768c313e3bfef7511937dcf72c3e"},
+		{"WHITEHOUSE.GOVdanny", "potatoe", "dfcd233dd0a43204ea6dc437fb15e061b02979c1f74f377a"},
+		{"EXAMPLE.COMbuckaroo", "penny", "6d2fcdf2d6fbbc3ddcadb5da5710a23489b0d3b69d5d9d4a"},
+		{"ATHENA.MIT.EDUJuri" + "\u0161" + "i" + "\u0107", "\u00DF", "16d5a40e1ce3bacb61b9dce00470324c831973a7b952feb0"},
+		{"EXAMPLE.COMpianist", "𝄞", "85763726585dbc1cce6ec43e1f751f07f1c4cbb098f40b19"},
+	}
+	var e Des3CbcSha1Kd
+	for _, test := range tests {
+		key, err := e.StringToKey(test.secret, test.salt, "")
+		if err != nil {
+			t.Errorf("Error in StringToKey: %v", err)
+		}
+		assert.Equal(t, test.key, hex.EncodeToString(key), "StringToKey not as expected")
 	}
 }

+ 37 - 17
crypto/rfc3961/encryption.go

@@ -2,28 +2,38 @@
 package rfc3961
 
 import (
-	"crypto/aes"
+	"crypto/cipher"
+	"crypto/des"
 	"crypto/hmac"
 	"crypto/rand"
 	"errors"
 	"fmt"
-	"github.com/jcmturner/gokrb5/crypto/aescts"
 	"github.com/jcmturner/gokrb5/crypto/common"
 	"github.com/jcmturner/gokrb5/crypto/etype"
 )
 
-func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
+func DES3EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
 	if len(key) != e.GetKeyByteSize() {
-		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+		return nil, nil, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+
 	}
-	ivz := make([]byte, aes.BlockSize)
-	return aescts.Encrypt(key, ivz, data)
-}
+	data, _ = common.ZeroPad(data, e.GetMessageBlockByteSize())
 
-func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
-	if len(key) != e.GetKeyByteSize() {
-		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return nil, nil, fmt.Errorf("Error creating cipher: %v", err)
 	}
+
+	//RFC 3961: initial cipher state      All bits zero
+	ivz := make([]byte, des.BlockSize)
+
+	ct := make([]byte, len(data))
+	mode := cipher.NewCBCEncrypter(block, ivz)
+	mode.CryptBlocks(ct, data)
+	return ivz, ct, nil
+}
+
+func DES3EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
 	//confounder
 	c := make([]byte, e.GetConfounderByteSize())
 	_, err := rand.Read(c)
@@ -41,7 +51,6 @@ func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, [
 		}
 	}
 
-	// Encrypt the data
 	iv, b, err := e.EncryptData(k, plainBytes)
 	if err != nil {
 		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
@@ -56,15 +65,26 @@ func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, [
 	return iv, b, nil
 }
 
-func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
+func DES3DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
 	if len(key) != e.GetKeyByteSize() {
 		return []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
 	}
-	ivz := make([]byte, aes.BlockSize)
-	return aescts.Decrypt(key, ivz, data)
+
+	if len(data) < des.BlockSize || len(data)%des.BlockSize != 0 {
+		return []byte{}, errors.New("Ciphertext is not a multiple of the block size.")
+	}
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return []byte{}, fmt.Errorf("Error creating cipher: %v", err)
+	}
+	pt := make([]byte, len(data))
+	ivz := make([]byte, des.BlockSize)
+	mode := cipher.NewCBCDecrypter(block, ivz)
+	mode.CryptBlocks(pt, data)
+	return pt, nil
 }
 
-func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
+func DES3DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
 	//Derive the key
 	k, err := e.DeriveKey(key, common.GetUsageKe(usage))
 	if err != nil {
@@ -73,11 +93,11 @@ func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte
 	// Strip off the checksum from the end
 	b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
 	if err != nil {
-		return nil, err
+		return nil, fmt.Errorf("Error decrypting: %v", err)
 	}
 	//Verify checksum
 	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
-		return nil, errors.New("Integrity verification failed")
+		return nil, errors.New("Error decrypting: integrity verification failed")
 	}
 	//Remove the confounder bytes
 	return b[e.GetConfounderByteSize():], nil

+ 89 - 9
crypto/rfc3961/keyDerivation.go

@@ -1,13 +1,16 @@
 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 (
-	prfconstant = "prf"
+	prfconstant   = "prf"
+	s2kParamsZero = 4294967296
 )
 
 // RFC 3961: DR(Key, Constant) = k-truncate(E(Key, Constant, initial-cipher-state)).
@@ -50,19 +53,34 @@ func DeriveRandom(key, usage []byte, e etype.EType) ([]byte, error) {
 }
 
 func DeriveKey(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
-	r, err := DeriveRandom(protocolKey, usage, e)
+	r, err := e.DeriveRandom(protocolKey, usage)
 	if err != nil {
 		return nil, err
 	}
-	return RandomToKey(r), nil
+	return e.RandomToKey(r), nil
 }
 
 func RandomToKey(b []byte) []byte {
 	return b
 }
 
+func DES3RandomToKey(b []byte) []byte {
+	r := stretch56Bits(b[:7])
+	r2 := stretch56Bits(b[7:14])
+	r = append(r, r2...)
+	r3 := stretch56Bits(b[14:21])
+	r = append(r, r3...)
+	return r
+}
+
+func DES3StringToKey(secret, salt string, e etype.EType) ([]byte, error) {
+	s := secret + salt
+	tkey := e.RandomToKey(Nfold([]byte(s), e.GetKeySeedBitLength()))
+	return e.DeriveKey(tkey, []byte("kerberos"))
+}
+
 func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
-	i, err := rfc3962.S2KparamsToItertions(s2kparams)
+	i, err := S2KparamsToItertions(s2kparams)
 	if err != nil {
 		return nil, err
 	}
@@ -74,21 +92,83 @@ func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
 }
 
 func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
-	tkey := RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
-	return DeriveKey(tkey, []byte("kerberos"), e)
+	tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
+	return e.DeriveKey(tkey, []byte("kerberos"))
 }
 
 func PseudoRandom(key, b []byte, e etype.EType) ([]byte, error) {
 	h := e.GetHashFunc()()
 	h.Write(b)
 	tmp := h.Sum(nil)[:e.GetMessageBlockByteSize()]
-	k, err := DeriveKey(key, []byte(prfconstant), e)
+	k, err := e.DeriveKey(key, []byte(prfconstant))
 	if err != nil {
 		return []byte{}, err
 	}
-	_, prf, err := EncryptData(k, tmp, e)
+	_, prf, err := e.EncryptData(k, tmp)
 	if err != nil {
 		return []byte{}, err
 	}
 	return prf, nil
 }
+
+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 stretch56Bits(b []byte) []byte {
+	d := make([]byte, len(b), len(b))
+	copy(d, b)
+	var lb byte
+	for i, v := range d {
+		bv, nb := calcEvenParity(v)
+		d[i] = nb
+		if bv != 0 {
+			lb = lb | (1 << uint(i+1))
+		} else {
+			lb = lb &^ (1 << uint(i+1))
+		}
+	}
+	_, lb = calcEvenParity(lb)
+	d = append(d, lb)
+	return d
+}
+
+func calcEvenParity(b byte) (uint8, uint8) {
+	lowestbit := b & 0x01
+	// c counter of 1s in the first 7 bits of the byte
+	var c int
+	// Iterate over the highest 7 bits (hence p starts at 1 not zero) and count the 1s.
+	for p := 1; p < 8; p++ {
+		v := b & (1 << uint(p))
+		if v != 0 {
+			c += 1
+		}
+	}
+	if c%2 == 0 {
+		//Even number of 1s so set parity to 1
+		b = b | 1
+	} else {
+		//Odd number of 1s so set parity to 0
+		b = b &^ 1
+	}
+	return lowestbit, b
+}

+ 81 - 0
crypto/rfc3962/encryption.go

@@ -0,0 +1,81 @@
+package rfc3962
+
+import (
+	"crypto/rand"
+	"errors"
+	"fmt"
+	"github.com/jcmturner/gokrb5/crypto/aescts"
+	"github.com/jcmturner/gokrb5/crypto/common"
+	"github.com/jcmturner/gokrb5/crypto/etype"
+)
+
+func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	ivz := make([]byte, e.GetCypherBlockBitLength()/8)
+	return aescts.Encrypt(key, ivz, data)
+}
+
+func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	//confounder
+	c := make([]byte, e.GetConfounderByteSize())
+	_, err := rand.Read(c)
+	if err != nil {
+		return []byte{}, []byte{}, fmt.Errorf("Could not generate random confounder: %v", err)
+	}
+	plainBytes := append(c, message...)
+
+	// Derive key for encryption from usage
+	var k []byte
+	if usage != 0 {
+		k, err = e.DeriveKey(key, 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)
+	}
+
+	// Generate and append integrity hash
+	ih, err := common.GetIntegrityHash(plainBytes, key, usage, e)
+	if err != nil {
+		return iv, b, fmt.Errorf("Error encrypting data: %v", err)
+	}
+	b = append(b, ih...)
+	return iv, b, nil
+}
+
+func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
+	if len(key) != e.GetKeyByteSize() {
+		return []byte{}, fmt.Errorf("Incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
+	}
+	ivz := make([]byte, e.GetCypherBlockBitLength()/8)
+	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
+}

+ 0 - 35
crypto/rfc3962/keyDerivation.go

@@ -1,35 +0,0 @@
-// 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
-}

+ 5 - 5
crypto/rfc8009/keyDerivation.go

@@ -60,7 +60,7 @@ func DeriveKey(protocolKey, label []byte, e etype.EType) []byte {
 	if kl == 0 {
 		kl = e.GetKeySeedBitLength()
 	}
-	return RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e))
+	return e.RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e))
 }
 
 func RandomToKey(b []byte) []byte {
@@ -72,12 +72,12 @@ func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error)
 	if err != nil {
 		return nil, err
 	}
-	return StringToKeyIter(secret, salt, int(i), e), nil
+	return StringToKeyIter(secret, salt, int(i), e)
 }
 
-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 StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
+	tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
+	return e.DeriveKey(tkey, []byte("kerberos"))
 }
 
 func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {

+ 3 - 3
testdata/test_vectors.go

@@ -109,10 +109,10 @@ var TestVectors = map[string]string{
 }
 
 const (
-	TESTUSER1_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0100110010698c4df8e9f60e7eea5a21bf4526ad25000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0200110010698c4df8e9f60e7eea5a21bf4526ad25000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba0200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000020000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba01001300102eb8501967a7886e1f0c63ac9be8c4a0000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba02001300102eb8501967a7886e1f0c63ac9be8c4a0000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba01001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592c05ba02001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee00000002"
+	TESTUSER1_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723100000001592d16240100110010698c4df8e9f60e7eea5a21bf4526ad25000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592d16240100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592d16240200110010698c4df8e9f60e7eea5a21bf4526ad25000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592d16240200120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de9000000020000003b0001000b544553542e474f4b524235000974657374757365723100000001592d162401001300102eb8501967a7886e1f0c63ac9be8c4a0000000010000003b0001000b544553542e474f4b524235000974657374757365723100000001592d162402001300102eb8501967a7886e1f0c63ac9be8c4a0000000020000004b0001000b544553542e474f4b524235000974657374757365723100000001592d162401001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee000000010000004b0001000b544553542e474f4b524235000974657374757365723100000001592d162402001400208ad66f209bb07daa186f8a229830f5ba06a3a2a33638f4ec66e1d29324e417ee00000002000000430001000b544553542e474f4b524235000974657374757365723100000001592d162401001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e300000001000000430001000b544553542e474f4b524235000974657374757365723100000001592d162402001000184580fb91760dabe6f808c22c26494f644cb35d61d32c79e300000002"
 	TESTUSER1_WRONGPASSWD = "0502000000370001000b544553542e474f4b52423500097465737475736572310000000158ef4bc5010011001039a9a382153105f8708e80f93382654e000000470001000b544553542e474f4b52423500097465737475736572310000000158ef4bc60100120020fc5bb940d6075214e0c6fc0456ce68c33306094198a927b4187d7cf3f4aea50d"
-	TESTUSER2_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723200000001592c0644010011001086824c55ff5de30386dd83dc62b44bb7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592c06440100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592c0644020011001086824c55ff5de30386dd83dc62b44bb7000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592c06440200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000020000003b0001000b544553542e474f4b524235000974657374757365723200000001592c064401001300106ccff358aaa8a4a41c444e173b1463c2000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592c064402001300106ccff358aaa8a4a41c444e173b1463c2000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592c064401001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592c064402001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd700000002"
-	TESTUSER3_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d01001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0200110010220789f5cf68e44a852c73b1e3729efc000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d02001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000020000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0100130010d2509e2bf9c08f73aafe08745c85e8fd000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592c069d0200130010d2509e2bf9c08f73aafe08745c85e8fd000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d010014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592c069d020014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e00000002"
+	TESTUSER2_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723200000001592d166f010011001086824c55ff5de30386dd83dc62b44bb7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592d166f0100120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592d166f020011001086824c55ff5de30386dd83dc62b44bb7000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592d166f0200120020d8ed27f96be76fd5b281ee9f8029db93cc5fb06c7eb3be9ee753106d3488fa92000000020000003b0001000b544553542e474f4b524235000974657374757365723200000001592d166f01001300106ccff358aaa8a4a41c444e173b1463c2000000010000003b0001000b544553542e474f4b524235000974657374757365723200000001592d166f02001300106ccff358aaa8a4a41c444e173b1463c2000000020000004b0001000b544553542e474f4b524235000974657374757365723200000001592d166f01001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd7000000010000004b0001000b544553542e474f4b524235000974657374757365723200000001592d166f02001400205cf3773dd920be800229ac1c6f9bf59c6706c583f82c2dea66c9a29152118cd700000002000000430001000b544553542e474f4b524235000974657374757365723200000001592d166f0100100018bc025746e9e66bd6b62a918f6413d529803192a28aabf79200000001000000430001000b544553542e474f4b524235000974657374757365723200000001592d166f0200100018bc025746e9e66bd6b62a918f6413d529803192a28aabf79200000002"
+	TESTUSER3_KEYTAB      = "05020000003b0001000b544553542e474f4b524235000974657374757365723300000001592d16c30100110010220789f5cf68e44a852c73b1e3729efc000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592d16c301001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592d16c30200110010220789f5cf68e44a852c73b1e3729efc000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592d16c302001200205e1b7287f78f3f88c9eefe92c08ec1613f35d72bc6ee2c2e1c5cb6fb9d8b0580000000020000003b0001000b544553542e474f4b524235000974657374757365723300000001592d16c30100130010d2509e2bf9c08f73aafe08745c85e8fd000000010000003b0001000b544553542e474f4b524235000974657374757365723300000001592d16c30200130010d2509e2bf9c08f73aafe08745c85e8fd000000020000004b0001000b544553542e474f4b524235000974657374757365723300000001592d16c3010014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e000000010000004b0001000b544553542e474f4b524235000974657374757365723300000001592d16c3020014002060e5e3fb9166db825eb3e4ff4bd1f4307e3f5fe85cdf7faccf8d65330557f74e00000002000000430001000b544553542e474f4b524235000974657374757365723300000001592d16c301001000182cad768989a125fd26f24adf671976456d8097548c4c83f100000001000000430001000b544553542e474f4b524235000974657374757365723300000001592d16c302001000182cad768989a125fd26f24adf671976456d8097548c4c83f100000002"
 	HTTP_KEYTAB           = "0502000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc010011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc01001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51000000440002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc020011001057a7754c70c4d85c155c718c2f1292b0000000540002000b544553542e474f4b5242350004485454500010686f73742e746573742e676f6b72623500000001590dc4dc02001200209cad00bbc72d703258e911dc18e6d5487cf737bf67fd111f0c2463ad6033bf51"
 	SYSHTTP_KEYTAB        = "0502000000450001000b544553542e474f4b52423500077379734854545000000001590dc5af020012002043763702868978d1b6d91a36704b987e27e517250055bdfc40b8a6b3848d9aae"
 	TEST_AS_REQ           = "6a81a63081a3a103020105a20302010aa30e300c300aa10402020095a2020400a48186308183a00703050040000010a1163014a003020101a10d300b1b09746573747573657231a20d1b0b544553542e474f4b524235a320301ea003020102a11730151b066b72627467741b0b544553542e474f4b524235a511180f32303137303232303134323530315aa70602040f6755a6a814301202011202011102011002011702011902011a"

BIN
testenv/testuser1.testtab


BIN
testenv/testuser2.testtab


BIN
testenv/testuser3.testtab