176 lines
5.0 KiB
Go
176 lines
5.0 KiB
Go
|
// 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
|
||
|
}
|