auth.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright © 2015-2023 Brett Vickers.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package ntp
  5. import (
  6. "bytes"
  7. "crypto/aes"
  8. "crypto/md5"
  9. "crypto/sha1"
  10. "crypto/sha256"
  11. "crypto/sha512"
  12. "crypto/subtle"
  13. "encoding/binary"
  14. "encoding/hex"
  15. )
  16. // AuthType specifies the cryptographic hash algorithm used to generate a
  17. // symmetric key authentication digest (or CMAC) for an NTP message. Please
  18. // note that MD5 and SHA1 are no longer considered secure; they appear here
  19. // solely for compatibility with existing NTP server implementations.
  20. type AuthType int
  21. const (
  22. AuthNone AuthType = iota // no authentication
  23. AuthMD5 // MD5 digest
  24. AuthSHA1 // SHA-1 digest
  25. AuthSHA256 // SHA-2 digest (256 bits)
  26. AuthSHA512 // SHA-2 digest (512 bits)
  27. AuthAES128 // AES-128-CMAC
  28. AuthAES256 // AES-256-CMAC
  29. )
  30. // AuthOptions contains fields used to configure symmetric key authentication
  31. // for an NTP query.
  32. type AuthOptions struct {
  33. // Type determines the cryptographic hash algorithm used to compute the
  34. // authentication digest or CMAC.
  35. Type AuthType
  36. // The cryptographic key used by the client to perform authentication. The
  37. // key may be hex-encoded or ascii-encoded. To use a hex-encoded key,
  38. // prefix it by "HEX:". To use an ascii-encoded key, prefix it by
  39. // "ASCII:". For example, "HEX:6931564b4a5a5045766c55356b30656c7666316c"
  40. // or "ASCII:cvuZyN4C8HX8hNcAWDWp".
  41. Key string
  42. // The identifier used by the NTP server to identify which key to use
  43. // for authentication purposes.
  44. KeyID uint16
  45. }
  46. var algorithms = []struct {
  47. MinKeySize int
  48. MaxKeySize int
  49. DigestSize int
  50. CalcDigest func(payload, key []byte) []byte
  51. }{
  52. {0, 0, 0, nil}, // AuthNone
  53. {4, 32, 16, calcDigest_MD5}, // AuthMD5
  54. {4, 32, 20, calcDigest_SHA1}, // AuthSHA1
  55. {4, 32, 20, calcDigest_SHA256}, // AuthSHA256
  56. {4, 32, 20, calcDigest_SHA512}, // AuthSHA512
  57. {16, 16, 16, calcCMAC_AES}, // AuthAES128
  58. {32, 32, 16, calcCMAC_AES}, // AuthAES256
  59. }
  60. func calcDigest_MD5(payload, key []byte) []byte {
  61. digest := md5.Sum(append(key, payload...))
  62. return digest[:]
  63. }
  64. func calcDigest_SHA1(payload, key []byte) []byte {
  65. digest := sha1.Sum(append(key, payload...))
  66. return digest[:]
  67. }
  68. func calcDigest_SHA256(payload, key []byte) []byte {
  69. digest := sha256.Sum256(append(key, payload...))
  70. return digest[:20]
  71. }
  72. func calcDigest_SHA512(payload, key []byte) []byte {
  73. digest := sha512.Sum512(append(key, payload...))
  74. return digest[:20]
  75. }
  76. func calcCMAC_AES(payload, key []byte) []byte {
  77. // calculate the CMAC according to the algorithm defined in RFC 4493. See
  78. // https://tools.ietf.org/html/rfc4493 for details.
  79. c, err := aes.NewCipher(key)
  80. if err != nil {
  81. panic(err)
  82. }
  83. // Generate subkeys.
  84. const rb = 0x87
  85. k1 := make([]byte, 16)
  86. k2 := make([]byte, 16)
  87. c.Encrypt(k1, k1)
  88. double(k1, k1, rb)
  89. double(k2, k1, rb)
  90. // Process all but the last block.
  91. cmac := make([]byte, 16)
  92. for ; len(payload) > 16; payload = payload[16:] {
  93. xor(cmac, payload[:16])
  94. c.Encrypt(cmac, cmac)
  95. }
  96. // Process the last block, padding as necessary.
  97. if len(payload) == 16 {
  98. xor(cmac, payload)
  99. xor(cmac, k1)
  100. } else {
  101. xor(cmac, pad(payload))
  102. xor(cmac, k2)
  103. }
  104. c.Encrypt(cmac, cmac)
  105. return cmac
  106. }
  107. func pad(block []byte) []byte {
  108. pad := make([]byte, 16-len(block))
  109. pad[0] = 0x80
  110. return append(block, pad...)
  111. }
  112. func double(dst, src []byte, xor int) {
  113. _ = src[15] // compiler hint: bounds check
  114. s0 := binary.BigEndian.Uint64(src[0:8])
  115. s1 := binary.BigEndian.Uint64(src[8:16])
  116. carry := int(s0 >> 63)
  117. d0 := (s0 << 1) | (s1 >> 63)
  118. d1 := (s1 << 1) ^ uint64(subtle.ConstantTimeSelect(carry, xor, 0))
  119. _ = dst[15] // compiler hint: bounds check
  120. binary.BigEndian.PutUint64(dst[0:8], d0)
  121. binary.BigEndian.PutUint64(dst[8:16], d1)
  122. }
  123. func xor(dst, src []byte) {
  124. _ = src[15] // compiler hint: bounds check
  125. s0 := binary.BigEndian.Uint64(src[0:8])
  126. s1 := binary.BigEndian.Uint64(src[8:16])
  127. _ = dst[15] // compiler hint: bounds check
  128. d0 := s0 ^ binary.BigEndian.Uint64(dst[0:8])
  129. d1 := s1 ^ binary.BigEndian.Uint64(dst[8:16])
  130. binary.BigEndian.PutUint64(dst[0:8], d0)
  131. binary.BigEndian.PutUint64(dst[8:16], d1)
  132. }
  133. func decodeAuthKey(opt AuthOptions) (key []byte, err error) {
  134. if opt.Type == AuthNone {
  135. return nil, nil
  136. }
  137. var keyIn string
  138. var isHex bool
  139. switch {
  140. case len(opt.Key) >= 4 && opt.Key[:4] == "HEX:":
  141. isHex, keyIn = true, opt.Key[4:]
  142. case len(opt.Key) >= 6 && opt.Key[:6] == "ASCII:":
  143. isHex, keyIn = false, opt.Key[6:]
  144. case len(opt.Key) > 20:
  145. isHex, keyIn = true, opt.Key
  146. default:
  147. isHex, keyIn = false, opt.Key
  148. }
  149. if isHex {
  150. key, err = hex.DecodeString(keyIn)
  151. if err != nil {
  152. return nil, ErrInvalidAuthKey
  153. }
  154. } else {
  155. key = []byte(keyIn)
  156. }
  157. a := algorithms[opt.Type]
  158. if len(key) < a.MinKeySize {
  159. return nil, ErrInvalidAuthKey
  160. }
  161. if len(key) > a.MaxKeySize {
  162. key = key[:a.MaxKeySize]
  163. }
  164. return key, nil
  165. }
  166. func appendMAC(buf *bytes.Buffer, opt AuthOptions, key []byte) {
  167. if opt.Type == AuthNone {
  168. return
  169. }
  170. a := algorithms[opt.Type]
  171. payload := buf.Bytes()
  172. digest := a.CalcDigest(payload, key)
  173. binary.Write(buf, binary.BigEndian, uint32(opt.KeyID))
  174. binary.Write(buf, binary.BigEndian, digest)
  175. }
  176. func verifyMAC(buf []byte, opt AuthOptions, key []byte) error {
  177. if opt.Type == AuthNone {
  178. return nil
  179. }
  180. // Validate that there are enough bytes at the end of the message to
  181. // contain a MAC.
  182. const headerSize = 48
  183. a := algorithms[opt.Type]
  184. macLen := 4 + a.DigestSize
  185. remain := len(buf) - headerSize
  186. if remain < macLen || (remain%4) != 0 {
  187. return ErrAuthFailed
  188. }
  189. // The key ID returned by the server must be the same as the key ID sent
  190. // to the server.
  191. payloadLen := len(buf) - macLen
  192. mac := buf[payloadLen:]
  193. keyID := binary.BigEndian.Uint32(mac[:4])
  194. if keyID != uint32(opt.KeyID) {
  195. return ErrAuthFailed
  196. }
  197. // Calculate and compare digests.
  198. payload := buf[:payloadLen]
  199. digest := a.CalcDigest(payload, key)
  200. if subtle.ConstantTimeCompare(digest, mac[4:]) != 1 {
  201. return ErrAuthFailed
  202. }
  203. return nil
  204. }