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

+ 100 - 17
krb5crypto/EncryptionEngine.go

@@ -1,24 +1,107 @@
 package krb5crypto
 
-type encryptFunc func([]byte, []byte) []byte
+import (
+	"bytes"
+	"encoding/binary"
+)
 
-func deriveRandom(key, usage []byte, n, k int, encrypt encryptFunc) {
+type EType interface {
+	GetETypeID() int
+	GetKeyByteSize() int // See protocol key format for defined values
+	StringToKey(string, salt string, s2kparams []byte) (protocolKey []byte)
+	GetDefaultStringToKeyParams() string // s2kparams
+	GetKeySeedBitLength() int // key-generation seed length, k
+	RandomToKey(b []byte) (protocolKey []byte)
+	GetHMACBitLength() int // HMAC output size, h
+	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
+	GetCypherBlockBitLength() int // cipher block size, c
+	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
+}
+type encryptFunc func([]byte, []byte) ([]byte, error)
+
+// 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
+// TODO usage - a constant
+// n - block size in bits (not bytes) - note if you use something like aes.BlockSize this is in bytes.
+// k - key length / key seed length in bits. Eg. for AES256 this value is 256
+// encrypt - the encryption function to use
+func deriveRandom(key, usage []byte, n, k int, encrypt encryptFunc) ([]byte, error) {
+	//Ensure the usage constant is at least the size of the cypher block size. Pass it through the nfold algorithm that will "stretch" it if needs be.
 	nFoldUsage := Nfold(usage, n)
-	out := make([]byte, k / 8)
-
-	fillBytes := encrypt(nFoldUsage, key)
-	p := 0
-	for i := 0; i < len(out); i++ {
-		if p < len(fillBytes) {
-			out[i] = fillBytes[p]
-			p += 1
-		} else {
-			fillBytes = encrypt(nFoldUsage, key)
-			p = 0
-			out[i] = fillBytes[p]
-			p += 1
-		}
+	//k-truncate implemented by creating a byte array the size of k (k is in bits hence /8)
+	out := make([]byte, k/8)
+
+	/*If the output	of E is shorter than k bits, it is fed back into the encryption as many times as necessary.
+	The construct is as follows (where | indicates concatentation):
+
+	K1 = E(Key, n-fold(Constant), initial-cipher-state)
+	K2 = E(Key, K1, initial-cipher-state)
+	K3 = E(Key, K2, initial-cipher-state)
+	K4 = ...
+
+	DR(Key, Constant) = k-truncate(K1 | K2 | K3 | K4 ...)*/
+	K, err := encrypt(key, nFoldUsage)
+	if err != nil {
+		return out, nil
 	}
-	return out
+	for i := copy(out, K); i < len(out); {
+		K, _ = encrypt(key, K)
+		i = i + copy(out[i:], K)
+	}
+	return out, nil
+}
+
+/*
+Key Usage Numbers
+
+RFC 3961: The "well-known constant" used for the DK function is the key usage number, expressed as four octets in big-endian order, followed by one octet indicated below.
+
+Kc = DK(base-key, usage | 0x99);
+Ke = DK(base-key, usage | 0xAA);
+Ki = DK(base-key, usage | 0x55);
+*/
+
+// un - usage number
+func GetUsageKc(un uint32) []byte {
+	return getUsage(un, 0x99)
 }
 
+// un - usage number
+func GetUsageKe(un uint32) []byte {
+	return getUsage(un, 0xAA)
+}
+
+// un - usage number
+func GetUsageKi(un uint32) []byte {
+	return getUsage(un, 0x55)
+}
+
+func getUsage(un uint32, o byte) []byte {
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, un)
+	return append(buf.Bytes(), o)
+}
+
+var KeyUsageNumbers map[int]string = map[int]string{
+	1:    "AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the client key",
+	2:    "AS-REP Ticket and TGS-REP Ticket (includes TGS session key or application session key), encrypted with the service key",
+	3:    "AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key",
+	4:    "TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS session key",
+	5:    "TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the TGS authenticator subkey",
+	6:    "TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed with the TGS session key",
+	7:    "TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes TGS authenticator subkey), encrypted with the TGS session key",
+	8:    "TGS-REP encrypted part (includes application session key), encrypted with the TGS session key",
+	9:    "TGS-REP encrypted part (includes application session key), encrypted with the TGS authenticator subkey",
+	10:   "AP-REQ Authenticator cksum, keyed with the application session key",
+	11:   "AP-REQ Authenticator (includes application authenticator subkey), encrypted with the application session key",
+	12:   "AP-REP encrypted part (includes application session subkey), encrypted with the application session key",
+	13:   "KRB-PRIV encrypted part, encrypted with a key chosen by the application",
+	14:   "KRB-CRED encrypted part, encrypted with a key chosen by the application",
+	15:   "KRB-SAFE cksum, keyed with a key chosen by the application",
+	19:   "AD-KDC-ISSUED checksum",
+	1024: "Encryption for application use in protocols that do not specify key usage values",
+}

+ 87 - 8
krb5crypto/aes256-cts-hmac-sha1-96.go

@@ -1,11 +1,17 @@
 package krb5crypto
 
 import (
+	"crypto/aes"
+	"crypto/cipher"
 	"errors"
+	"fmt"
 )
 
 const (
-
+	CipherKeyLength = 256 // AES256
+	HMACKeyLength   = 96  // SHA96
+	KeySize         = 352 // CipherKeyLength + HMACKeyLength
+	NonceSize       = aes.BlockSize
 )
 
 func StringToKey(secret string, salt string, s2kparams []byte) (protocolKey []byte) {
@@ -16,18 +22,91 @@ func RandomToKey(b []byte) (protocolKey []byte) {
 	return
 }
 
-func DeriveKey(protocolKey []byte, usage int) (specificKey []byte) {
-	return
+func DeriveKey(protocolKey, usage []byte) (specificKey []byte) {
+	e := Encrypt
+	r, _ := deriveRandom(protocolKey, usage, aes.BlockSize * 8, CipherKeyLength, e)
+	return r
 }
 
-func Encrypt(specificKey []byte, ivec []byte, plaintext []byte) (new_ivec []byte, cyphertext []byte, err error) {
-	if len(plaintext) < 1 {
-		err = errors.New("Plain text is empty")
+func Encrypt(key, message []byte) (ct []byte, err error) {
+	if len(key) != KeySize {
+		err = fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, len(key))
+		return
+	}
+	if len(message)%aes.BlockSize != 0 {
+		err = errors.New("Plaintext is not a multiple of the block size. Padding may be needed.")
+		return
+	}
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		err = fmt.Errorf("Error creating cipher: %v", err)
 		return
 	}
+
+	//RFC 3961: initial cipher state      All bits zero
+	iv := make([]byte, NonceSize)
+	//_, err = rand.Read(iv) //Not needed as all bits need to be zero
+	if err != nil {
+		err = fmt.Errorf("Error creating random nonce/initial state: %v", err)
+		return
+	}
+
+	mode := cipher.NewCBCEncrypter(block, iv)
+	mode.CryptBlocks(ct, message)
 	return
 }
 
-func Decrypt(specificKey []byte, ivec []byte, cyphertext []byte) (new_ivec []byte, plaintext []byte, err error) {
- return
+func Decrypt(key, ciphertext []byte) (message []byte, err error) {
+	if len(key) != KeySize {
+		err = fmt.Errorf("Incorrect keysize: expected: %v actual: %v", KeySize, 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)
+
+	h := hmac.New(sha1.New(), key[CipherKeyLength:])
+	ct = append(iv, ct...)
+	h.Write(ct)
+	ct = h.Sum(ct)
+	return ct[:HMACKeyLength], nil
+}*/

+ 163 - 0
krb5crypto/des3-cbc-sha1-kd.go

@@ -0,0 +1,163 @@
+package krb5crypto
+
+import (
+	"crypto/des"
+	"fmt"
+	"crypto/cipher"
+	"errors"
+	"crypto/sha1"
+	"hash"
+)
+
+//RFC: 3961 Section 6.3
+
+
+/*
+                 des3-cbc-hmac-sha1-kd, hmac-sha1-des3-kd
+              ------------------------------------------------
+              protocol key format     24 bytes, parity in low
+                                      bit of each
+
+              key-generation seed     21 bytes
+              length
+
+              hash function           SHA-1
+
+              HMAC output size        160 bits
+
+              message block size      8 bytes
+
+              default string-to-key   empty string
+              params
+
+              encryption and          triple-DES encrypt and
+              decryption functions    decrypt, in outer-CBC
+                                      mode (cipher block size
+                                      8 octets)
+
+              key generation functions:
+
+              random-to-key           DES3random-to-key (see
+                                      below)
+
+              string-to-key           DES3string-to-key (see
+                                      below)
+
+   The des3-cbc-hmac-sha1-kd encryption type is assigned the value
+   sixteen (16).  The hmac-sha1-des3-kd checksum algorithm is assigned a
+   checksum type number of twelve (12)*/
+
+type Des3CbcSha1Kd struct {
+
+}
+
+func (e *Des3CbcSha1Kd) GetETypeID() int {
+	return 16
+}
+
+func (e *Des3CbcSha1Kd) GetKeyByteSize() int {
+	return 24
+}
+
+func (e *Des3CbcSha1Kd) GetKeySeedBitLength() int {
+	return 21 * 8
+}
+
+func (e *Des3CbcSha1Kd) GetHash() hash.Hash {
+	return sha1.New()
+}
+
+func (e *Des3CbcSha1Kd) GetMessageBlockByteSize() int {
+	return 8
+}
+
+func (e *Des3CbcSha1Kd) GetDefaultStringToKeyParams() string {
+	var s string
+	return s
+}
+
+func (e *Des3CbcSha1Kd) GetConfounderByteSize() int {
+	return des.BlockSize
+}
+
+func (e *Des3CbcSha1Kd) GetHMACBitLength() int {
+	return e.GetHash().Size()
+}
+
+func (e *Des3CbcSha1Kd) GetCypherBlockBitLength() int {
+	return des.BlockSize * 8
+}
+
+func (e *Des3CbcSha1Kd) StringToKey(secret string, salt string, s2kparams string) (protocolKey []byte) {
+	return
+}
+
+func (e *Des3CbcSha1Kd) RandomToKey(b []byte) (protocolKey []byte) {
+	return
+}
+
+func (e *Des3CbcSha1Kd) DeriveRandom(protocolKey, usage []byte) ([]byte, error) {
+	r, err := deriveRandom(protocolKey, usage, e.GetCypherBlockBitLength(), e.GetKeySeedBitLength(), e.Encrypt)
+	return r, err
+}
+
+func (e *Des3CbcSha1Kd) DeriveKey(protocolKey, usage []byte) ([]byte, error) {
+	r, err := e.DeriveRandom(protocolKey, usage)
+	if err != nil {
+		return nil, err
+	}
+	return RandomToKey(r)
+}
+
+func (e *Des3CbcSha1Kd) 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) % des.BlockSize != 0 {
+		return nil, errors.New("Plaintext is not a multiple of the block size. Padding may be needed.")
+
+	}
+
+	block, err := des.NewTripleDESCipher(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
+	if err != nil {
+		return nil, fmt.Errorf("Error creating random nonce/initial state: %v", err)
+	}
+
+	ct := make([]byte, len(message))
+	mode := cipher.NewCBCEncrypter(block, iv)
+	mode.CryptBlocks(ct, message)
+	return ct, nil
+}
+
+func (e *Des3CbcSha1Kd) 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) < des.BlockSize || len(ciphertext) % des.BlockSize != 0 {
+		err = errors.New("Ciphertext is not a multiple of the block size.")
+		return
+	}
+
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		err = fmt.Errorf("Error creating cipher: %v", err)
+		return
+	}
+
+	iv := ciphertext[:e.GetConfounderByteSize()]
+	ciphertext = ciphertext[e.GetConfounderByteSize():]
+
+	mode := cipher.NewCBCDecrypter(block, iv)
+	mode.CryptBlocks(message, ciphertext)
+	return
+}

+ 38 - 0
krb5crypto/des3-cbc-sha1-kd_test.go

@@ -0,0 +1,38 @@
+package krb5crypto
+
+import (
+	"testing"
+	"github.com/stretchr/testify/assert"
+	"encoding/hex"
+	"fmt"
+)
+
+func TestDes3CbcSha1Kd_DR_DK(t *testing.T){
+	// Test vectors from RFC 3961 Appendix A3
+	var tests = []struct {
+		key string
+		usage string
+		dr string
+		dk string
+	}{
+		{"dce06b1f64c857a11c3db57c51899b2cc1791008ce973b92", "0000000155", "935079d14490a75c3093c4a6e8c3b049c71e6ee705", "925179d04591a79b5d3192c4a7e9c289b049c71f6ee604cd"},
+		{"5e13d31c70ef765746578531cb51c15bf11ca82c97cee9f2", "00000001aa", "9f58e5a047d894101c469845d67ae3c5249ed812f2", "9e58e5a146d9942a101c469845d67a20e3c4259ed913f207"},
+		{"98e6fd8a04a4b6859b75a176540b9752bad3ecd610a252bc", "0000000155", "12fff90c773f956d13fc2ca0d0840349dbd39908eb", "13fef80d763e94ec6d13fd2ca1d085070249dad39808eabf"},
+		{"622aec25a2fe2cad7094680b7c64940280084c1a7cec92b5", "00000001aa", "f8debf05b097e7dc0603686aca35d91fd9a5516a70", "f8dfbf04b097e6d9dc0702686bcb3489d91fd9a4516b703e"},
+		{"d3f8298ccb166438dcb9b93ee5a7629286a491f838f802fb", "6b65726265726f73", "2270db565d2a3d64cfbfdc5305d4f778a6de42d9da", "2370da575d2a3da864cebfdc5204d56df779a7df43d9da43"},
+		{"c1081649ada74362e6a1459d01dfd30d67c2234c940704da", "0000000155", "348056ec98fcc517171d2b4d7a9493af482d999175", "348057ec98fdc48016161c2a4c7a943e92ae492c989175f7"},
+		{"5d154af238f46713155719d55e2f1f790dd661f279a7917c", "00000001aa", "a8818bc367dadacbe9a6c84627fb60c294b01215e5", "a8808ac267dada3dcbe9a7c84626fbc761c294b01315e5c1"},
+		{"798562e049852f57dc8c343ba17f2ca1d97394efc8adc443", "0000000155", "c813f88b3be2b2f75424ce9175fbc8483b88c8713a", "c813f88a3be3b334f75425ce9175fbe3c8493b89c8703b49"},
+		{"26dce334b545292f2feab9a8701a89a4b99eb9942cecd016", "00000001aa", "f58efc6f83f93e55e695fd252cf8fe59f7d5ba37ec", "f48ffd6e83f83e7354e694fd252cf83bfe58f7d5ba37ec5d"},
+	}
+	for _, test := range tests {
+		var e Des3CbcSha1Kd
+		key, _ := hex.DecodeString(test.key)
+		usage, _ := hex.DecodeString(test.usage)
+		derivedRandom, err := e.DeriveRandom(key, usage)
+		if err != nil {
+			t.Fatal(fmt.Sprintf("Error in deriveRandom: %v", err))
+		}
+		assert.Equal(t, test.dr, hex.EncodeToString(derivedRandom), "DR not as expected")
+	}
+}