github.com/jcmturner/gokrb5/v8@v8.4.4/crypto/rfc8009/encryption.go (about)

     1  // Package rfc8009 provides encryption and checksum methods as specified in RFC 8009
     2  package rfc8009
     3  
     4  import (
     5  	"crypto/aes"
     6  	"crypto/hmac"
     7  	"crypto/rand"
     8  	"errors"
     9  	"fmt"
    10  
    11  	"github.com/jcmturner/aescts/v2"
    12  	"github.com/jcmturner/gokrb5/v8/crypto/common"
    13  	"github.com/jcmturner/gokrb5/v8/crypto/etype"
    14  	"github.com/jcmturner/gokrb5/v8/iana/etypeID"
    15  )
    16  
    17  // EncryptData encrypts the data provided using methods specific to the etype provided as defined in RFC 8009.
    18  func EncryptData(key, data []byte, e etype.EType) ([]byte, []byte, error) {
    19  	kl := e.GetKeyByteSize()
    20  	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
    21  		kl = 32
    22  	}
    23  	if len(key) != kl {
    24  		return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", e.GetKeyByteSize(), len(key))
    25  	}
    26  	ivz := make([]byte, aes.BlockSize)
    27  	return aescts.Encrypt(key, ivz, data)
    28  }
    29  
    30  // EncryptMessage encrypts the message provided using the methods specific to the etype provided as defined in RFC 8009.
    31  // The encrypted data is concatenated with its integrity hash to create an encrypted message.
    32  func EncryptMessage(key, message []byte, usage uint32, e etype.EType) ([]byte, []byte, error) {
    33  	kl := e.GetKeyByteSize()
    34  	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
    35  		kl = 32
    36  	}
    37  	if len(key) != kl {
    38  		return []byte{}, []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key))
    39  	}
    40  	if len(key) != e.GetKeyByteSize() {
    41  	}
    42  	//confounder
    43  	c := make([]byte, e.GetConfounderByteSize())
    44  	_, err := rand.Read(c)
    45  	if err != nil {
    46  		return []byte{}, []byte{}, fmt.Errorf("could not generate random confounder: %v", err)
    47  	}
    48  	plainBytes := append(c, message...)
    49  
    50  	// Derive key for encryption from usage
    51  	var k []byte
    52  	if usage != 0 {
    53  		k, err = e.DeriveKey(key, common.GetUsageKe(usage))
    54  		if err != nil {
    55  			return []byte{}, []byte{}, fmt.Errorf("error deriving key for encryption: %v", err)
    56  		}
    57  	}
    58  
    59  	// Encrypt the data
    60  	iv, b, err := e.EncryptData(k, plainBytes)
    61  	if err != nil {
    62  		return iv, b, fmt.Errorf("error encrypting data: %v", err)
    63  	}
    64  
    65  	ivz := make([]byte, e.GetConfounderByteSize())
    66  	ih, err := GetIntegityHash(ivz, b, key, usage, e)
    67  	if err != nil {
    68  		return iv, b, fmt.Errorf("error encrypting data: %v", err)
    69  	}
    70  	b = append(b, ih...)
    71  	return iv, b, nil
    72  }
    73  
    74  // DecryptData decrypts the data provided using the methods specific to the etype provided as defined in RFC 8009.
    75  func DecryptData(key, data []byte, e etype.EType) ([]byte, error) {
    76  	kl := e.GetKeyByteSize()
    77  	if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
    78  		kl = 32
    79  	}
    80  	if len(key) != kl {
    81  		return []byte{}, fmt.Errorf("incorrect keysize: expected: %v actual: %v", kl, len(key))
    82  	}
    83  	ivz := make([]byte, aes.BlockSize)
    84  	return aescts.Decrypt(key, ivz, data)
    85  }
    86  
    87  // DecryptMessage decrypts the message provided using the methods specific to the etype provided as defined in RFC 8009.
    88  // The integrity of the message is also verified.
    89  func DecryptMessage(key, ciphertext []byte, usage uint32, e etype.EType) ([]byte, error) {
    90  	//Derive the key
    91  	k, err := e.DeriveKey(key, common.GetUsageKe(usage))
    92  	if err != nil {
    93  		return nil, fmt.Errorf("error deriving key: %v", err)
    94  	}
    95  	// Strip off the checksum from the end
    96  	b, err := e.DecryptData(k, ciphertext[:len(ciphertext)-e.GetHMACBitLength()/8])
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	//Verify checksum
   101  	if !e.VerifyIntegrity(key, ciphertext, b, usage) {
   102  		return nil, errors.New("integrity verification failed")
   103  	}
   104  	//Remove the confounder bytes
   105  	return b[e.GetConfounderByteSize():], nil
   106  }
   107  
   108  // GetIntegityHash returns a keyed integrity hash of the bytes provided as defined in RFC 8009
   109  func GetIntegityHash(iv, c, key []byte, usage uint32, e etype.EType) ([]byte, error) {
   110  	// Generate and append integrity hash
   111  	// Rather than calculating the hash over the confounder and plaintext
   112  	// it is calculated over the iv concatenated with the AES cipher output.
   113  	ib := append(iv, c...)
   114  	return common.GetIntegrityHash(ib, key, usage, e)
   115  }
   116  
   117  // VerifyIntegrity verifies the integrity of cipertext bytes ct.
   118  func VerifyIntegrity(key, ct []byte, usage uint32, etype etype.EType) bool {
   119  	h := make([]byte, etype.GetHMACBitLength()/8)
   120  	copy(h, ct[len(ct)-etype.GetHMACBitLength()/8:])
   121  	ivz := make([]byte, etype.GetConfounderByteSize())
   122  	ib := append(ivz, ct[:len(ct)-(etype.GetHMACBitLength()/8)]...)
   123  	expectedMAC, _ := common.GetIntegrityHash(ib, key, usage, etype)
   124  	return hmac.Equal(h, expectedMAC)
   125  }