blob: e94732226c6e78e71b48b369f9565769c0f3ab10 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001package rfc8009
2
3import (
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
14const (
15 s2kParamsZero = 32768
16)
17
18// DeriveRandom for key derivation as defined in RFC 8009
19func 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
27func 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.
62func 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.
67func 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.
76func 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
82func 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
91func 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
111func 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.
119func 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}