tls_handshake.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. package scan
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "net"
  7. "strings"
  8. "github.com/cloudflare/cfssl/helpers"
  9. "github.com/cloudflare/cfssl/scan/crypto/tls"
  10. )
  11. // Sentinel for failures in sayHello. Should always be caught.
  12. var errHelloFailed = errors.New("Handshake failed in sayHello")
  13. // TLSHandshake contains scanners testing host cipher suite negotiation
  14. var TLSHandshake = &Family{
  15. Description: "Scans for host's SSL/TLS version and cipher suite negotiation",
  16. Scanners: map[string]*Scanner{
  17. "CipherSuite": {
  18. "Determines host's cipher suites accepted and preferred order",
  19. cipherSuiteScan,
  20. },
  21. "SigAlgs": {
  22. "Determines host's accepted signature and hash algorithms",
  23. sigAlgsScan,
  24. },
  25. "CertsBySigAlgs": {
  26. "Determines host's certificate signature algorithm matching client's accepted signature and hash algorithms",
  27. certSigAlgsScan,
  28. },
  29. "CertsByCiphers": {
  30. "Determines host's certificate signature algorithm matching client's accepted ciphers",
  31. certSigAlgsScanByCipher,
  32. },
  33. "ECCurves": {
  34. "Determines the host's ec curve support for TLS 1.2",
  35. ecCurveScan,
  36. },
  37. },
  38. }
  39. func getCipherIndex(ciphers []uint16, serverCipher uint16) (cipherIndex int, err error) {
  40. //func getCipherIndex(ciphers []uint16, serverCipher uint16) (cipherIndex int, err error) {
  41. // fmt.Println(serverCipher, ciphers)
  42. var cipherID uint16
  43. for cipherIndex, cipherID = range ciphers {
  44. if serverCipher == cipherID {
  45. return
  46. }
  47. }
  48. err = fmt.Errorf("server negotiated ciphersuite we didn't send: %s", tls.CipherSuites[serverCipher])
  49. return
  50. }
  51. func getCurveIndex(curves []tls.CurveID, serverCurve tls.CurveID) (curveIndex int, err error) {
  52. var curveID tls.CurveID
  53. for curveIndex, curveID = range curves {
  54. if serverCurve == curveID {
  55. return
  56. }
  57. }
  58. err = fmt.Errorf("server negotiated elliptic curve we didn't send: %s", tls.Curves[serverCurve])
  59. return
  60. }
  61. func sayHello(addr, hostname string, ciphers []uint16, curves []tls.CurveID, vers uint16, sigAlgs []tls.SignatureAndHash) (cipherIndex, curveIndex int, certs [][]byte, err error) {
  62. tcpConn, err := net.Dial(Network, addr)
  63. if err != nil {
  64. return
  65. }
  66. config := defaultTLSConfig(hostname)
  67. config.MinVersion = vers
  68. config.MaxVersion = vers
  69. if ciphers == nil {
  70. ciphers = allCiphersIDs()
  71. }
  72. config.CipherSuites = ciphers
  73. if curves == nil {
  74. curves = allCurvesIDs()
  75. }
  76. config.CurvePreferences = curves
  77. if sigAlgs == nil {
  78. sigAlgs = tls.AllSignatureAndHashAlgorithms
  79. }
  80. conn := tls.Client(tcpConn, config)
  81. serverCipher, serverCurveType, serverCurve, serverVersion, certificates, err := conn.SayHello(sigAlgs)
  82. certs = certificates
  83. conn.Close()
  84. if err != nil {
  85. err = errHelloFailed
  86. return
  87. }
  88. if serverVersion != vers {
  89. err = fmt.Errorf("server negotiated protocol version we didn't send: %s", tls.Versions[serverVersion])
  90. return
  91. }
  92. cipherIndex, err = getCipherIndex(ciphers, serverCipher)
  93. if tls.CipherSuites[serverCipher].EllipticCurve {
  94. if curves == nil {
  95. curves = allCurvesIDs()
  96. }
  97. if serverCurveType != 3 {
  98. err = fmt.Errorf("server negotiated non-named ECDH parameters; we didn't analyze them. Server curve type: %d", serverCurveType)
  99. return
  100. }
  101. curveIndex, err = getCurveIndex(curves, serverCurve)
  102. }
  103. return
  104. }
  105. func allCiphersIDs() []uint16 {
  106. ciphers := make([]uint16, 0, len(tls.CipherSuites))
  107. for cipherID := range tls.CipherSuites {
  108. ciphers = append(ciphers, cipherID)
  109. }
  110. return ciphers
  111. }
  112. func allECDHECiphersIDs() []uint16 {
  113. var ecdheCiphers = map[uint16]tls.CipherSuite{
  114. 0xC006: {Name: "TLS_ECDHE_ECDSA_WITH_NULL_SHA", ForwardSecret: true, EllipticCurve: true},
  115. 0xC007: {Name: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", ShortName: "ECDHE-ECDSA-RC4-SHA", ForwardSecret: true, EllipticCurve: true},
  116. 0xC008: {Name: "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", ShortName: "ECDHE-ECDSA-DES-CBC3-SHA", ForwardSecret: true, EllipticCurve: true},
  117. 0xC009: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", ShortName: "ECDHE-ECDSA-AES128-SHA", ForwardSecret: true, EllipticCurve: true},
  118. 0xC00A: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", ShortName: "ECDHE-ECDSA-AES256-SHA", ForwardSecret: true, EllipticCurve: true},
  119. 0xC010: {Name: "TLS_ECDHE_RSA_WITH_NULL_SHA", ForwardSecret: true, EllipticCurve: true},
  120. 0xC011: {Name: "TLS_ECDHE_RSA_WITH_RC4_128_SHA", ShortName: "ECDHE-RSA-RC4-SHA", ForwardSecret: true, EllipticCurve: true},
  121. 0xC012: {Name: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", ShortName: "ECDHE-RSA-DES-CBC3-SHA", ForwardSecret: true, EllipticCurve: true},
  122. 0xC013: {Name: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", ShortName: "ECDHE-RSA-AES128-SHA", ForwardSecret: true, EllipticCurve: true},
  123. 0xC014: {Name: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", ShortName: "ECDHE-RSA-AES256-SHA", ForwardSecret: true, EllipticCurve: true},
  124. 0xC023: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", ShortName: "ECDHE-ECDSA-AES128-SHA256", ForwardSecret: true, EllipticCurve: true},
  125. 0xC024: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", ShortName: "ECDHE-ECDSA-AES256-SHA384", ForwardSecret: true, EllipticCurve: true},
  126. 0xC027: {Name: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", ShortName: "ECDHE-RSA-AES128-SHA256", ForwardSecret: true, EllipticCurve: true},
  127. 0xC028: {Name: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", ShortName: "ECDHE-RSA-AES256-SHA384", ForwardSecret: true, EllipticCurve: true},
  128. 0xC02B: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", ShortName: "ECDHE-ECDSA-AES128-GCM-SHA256", ForwardSecret: true, EllipticCurve: true},
  129. 0xC02C: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", ShortName: "ECDHE-ECDSA-AES256-GCM-SHA384", ForwardSecret: true, EllipticCurve: true},
  130. 0xC02F: {Name: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", ShortName: "ECDHE-RSA-AES128-GCM-SHA256", ForwardSecret: true, EllipticCurve: true},
  131. 0xC030: {Name: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", ShortName: "ECDHE-RSA-AES256-GCM-SHA384", ForwardSecret: true, EllipticCurve: true},
  132. 0xC048: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
  133. 0xC049: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
  134. 0xC04C: {Name: "TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
  135. 0xC04D: {Name: "TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
  136. 0xC05D: {Name: "TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
  137. 0xC060: {Name: "TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
  138. 0xC061: {Name: "TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
  139. 0xC072: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
  140. 0xC073: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
  141. 0xC076: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", ForwardSecret: true, EllipticCurve: true},
  142. 0xC077: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384", ForwardSecret: true, EllipticCurve: true},
  143. 0xC086: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
  144. 0xC087: {Name: "TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
  145. 0xC08A: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256", ForwardSecret: true, EllipticCurve: true},
  146. 0xC08B: {Name: "TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384", ForwardSecret: true, EllipticCurve: true},
  147. 0xC08C: {Name: "TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256", EllipticCurve: true},
  148. 0xC0AC: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM", ForwardSecret: true, EllipticCurve: true},
  149. 0xC0AD: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM", ForwardSecret: true, EllipticCurve: true},
  150. 0xC0AE: {Name: "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8", ForwardSecret: true, EllipticCurve: true},
  151. 0xC0AF: {Name: "TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8", ForwardSecret: true, EllipticCurve: true},
  152. // Non-IANA standardized cipher suites:
  153. // ChaCha20, Poly1305 cipher suites are defined in
  154. // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
  155. 0xCC13: {Name: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", ForwardSecret: true, EllipticCurve: true},
  156. 0xCC14: {Name: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", ForwardSecret: true, EllipticCurve: true},
  157. }
  158. ciphers := make([]uint16, 0, len(ecdheCiphers))
  159. for cipherID := range ecdheCiphers {
  160. ciphers = append(ciphers, cipherID)
  161. }
  162. return ciphers
  163. }
  164. func allCurvesIDs() []tls.CurveID {
  165. curves := make([]tls.CurveID, 0, len(tls.Curves))
  166. for curveID := range tls.Curves {
  167. // No unassigned or explicit curves in the scan, per http://tools.ietf.org/html/rfc4492#section-5.4
  168. if curveID == 0 || curveID == 65281 || curveID == 65282 {
  169. continue
  170. } else {
  171. curves = append(curves, curveID)
  172. }
  173. }
  174. return curves
  175. }
  176. type cipherDatum struct {
  177. versionID uint16
  178. curves []tls.CurveID
  179. }
  180. // cipherVersions contains lists of host's supported cipher suites based on SSL/TLS Version.
  181. // If a cipher suite uses ECC, also contains a list of supported curves by SSL/TLS Version.
  182. type cipherVersions struct {
  183. cipherID uint16
  184. data []cipherDatum
  185. }
  186. type cipherVersionList []cipherVersions
  187. func (cvList cipherVersionList) String() string {
  188. cvStrings := make([]string, len(cvList))
  189. for i, c := range cvList {
  190. versStrings := make([]string, len(c.data))
  191. for j, d := range c.data {
  192. curveStrings := make([]string, len(d.curves))
  193. for k, c := range d.curves {
  194. curveStrings[k] = tls.Curves[c]
  195. }
  196. versStrings[j] = fmt.Sprintf("%s: [ %s ]", tls.Versions[d.versionID], strings.Join(curveStrings, ","))
  197. }
  198. cvStrings[i] = fmt.Sprintf("%s\t%s", tls.CipherSuites[c.cipherID], strings.Join(versStrings, ","))
  199. }
  200. return strings.Join(cvStrings, "\n")
  201. }
  202. func (cvList cipherVersionList) MarshalJSON() ([]byte, error) {
  203. b := new(bytes.Buffer)
  204. cvStrs := make([]string, len(cvList))
  205. for i, cv := range cvList {
  206. versStrings := make([]string, len(cv.data))
  207. for j, d := range cv.data {
  208. curveStrings := make([]string, len(d.curves))
  209. if len(d.curves) > 0 {
  210. for k, c := range d.curves {
  211. curveStrings[k] = fmt.Sprintf("\"%s\"", tls.Curves[c])
  212. }
  213. versStrings[j] = fmt.Sprintf("{\"%s\":[%s]}", tls.Versions[d.versionID], strings.Join(curveStrings, ","))
  214. } else {
  215. versStrings[j] = fmt.Sprintf("\"%s\"", tls.Versions[d.versionID])
  216. }
  217. }
  218. cvStrs[i] = fmt.Sprintf("{\"%s\":[%s]}", tls.CipherSuites[cv.cipherID].String(), strings.Join(versStrings, ","))
  219. }
  220. fmt.Fprintf(b, "[%s]", strings.Join(cvStrs, ","))
  221. return b.Bytes(), nil
  222. }
  223. func doCurveScan(addr, hostname string, vers, cipherID uint16, ciphers []uint16) (supportedCurves []tls.CurveID, err error) {
  224. allCurves := allCurvesIDs()
  225. curves := make([]tls.CurveID, len(allCurves))
  226. copy(curves, allCurves)
  227. for len(curves) > 0 {
  228. var curveIndex int
  229. _, curveIndex, _, err = sayHello(addr, hostname, []uint16{cipherID}, curves, vers, nil)
  230. if err != nil {
  231. // This case is expected, because eventually we ask only for curves the server doesn't support
  232. if err == errHelloFailed {
  233. err = nil
  234. break
  235. }
  236. return
  237. }
  238. curveID := curves[curveIndex]
  239. supportedCurves = append(supportedCurves, curveID)
  240. curves = append(curves[:curveIndex], curves[curveIndex+1:]...)
  241. }
  242. return
  243. }
  244. // cipherSuiteScan returns, by TLS Version, the sort list of cipher suites
  245. // supported by the host
  246. func cipherSuiteScan(addr, hostname string) (grade Grade, output Output, err error) {
  247. var cvList cipherVersionList
  248. allCiphers := allCiphersIDs()
  249. var vers uint16
  250. for vers = tls.VersionTLS12; vers >= tls.VersionSSL30; vers-- {
  251. ciphers := make([]uint16, len(allCiphers))
  252. copy(ciphers, allCiphers)
  253. for len(ciphers) > 0 {
  254. var cipherIndex int
  255. cipherIndex, _, _, err = sayHello(addr, hostname, ciphers, nil, vers, nil)
  256. if err != nil {
  257. if err == errHelloFailed {
  258. err = nil
  259. break
  260. }
  261. return
  262. }
  263. if vers == tls.VersionSSL30 {
  264. grade = Warning
  265. }
  266. cipherID := ciphers[cipherIndex]
  267. // If this is an EC cipher suite, do a second scan for curve support
  268. var supportedCurves []tls.CurveID
  269. if tls.CipherSuites[cipherID].EllipticCurve {
  270. supportedCurves, err = doCurveScan(addr, hostname, vers, cipherID, ciphers)
  271. if len(supportedCurves) == 0 {
  272. err = errors.New("couldn't negotiate any curves")
  273. }
  274. }
  275. for i, c := range cvList {
  276. if cipherID == c.cipherID {
  277. cvList[i].data = append(c.data, cipherDatum{vers, supportedCurves})
  278. goto exists
  279. }
  280. }
  281. cvList = append(cvList, cipherVersions{cipherID, []cipherDatum{{vers, supportedCurves}}})
  282. exists:
  283. ciphers = append(ciphers[:cipherIndex], ciphers[cipherIndex+1:]...)
  284. }
  285. }
  286. if len(cvList) == 0 {
  287. err = errors.New("couldn't negotiate any cipher suites")
  288. return
  289. }
  290. if grade != Warning {
  291. grade = Good
  292. }
  293. output = cvList
  294. return
  295. }
  296. // sigAlgsScan returns the accepted signature and hash algorithms of the host
  297. func sigAlgsScan(addr, hostname string) (grade Grade, output Output, err error) {
  298. var supportedSigAlgs []tls.SignatureAndHash
  299. for _, sigAlg := range tls.AllSignatureAndHashAlgorithms {
  300. _, _, _, e := sayHello(addr, hostname, nil, nil, tls.VersionTLS12, []tls.SignatureAndHash{sigAlg})
  301. if e == nil {
  302. supportedSigAlgs = append(supportedSigAlgs, sigAlg)
  303. }
  304. }
  305. if len(supportedSigAlgs) > 0 {
  306. grade = Good
  307. output = supportedSigAlgs
  308. } else {
  309. err = errors.New("no SigAlgs supported")
  310. }
  311. return
  312. }
  313. // certSigAlgScan returns the server certificate with various sigature and hash algorithms in the ClientHello
  314. func certSigAlgsScan(addr, hostname string) (grade Grade, output Output, err error) {
  315. var certSigAlgs = make(map[string]string)
  316. for _, sigAlg := range tls.AllSignatureAndHashAlgorithms {
  317. _, _, derCerts, e := sayHello(addr, hostname, nil, nil, tls.VersionTLS12, []tls.SignatureAndHash{sigAlg})
  318. if e == nil {
  319. if len(derCerts) == 0 {
  320. return Bad, nil, errors.New("no certs returned")
  321. }
  322. certs, _, err := helpers.ParseCertificatesDER(derCerts[0], "")
  323. if err != nil {
  324. return Bad, nil, err
  325. }
  326. certSigAlgs[sigAlg.String()] = helpers.SignatureString(certs[0].SignatureAlgorithm)
  327. //certSigAlgs = append(certSigAlgs, certs[0].SignatureAlgorithm)
  328. }
  329. }
  330. if len(certSigAlgs) > 0 {
  331. grade = Good
  332. output = certSigAlgs
  333. } else {
  334. err = errors.New("no SigAlgs supported")
  335. }
  336. return
  337. }
  338. // certSigAlgScan returns the server certificate with various ciphers in the ClientHello
  339. func certSigAlgsScanByCipher(addr, hostname string) (grade Grade, output Output, err error) {
  340. var certSigAlgs = make(map[string]string)
  341. for cipherID := range tls.CipherSuites {
  342. _, _, derCerts, e := sayHello(addr, hostname, []uint16{cipherID}, nil, tls.VersionTLS12, []tls.SignatureAndHash{})
  343. if e == nil {
  344. if len(derCerts) == 0 {
  345. return Bad, nil, errors.New("no certs returned")
  346. }
  347. certs, _, err := helpers.ParseCertificatesDER(derCerts[0], "")
  348. if err != nil {
  349. return Bad, nil, err
  350. }
  351. certSigAlgs[tls.CipherSuites[cipherID].Name] = helpers.SignatureString(certs[0].SignatureAlgorithm)
  352. //certSigAlgs = append(certSigAlgs, certs[0].SignatureAlgorithm)
  353. }
  354. }
  355. if len(certSigAlgs) > 0 {
  356. grade = Good
  357. output = certSigAlgs
  358. } else {
  359. err = errors.New("no cipher supported")
  360. }
  361. return
  362. }
  363. // ecCurveScan returns the elliptic curves supported by the host.
  364. func ecCurveScan(addr, hostname string) (grade Grade, output Output, err error) {
  365. allCurves := allCurvesIDs()
  366. curves := make([]tls.CurveID, len(allCurves))
  367. copy(curves, allCurves)
  368. var supportedCurves []string
  369. for len(curves) > 0 {
  370. var curveIndex int
  371. _, curveIndex, _, err = sayHello(addr, hostname, allECDHECiphersIDs(), curves, tls.VersionTLS12, nil)
  372. if err != nil {
  373. // This case is expected, because eventually we ask only for curves the server doesn't support
  374. if err == errHelloFailed {
  375. err = nil
  376. break
  377. }
  378. return
  379. }
  380. curveID := curves[curveIndex]
  381. supportedCurves = append(supportedCurves, tls.Curves[curveID])
  382. curves = append(curves[:curveIndex], curves[curveIndex+1:]...)
  383. }
  384. output = supportedCurves
  385. grade = Good
  386. return
  387. }