123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- package rfc8009
- import (
- "crypto/hmac"
- "encoding/binary"
- "encoding/hex"
- "errors"
- "github.com/jcmturner/gokrb5/v8/crypto/etype"
- "github.com/jcmturner/gokrb5/v8/iana/etypeID"
- "golang.org/x/crypto/pbkdf2"
- )
- const (
- s2kParamsZero = 32768
- )
- // DeriveRandom for key derivation as defined in RFC 8009
- func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) {
- h := e.GetHashFunc()()
- return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil
- }
- // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods.
- //
- // https://tools.ietf.org/html/rfc8009#section-5
- func DeriveKey(protocolKey, label []byte, e etype.EType) []byte {
- var context []byte
- var kl int
- // Key length is longer for aes256-cts-hmac-sha384-192 is it is a Ke or from StringToKey (where label is "kerberos")
- if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
- Swtch:
- switch label[len(label)-1] {
- case 0x73:
- // 0x73 is "s" so label could be kerberos meaning StringToKey so now check if the label is "kerberos"
- kerblabel := []byte("kerberos")
- if len(label) != len(kerblabel) {
- break
- }
- for i, b := range label {
- if b != kerblabel[i] {
- kl = e.GetKeySeedBitLength()
- break Swtch
- }
- }
- if kl == 0 {
- // This is StringToKey
- kl = 256
- }
- case 0xAA:
- // This is a Ke
- kl = 256
- }
- }
- if kl == 0 {
- kl = e.GetKeySeedBitLength()
- }
- return e.RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e))
- }
- // RandomToKey returns a key from the bytes provided according to the definition in RFC 8009.
- func RandomToKey(b []byte) []byte {
- return b
- }
- // StringToKey returns a key derived from the string provided according to the definition in RFC 8009.
- func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) {
- i, err := S2KparamsToItertions(s2kparams)
- if err != nil {
- return nil, err
- }
- return StringToKeyIter(secret, salt, i, e)
- }
- // StringToKeyIter returns a key derived from the string provided according to the definition in RFC 8009.
- func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) {
- tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e))
- return e.DeriveKey(tkey, []byte("kerberos"))
- }
- // StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0
- func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte {
- kl := e.GetKeyByteSize()
- if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 {
- kl = 32
- }
- return pbkdf2.Key([]byte(secret), []byte(salt), iterations, kl, e.GetHashFunc())
- }
- // KDF_HMAC_SHA2 key derivation: https://tools.ietf.org/html/rfc8009#section-3
- func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte {
- //k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes.
- k := make([]byte, 4, 4)
- binary.BigEndian.PutUint32(k, uint32(kl))
- c := make([]byte, 4, 4)
- binary.BigEndian.PutUint32(c, uint32(1))
- c = append(c, label...)
- c = append(c, byte(0))
- if len(context) > 0 {
- c = append(c, context...)
- }
- c = append(c, k...)
- mac := hmac.New(e.GetHashFunc(), protocolKey)
- mac.Write(c)
- return mac.Sum(nil)[:(kl / 8)]
- }
- // GetSaltP returns the salt value based on the etype name: https://tools.ietf.org/html/rfc8009#section-4
- func GetSaltP(salt, ename string) string {
- b := []byte(ename)
- b = append(b, byte(0))
- b = append(b, []byte(salt)...)
- return string(b)
- }
- // S2KparamsToItertions converts the string representation of iterations to an integer for RFC 8009.
- func S2KparamsToItertions(s2kparams string) (int, error) {
- var i uint32
- if len(s2kparams) != 8 {
- return s2kParamsZero, errors.New("Invalid s2kparams length")
- }
- b, err := hex.DecodeString(s2kparams)
- if err != nil {
- return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes")
- }
- i = binary.BigEndian.Uint32(b)
- //buf := bytes.NewBuffer(b)
- //err = binary.Read(buf, binary.BigEndian, &i)
- if err != nil {
- return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32")
- }
- return int(i), nil
- }
|