keyDerivation.go 4.1 KB

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