Browse Source

decrypt refactor

Jonathan Turner 9 years ago
parent
commit
be5a4d8239
5 changed files with 124 additions and 52 deletions
  1. 41 0
      crypto/EncryptionEngine.go
  2. 19 0
      keytab/keytab.go
  3. 24 32
      messages/KDCRep.go
  4. 9 20
      messages/KRBCred.go
  5. 31 0
      messages/constants.go

+ 41 - 0
crypto/EncryptionEngine.go

@@ -6,6 +6,7 @@ import (
 	"encoding/binary"
 	"errors"
 	"fmt"
+	"github.com/jcmturner/gokrb5/types"
 	"hash"
 )
 
@@ -28,6 +29,19 @@ type EType interface {
 	GetHash() hash.Hash
 }
 
+func GetEtype(id int) (EType, error) {
+	switch id {
+	case 17:
+		var et Aes128CtsHmacSha96
+		return et, nil
+	case 18:
+		var et Aes256CtsHmacSha96
+		return et, nil
+	default:
+		return nil, fmt.Errorf("Unknown or unsupported EType: %d", id)
+	}
+}
+
 // 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
@@ -112,6 +126,33 @@ func pkcs7Unpad(b []byte, m int) ([]byte, error) {
 	return b[:len(b)-n], nil
 }
 
+func DecryptEncPart(key []byte, pe types.EncryptedData, etype EType, usage uint32) ([]byte, error) {
+	//TODO move this to the a method on the Encrypted data object and call that from here. update the KRB_CRED too
+	//TODO create the etype based on the EType value in the EncPart and find the corresponding entry in the keytab
+	//Derive the key
+	//Key Usage Number: 3 - "AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key"
+	//TODO need to consider PAdata for deriving key
+	k, err := etype.DeriveKey(key, GetUsageKe(usage))
+	if err != nil {
+		return nil, fmt.Errorf("Error deriving key: %v", err)
+	}
+	// Strip off the checksum from the end
+	b, err := etype.Decrypt(k, pe.Cipher[:len(pe.Cipher)-etype.GetHMACBitLength()/8])
+	if err != nil {
+		return nil, fmt.Errorf("Error decrypting: %v", err)
+	}
+	//Verify checksum
+	if !etype.VerifyChecksum(key, pe.Cipher, b, 3) {
+		return nil, errors.New("Error decrypting encrypted part: checksum verification failed")
+	}
+	//Remove the confounder bytes
+	b = b[etype.GetConfounderByteSize():]
+	if err != nil {
+		return nil, fmt.Errorf("Error decrypting encrypted part: %v", err)
+	}
+	return b, nil
+}
+
 func GetChecksum(pt, key []byte, usage int, etype EType) ([]byte, error) {
 	k, err := etype.DeriveKey(key, GetUsageKi(uint32(usage)))
 	if err != nil {

+ 19 - 0
keytab/keytab.go

@@ -44,6 +44,25 @@ func NewKeytab() Keytab {
 	}
 }
 
+// Get the key material from the keytab for the newest entry with the required kvno, etype and matching principal
+func (kt *Keytab) GetKey(username, realm string, kvno, etype int) ([]byte, error) {
+	var key []byte
+	var t time.Time
+	for _, k := range kt.Entries {
+		if k.Principal.Realm == realm && int(k.Key.EType) == etype && (int(k.KVNO) == kvno || kvno == 0) && k.Timestamp.After(t) {
+			for _, n := range k.Principal.Components {
+				if n == username {
+					key = k.Key.KeyMaterial
+				}
+			}
+		}
+	}
+	if len(key) < 1 {
+		return key, errors.New("Matching key not found in keytab")
+	}
+	return key, nil
+}
+
 func newKeytabEntry() KeytabEntry {
 	return KeytabEntry{
 		Principal: newPrincipal(),

+ 24 - 32
messages/KDCRep.go

@@ -122,53 +122,45 @@ func (e *EncKDCRepPart) Unmarshal(b []byte) error {
 	return err
 }
 
-func decryptKDCRepEncPart(ct []byte, kt keytab.Keytab) (EncKDCRepPart, error) {
-	//TODO move this to the a method on the Encrypted data object and call that from here. update the KRB_CRED too
-	//TODO create the etype based on the EType value in the EncPart and find the corresponding entry in the keytab
-	//k.EncPart.EType
-	var etype crypto.Aes256CtsHmacSha96
-	var denc EncKDCRepPart
-	//Derive the key
-	//Key Usage Number: 3 - "AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key"
-	key, err := etype.DeriveKey(kt.Entries[0].Key.KeyMaterial, crypto.GetUsageKe(3))
+func (k *ASRep) DecryptEncPart(kt keytab.Keytab) error {
+	etype, err := crypto.GetEtype(k.EncPart.EType)
 	if err != nil {
-		return denc, fmt.Errorf("Error deriving key: %v", err)
+		return fmt.Errorf("Keytab error: %v", err)
 	}
-	// Strip off the checksum from the end
-	//TODO should this check be moved to the Decrypt method? No as makes it hard to test
-	b, err := etype.Decrypt(key, ct[:len(ct)-etype.GetHMACBitLength()/8])
+	key, err := kt.GetKey(k.CName.NameString[0], k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
 	if err != nil {
-		return denc, fmt.Errorf("Error decrypting: %v", err)
-	}
-	//Verify checksum
-	if !etype.VerifyChecksum(kt.Entries[0].Key.KeyMaterial, ct, b, 3) {
-		return denc, errors.New("Error decrypting encrypted part: checksum verification failed")
+		return fmt.Errorf("Could not get key from keytab: %v", err)
 	}
-	//Remove the confounder bytes
-	b = b[etype.GetConfounderByteSize():]
+	b, err := crypto.DecryptEncPart(key, k.EncPart, etype, USAGE_AS_REP_ENCPART)
 	if err != nil {
-		return denc, fmt.Errorf("Error decrypting encrypted part: %v", err)
+		return fmt.Errorf("Error decrypting KDC_REP EncPart: %v", err)
 	}
+	var denc EncKDCRepPart
 	err = denc.Unmarshal(b)
 	if err != nil {
-		return denc, fmt.Errorf("Error unmarshalling encrypted part: %v", err)
-	}
-	return denc, nil
-}
-
-func (k *ASRep) DecryptEncPart(kt keytab.Keytab) error {
-	denc, err := decryptKDCRepEncPart(k.EncPart.Cipher, kt)
-	if err != nil {
-		return err
+		return fmt.Errorf("Error unmarshalling encrypted part: %v", err)
 	}
 	k.DecryptedEncPart = denc
 	return nil
 }
 
 func (k *TGSRep) DecryptEncPart(kt keytab.Keytab) error {
-	denc, err := decryptKDCRepEncPart(k.EncPart.Cipher, kt)
+	etype, err := crypto.GetEtype(k.EncPart.EType)
 	if err != nil {
-		return err
+		return fmt.Errorf("Keytab error: %v", err)
+	}
+	key, err := kt.GetKey(k.CName.NameString[0], k.CRealm, k.EncPart.KVNO, k.EncPart.EType)
+	if err != nil {
+		return fmt.Errorf("Could not get key from keytab: %v", err)
+	}
+	b, err := crypto.DecryptEncPart(key, k.EncPart, etype, USAGE_AS_REP_ENCPART)
+	if err != nil {
+		return fmt.Errorf("Error decrypting KDC_REP EncPart: %v", err)
+	}
+	var denc EncKDCRepPart
+	err = denc.Unmarshal(b)
+	if err != nil {
+		return fmt.Errorf("Error unmarshalling encrypted part: %v", err)
 	}
 	k.DecryptedEncPart = denc
 	return nil

+ 9 - 20
messages/KRBCred.go

@@ -1,11 +1,9 @@
 package messages
 
 import (
-	"errors"
 	"fmt"
 	"github.com/jcmturner/asn1"
 	"github.com/jcmturner/gokrb5/crypto"
-	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/types"
 	"github.com/jcmturner/gokrb5/types/asnAppTag"
 	"time"
@@ -71,30 +69,21 @@ func (k *KRBCred) Unmarshal(b []byte) error {
 	return nil
 }
 
-func (k *KRBCred) DecryptEncPart(kt keytab.Keytab) error {
-	//TODO move this to the a method on the Encrypted data object and call that from here. update the KDCRep too
-	//TODO create the etype based on the EType value in the EncPart and find the corresponding entry in the keytab
-	//k.EncPart.EType
-	var etype crypto.Aes256CtsHmacSha96
-	//Derive the key
-	//Key Usage Number: 3 - "AS-REP encrypted part (includes TGS session key or application session key), encrypted with the client key"
-	key, err := etype.DeriveKey(kt.Entries[0].Key.KeyMaterial, crypto.GetUsageKe(3))
-	// Strip off the checksum from the end
-	//TODO should this check be moved to the Decrypt method?
-	b, err := etype.Decrypt(key, k.EncPart.Cipher[:len(k.EncPart.Cipher)-etype.GetHMACBitLength()/8])
-	//Verify checksum
-	if !etype.VerifyChecksum(kt.Entries[0].Key.KeyMaterial, k.EncPart.Cipher, b, 3) {
-		return errors.New("Error decrypting encrypted part: checksum verification failed")
+func (k *KRBCred) DecryptEncPart(key []byte) error {
+	etype, err := crypto.GetEtype(k.EncPart.EType)
+	if err != nil {
+		return fmt.Errorf("Keytab error: %v", err)
 	}
-	//Remove the confounder bytes
-	b = b[etype.GetConfounderByteSize():]
+	b, err := crypto.DecryptEncPart(key, k.EncPart, etype, USAGE_KRB_CRED_ENCPART)
 	if err != nil {
-		return fmt.Errorf("Error decrypting encrypted part: %v", err)
+		return fmt.Errorf("Error decrypting KDC_REP EncPart: %v", err)
 	}
-	err = k.DecryptedEncPart.Unmarshal(b)
+	var denc EncKrbCredPart
+	err = denc.Unmarshal(b)
 	if err != nil {
 		return fmt.Errorf("Error unmarshalling encrypted part: %v", err)
 	}
+	k.DecryptedEncPart = denc
 	return nil
 }
 

+ 31 - 0
messages/constants.go

@@ -0,0 +1,31 @@
+package messages
+
+const (
+	USAGE_AS_REQ_PA_ENC_TIMESTAMP                        = 1
+	USAGE_KDC_REP_TICKET                                 = 2
+	USAGE_AS_REP_ENCPART                                 = 3
+	USAGE_TGS_REQ_KDC_REQ_BODY_AUTHDATA_SESSION_KEY      = 4
+	USAGE_TGS_REQ_KDC_REQ_BODY_AUTHDATA_SUB_KEY          = 5
+	USAGE_TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR_CHKSUM = 6
+	USAGE_TGS_REQ_PA_TGS_REQ_AP_REQ_AUTHENTICATOR        = 7
+	USAGE_TGS_REP_ENCPART_SESSION_KEY                    = 8
+	USAGE_TGS_REP_ENCPART_AUTHENTICATOR_SUB_KEY          = 9
+	USAGE_AP_REQ_AUTHENTICATOR_CHKSUM                    = 10
+	USAGE_AP_REQ_AUTHENTICATOR                           = 11
+	USAGE_AP_REP_ENCPART                                 = 12
+	USAGE_KRB_PRIV_ENCPART                               = 13
+	USAGE_KRB_CRED_ENCPART                               = 14
+	USAGE_KRB_SAFE_CHKSUM                                = 15
+	//16-18.  Reserved for future use in Kerberos and related protocols.
+	USAGE_AD_KDC_ISSUED_CHKSUM = 19
+	//20-21.  Reserved for future use in Kerberos and related protocols.
+	USAGE_GSSAPI_ACCEPTOR_SEAL  = 22
+	USAGE_GSSAPI_ACCEPTOR_SIGN  = 23
+	USAGE_GSSAPI_INITIATOR_SEAL = 24
+	USAGE_GSSAPI_INITIATOR_SIGN = 25
+	//26-511.  Reserved for future use in Kerberos and related protocols.
+	//512-1023.  Reserved for uses internal to a Kerberos implementation.
+	//1024.  Encryption for application use in protocols that do not specify key usage values
+	//1025.  Checksums for application use in protocols that do not specify key usage values
+	//1026-2047.  Reserved for application use.
+)