MICToken.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. package gssapi
  2. import (
  3. "bytes"
  4. "crypto/hmac"
  5. "encoding/binary"
  6. "encoding/hex"
  7. "errors"
  8. "fmt"
  9. "gopkg.in/jcmturner/gokrb5.v7/crypto"
  10. "gopkg.in/jcmturner/gokrb5.v7/iana/keyusage"
  11. "gopkg.in/jcmturner/gokrb5.v7/types"
  12. )
  13. /*
  14. From RFC 4121, section 4.2.6.1:
  15. Use of the GSS_GetMIC() call yields a token (referred as the MIC
  16. token in this document), separate from the user data being protected,
  17. which can be used to verify the integrity of that data as received.
  18. The token has the following format:
  19. Octet no Name Description
  20. --------------------------------------------------------------
  21. 0..1 TOK_ID Identification field. Tokens emitted by
  22. GSS_GetMIC() contain the hex value 04 04
  23. expressed in big-endian order in this
  24. field.
  25. 2 Flags Attributes field, as described in section
  26. 4.2.2.
  27. 3..7 Filler Contains five octets of hex value FF.
  28. 8..15 SND_SEQ Sequence number field in clear text,
  29. expressed in big-endian order.
  30. 16..last SGN_CKSUM Checksum of the "to-be-signed" data and
  31. octet 0..15, as described in section 4.2.4.
  32. The Filler field is included in the checksum calculation for
  33. simplicity.
  34. */
  35. const (
  36. // MICTokenFlagSentByAcceptor - this flag indicates the sender is the context acceptor. When not set, it indicates the sender is the context initiator
  37. MICTokenFlagSentByAcceptor = 1 << iota
  38. // MICTokenFlagSealed - this flag indicates confidentiality is provided for. It SHALL NOT be set in MIC tokens
  39. MICTokenFlagSealed
  40. // MICTokenFlagAcceptorSubkey - a subkey asserted by the context acceptor is used to protect the message
  41. MICTokenFlagAcceptorSubkey
  42. )
  43. const (
  44. micHdrLen = 16 // Length of the MIC Token's header
  45. )
  46. // MICToken represents a GSS API MIC token, as defined in RFC 4121.
  47. // It contains the header fields, the payload (this is not transmitted) and
  48. // the checksum, and provides the logic for converting to/from bytes plus
  49. // computing and verifying checksums
  50. type MICToken struct {
  51. // const GSS Token ID: 0x0404
  52. Flags byte // contains three flags: acceptor, sealed, acceptor subkey
  53. // const Filler: 0xFF 0xFF 0xFF 0xFF 0xFF
  54. SndSeqNum uint64 // sender's sequence number. big-endian
  55. Payload []byte // your data! :)
  56. Checksum []byte // checksum of { payload | header }
  57. }
  58. // Return the 2 bytes identifying a GSS API MIC token
  59. func getGSSMICTokenID() *[2]byte {
  60. return &[2]byte{0x04, 0x04}
  61. }
  62. // Return the filler bytes used in header
  63. func fillerBytes() *[5]byte {
  64. return &[5]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
  65. }
  66. // Marshal the MICToken into a byte slice.
  67. // The payload should have been set and the checksum computed, otherwise an error is returned.
  68. func (mt *MICToken) Marshal() ([]byte, error) {
  69. if mt.Checksum == nil {
  70. return nil, errors.New("checksum has not been set")
  71. }
  72. bytes := make([]byte, micHdrLen+len(mt.Checksum))
  73. copy(bytes[0:micHdrLen], mt.getMICChecksumHeader()[:])
  74. copy(bytes[micHdrLen:], mt.Checksum)
  75. return bytes, nil
  76. }
  77. // SetChecksum uses the passed encryption key and key usage to compute the checksum over the payload and
  78. // the header, and sets the Checksum field of this MICToken.
  79. // If the payload has not been set or the checksum has already been set, an error is returned.
  80. func (mt *MICToken) SetChecksum(key types.EncryptionKey, keyUsage uint32) error {
  81. if mt.Checksum != nil {
  82. return errors.New("checksum has already been computed")
  83. }
  84. checksum, err := mt.checksum(key, keyUsage)
  85. if err != nil {
  86. return err
  87. }
  88. mt.Checksum = checksum
  89. return nil
  90. }
  91. // Compute and return the checksum of this token, computed using the passed key and key usage.
  92. // Confirms to RFC 4121 in that the checksum will be computed over { body | header }.
  93. // In the context of Kerberos MIC tokens, mostly keyusage GSSAPI_ACCEPTOR_SIGN (=23)
  94. // and GSSAPI_INITIATOR_SIGN (=25) will be used.
  95. // Note: This will NOT update the struct's Checksum field.
  96. func (mt *MICToken) checksum(key types.EncryptionKey, keyUsage uint32) ([]byte, error) {
  97. if mt.Payload == nil {
  98. return nil, errors.New("cannot compute checksum with uninitialized payload")
  99. }
  100. d := make([]byte, micHdrLen+len(mt.Payload))
  101. copy(d[0:], mt.Payload)
  102. copy(d[len(mt.Payload):], mt.getMICChecksumHeader())
  103. encType, err := crypto.GetEtype(key.KeyType)
  104. if err != nil {
  105. return nil, err
  106. }
  107. return encType.GetChecksumHash(key.KeyValue, d, keyUsage)
  108. }
  109. // Build a header suitable for a checksum computation
  110. func (mt *MICToken) getMICChecksumHeader() []byte {
  111. header := make([]byte, micHdrLen)
  112. copy(header[0:2], getGSSMICTokenID()[:])
  113. header[2] = mt.Flags
  114. copy(header[3:8], fillerBytes()[:])
  115. binary.BigEndian.PutUint64(header[8:16], mt.SndSeqNum)
  116. return header
  117. }
  118. // Verify computes the token's checksum with the provided key and usage,
  119. // and compares it to the checksum present in the token.
  120. // In case of any failure, (false, err) is returned, with err an explanatory error.
  121. func (mt *MICToken) Verify(key types.EncryptionKey, keyUsage uint32) (bool, error) {
  122. computed, err := mt.checksum(key, keyUsage)
  123. if err != nil {
  124. return false, err
  125. }
  126. if !hmac.Equal(computed, mt.Checksum) {
  127. return false, fmt.Errorf(
  128. "checksum mismatch. Computed: %s, Contained in token: %s",
  129. hex.EncodeToString(computed), hex.EncodeToString(mt.Checksum))
  130. }
  131. return true, nil
  132. }
  133. // Unmarshal bytes into the corresponding MICToken.
  134. // If expectFromAcceptor is true we expect the token to have been emitted by the gss acceptor,
  135. // and will check the according flag, returning an error if the token does not match the expectation.
  136. func (mt *MICToken) Unmarshal(b []byte, expectFromAcceptor bool) error {
  137. if len(b) < micHdrLen {
  138. return errors.New("bytes shorter than header length")
  139. }
  140. if !bytes.Equal(getGSSMICTokenID()[:], b[0:2]) {
  141. return fmt.Errorf("wrong Token ID, Expected %s, was %s",
  142. hex.EncodeToString(getGSSMICTokenID()[:]),
  143. hex.EncodeToString(b[0:2]))
  144. }
  145. flags := b[2]
  146. isFromAcceptor := flags&MICTokenFlagSentByAcceptor != 0
  147. if isFromAcceptor && !expectFromAcceptor {
  148. return errors.New("unexpected acceptor flag is set: not expecting a token from the acceptor")
  149. }
  150. if !isFromAcceptor && expectFromAcceptor {
  151. return errors.New("unexpected acceptor flag is not set: expecting a token from the acceptor, not in the initiator")
  152. }
  153. if !bytes.Equal(b[3:8], fillerBytes()[:]) {
  154. return fmt.Errorf("unexpected filler bytes: expecting %s, was %s",
  155. hex.EncodeToString(fillerBytes()[:]),
  156. hex.EncodeToString(b[3:8]))
  157. }
  158. mt.Flags = flags
  159. mt.SndSeqNum = binary.BigEndian.Uint64(b[8:16])
  160. mt.Checksum = b[micHdrLen:]
  161. return nil
  162. }
  163. // NewInitiatorMICToken builds a new initiator token (acceptor flag will be set to 0) and computes the authenticated checksum.
  164. // Other flags are set to 0.
  165. // Note that in certain circumstances you may need to provide a sequence number that has been defined earlier.
  166. // This is currently not supported.
  167. func NewInitiatorMICToken(payload []byte, key types.EncryptionKey) (*MICToken, error) {
  168. token := MICToken{
  169. Flags: 0x00,
  170. SndSeqNum: 0,
  171. Payload: payload,
  172. }
  173. if err := token.SetChecksum(key, keyusage.GSSAPI_INITIATOR_SIGN); err != nil {
  174. return nil, err
  175. }
  176. return &token, nil
  177. }