github.com/jcmturner/gokrb5/v8@v8.4.4/crypto/rfc8009/keyDerivation.go (about) 1 package rfc8009 2 3 import ( 4 "crypto/hmac" 5 "encoding/binary" 6 "encoding/hex" 7 "errors" 8 9 "github.com/jcmturner/gokrb5/v8/crypto/etype" 10 "github.com/jcmturner/gokrb5/v8/iana/etypeID" 11 "golang.org/x/crypto/pbkdf2" 12 ) 13 14 const ( 15 s2kParamsZero = 32768 16 ) 17 18 // DeriveRandom for key derivation as defined in RFC 8009 19 func DeriveRandom(protocolKey, usage []byte, e etype.EType) ([]byte, error) { 20 h := e.GetHashFunc()() 21 return KDF_HMAC_SHA2(protocolKey, []byte("prf"), usage, h.Size(), e), nil 22 } 23 24 // DeriveKey derives a key from the protocol key based on the usage and the etype's specific methods. 25 // 26 // https://tools.ietf.org/html/rfc8009#section-5 27 func DeriveKey(protocolKey, label []byte, e etype.EType) []byte { 28 var context []byte 29 var kl int 30 // Key length is longer for aes256-cts-hmac-sha384-192 is it is a Ke or from StringToKey (where label is "kerberos") 31 if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { 32 Swtch: 33 switch label[len(label)-1] { 34 case 0x73: 35 // 0x73 is "s" so label could be kerberos meaning StringToKey so now check if the label is "kerberos" 36 kerblabel := []byte("kerberos") 37 if len(label) != len(kerblabel) { 38 break 39 } 40 for i, b := range label { 41 if b != kerblabel[i] { 42 kl = e.GetKeySeedBitLength() 43 break Swtch 44 } 45 } 46 if kl == 0 { 47 // This is StringToKey 48 kl = 256 49 } 50 case 0xAA: 51 // This is a Ke 52 kl = 256 53 } 54 } 55 if kl == 0 { 56 kl = e.GetKeySeedBitLength() 57 } 58 return e.RandomToKey(KDF_HMAC_SHA2(protocolKey, label, context, kl, e)) 59 } 60 61 // RandomToKey returns a key from the bytes provided according to the definition in RFC 8009. 62 func RandomToKey(b []byte) []byte { 63 return b 64 } 65 66 // StringToKey returns a key derived from the string provided according to the definition in RFC 8009. 67 func StringToKey(secret, salt, s2kparams string, e etype.EType) ([]byte, error) { 68 i, err := S2KparamsToItertions(s2kparams) 69 if err != nil { 70 return nil, err 71 } 72 return StringToKeyIter(secret, salt, i, e) 73 } 74 75 // StringToKeyIter returns a key derived from the string provided according to the definition in RFC 8009. 76 func StringToKeyIter(secret, salt string, iterations int, e etype.EType) ([]byte, error) { 77 tkey := e.RandomToKey(StringToPBKDF2(secret, salt, iterations, e)) 78 return e.DeriveKey(tkey, []byte("kerberos")) 79 } 80 81 // StringToPBKDF2 generates an encryption key from a pass phrase and salt string using the PBKDF2 function from PKCS #5 v2.0 82 func StringToPBKDF2(secret, salt string, iterations int, e etype.EType) []byte { 83 kl := e.GetKeyByteSize() 84 if e.GetETypeID() == etypeID.AES256_CTS_HMAC_SHA384_192 { 85 kl = 32 86 } 87 return pbkdf2.Key([]byte(secret), []byte(salt), iterations, kl, e.GetHashFunc()) 88 } 89 90 // KDF_HMAC_SHA2 key derivation: https://tools.ietf.org/html/rfc8009#section-3 91 func KDF_HMAC_SHA2(protocolKey, label, context []byte, kl int, e etype.EType) []byte { 92 //k: Length in bits of the key to be outputted, expressed in big-endian binary representation in 4 bytes. 93 k := make([]byte, 4, 4) 94 binary.BigEndian.PutUint32(k, uint32(kl)) 95 96 c := make([]byte, 4, 4) 97 binary.BigEndian.PutUint32(c, uint32(1)) 98 c = append(c, label...) 99 c = append(c, byte(0)) 100 if len(context) > 0 { 101 c = append(c, context...) 102 } 103 c = append(c, k...) 104 105 mac := hmac.New(e.GetHashFunc(), protocolKey) 106 mac.Write(c) 107 return mac.Sum(nil)[:(kl / 8)] 108 } 109 110 // GetSaltP returns the salt value based on the etype name: https://tools.ietf.org/html/rfc8009#section-4 111 func GetSaltP(salt, ename string) string { 112 b := []byte(ename) 113 b = append(b, byte(0)) 114 b = append(b, []byte(salt)...) 115 return string(b) 116 } 117 118 // S2KparamsToItertions converts the string representation of iterations to an integer for RFC 8009. 119 func S2KparamsToItertions(s2kparams string) (int, error) { 120 var i uint32 121 if len(s2kparams) != 8 { 122 return s2kParamsZero, errors.New("Invalid s2kparams length") 123 } 124 b, err := hex.DecodeString(s2kparams) 125 if err != nil { 126 return s2kParamsZero, errors.New("Invalid s2kparams, cannot decode string to bytes") 127 } 128 i = binary.BigEndian.Uint32(b) 129 //buf := bytes.NewBuffer(b) 130 //err = binary.Read(buf, binary.BigEndian, &i) 131 if err != nil { 132 return s2kParamsZero, errors.New("Invalid s2kparams, cannot convert to big endian int32") 133 } 134 return int(i), nil 135 }