blob: 5c96ddfdf6fb8eac0c0831060ab3c16ebd18178b [file] [log] [blame]
// Package crypto implements cryptographic functions for Kerberos 5 implementation.
package crypto
import (
"encoding/hex"
"fmt"
"github.com/jcmturner/gokrb5/v8/crypto/etype"
"github.com/jcmturner/gokrb5/v8/iana/chksumtype"
"github.com/jcmturner/gokrb5/v8/iana/etypeID"
"github.com/jcmturner/gokrb5/v8/iana/patype"
"github.com/jcmturner/gokrb5/v8/types"
)
// GetEtype returns an instances of the required etype struct for the etype ID.
func GetEtype(id int32) (etype.EType, error) {
switch id {
case etypeID.AES128_CTS_HMAC_SHA1_96:
var et Aes128CtsHmacSha96
return et, nil
case etypeID.AES256_CTS_HMAC_SHA1_96:
var et Aes256CtsHmacSha96
return et, nil
case etypeID.AES128_CTS_HMAC_SHA256_128:
var et Aes128CtsHmacSha256128
return et, nil
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
case etypeID.RC4_HMAC:
var et RC4HMAC
return et, nil
default:
return nil, fmt.Errorf("unknown or unsupported EType: %d", id)
}
}
// GetChksumEtype returns an instances of the required etype struct for the checksum ID.
func GetChksumEtype(id int32) (etype.EType, error) {
switch id {
case chksumtype.HMAC_SHA1_96_AES128:
var et Aes128CtsHmacSha96
return et, nil
case chksumtype.HMAC_SHA1_96_AES256:
var et Aes256CtsHmacSha96
return et, nil
case chksumtype.HMAC_SHA256_128_AES128:
var et Aes128CtsHmacSha256128
return et, nil
case chksumtype.HMAC_SHA384_192_AES256:
var et Aes256CtsHmacSha384192
return et, nil
case chksumtype.HMAC_SHA1_DES3_KD:
var et Des3CbcSha1Kd
return et, nil
case chksumtype.KERB_CHECKSUM_HMAC_MD5:
var et RC4HMAC
return et, nil
//case chksumtype.KERB_CHECKSUM_HMAC_MD5_UNSIGNED:
// var et RC4HMAC
// return et, nil
default:
return nil, fmt.Errorf("unknown or unsupported checksum type: %d", id)
}
}
// GetKeyFromPassword generates an encryption key from the principal's password.
func GetKeyFromPassword(passwd string, cname types.PrincipalName, realm string, etypeID int32, pas types.PADataSequence) (types.EncryptionKey, etype.EType, error) {
var key types.EncryptionKey
et, err := GetEtype(etypeID)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
sk2p := et.GetDefaultStringToKeyParams()
var salt string
var paID int32
for _, pa := range pas {
switch pa.PADataType {
case patype.PA_PW_SALT:
if paID > pa.PADataType {
continue
}
salt = string(pa.PADataValue)
case patype.PA_ETYPE_INFO:
if paID > pa.PADataType {
continue
}
var eti types.ETypeInfo
err := eti.Unmarshal(pa.PADataValue)
if err != nil {
return key, et, fmt.Errorf("error unmashaling PA Data to PA-ETYPE-INFO2: %v", err)
}
if etypeID != eti[0].EType {
et, err = GetEtype(eti[0].EType)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
}
salt = string(eti[0].Salt)
case patype.PA_ETYPE_INFO2:
if paID > pa.PADataType {
continue
}
var et2 types.ETypeInfo2
err := et2.Unmarshal(pa.PADataValue)
if err != nil {
return key, et, fmt.Errorf("error unmashalling PA Data to PA-ETYPE-INFO2: %v", err)
}
if etypeID != et2[0].EType {
et, err = GetEtype(et2[0].EType)
if err != nil {
return key, et, fmt.Errorf("error getting encryption type: %v", err)
}
}
if len(et2[0].S2KParams) == 4 {
sk2p = hex.EncodeToString(et2[0].S2KParams)
}
salt = et2[0].Salt
}
}
if salt == "" {
salt = cname.GetSalt(realm)
}
k, err := et.StringToKey(passwd, salt, sk2p)
if err != nil {
return key, et, fmt.Errorf("error deriving key from string: %+v", err)
}
key = types.EncryptionKey{
KeyType: etypeID,
KeyValue: k,
}
return key, et, nil
}
// GetEncryptedData encrypts the data provided and returns and EncryptedData type.
// Pass a usage value of zero to use the key provided directly rather than deriving one.
func GetEncryptedData(plainBytes []byte, key types.EncryptionKey, usage uint32, kvno int) (types.EncryptedData, error) {
var ed types.EncryptedData
et, err := GetEtype(key.KeyType)
if err != nil {
return ed, fmt.Errorf("error getting etype: %v", err)
}
_, b, err := et.EncryptMessage(key.KeyValue, plainBytes, usage)
if err != nil {
return ed, err
}
ed = types.EncryptedData{
EType: key.KeyType,
Cipher: b,
KVNO: kvno,
}
return ed, nil
}
// DecryptEncPart decrypts the EncryptedData.
func DecryptEncPart(ed types.EncryptedData, key types.EncryptionKey, usage uint32) ([]byte, error) {
return DecryptMessage(ed.Cipher, key, usage)
}
// DecryptMessage decrypts the ciphertext and verifies the integrity.
func DecryptMessage(ciphertext []byte, key types.EncryptionKey, usage uint32) ([]byte, error) {
et, err := GetEtype(key.KeyType)
if err != nil {
return []byte{}, fmt.Errorf("error decrypting: %v", err)
}
b, err := et.DecryptMessage(key.KeyValue, ciphertext, usage)
if err != nil {
return nil, fmt.Errorf("error decrypting: %v", err)
}
return b, nil
}