You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

278 line
5.3KB

  1. module CryptCheck
  2. module Tls
  3. class Cipher
  4. TYPES = {
  5. md5: %w(MD5),
  6. sha1: %w(SHA),
  7. sha256: %w(SHA256),
  8. sha384: %w(SHA384),
  9. poly1305: %w(POLY1305),
  10. psk: %w(PSK),
  11. srp: %w(SRP),
  12. anonymous: %w(ADH AECDH),
  13. dss: %w(DSS),
  14. rsa: %w(RSA),
  15. ecdsa: %w(ECDSA),
  16. dh: %w(DH ADH),
  17. ecdh: %w(ECDH AECDH),
  18. dhe: %w(DHE EDH ADH),
  19. ecdhe: %w(ECDHE AECDH),
  20. null: %w(NULL),
  21. export: %w(EXP),
  22. rc2: %w(RC2),
  23. rc4: %w(RC4),
  24. des: %w(DES-CBC),
  25. des3: %w(3DES DES-CBC3),
  26. aes: %w(AES(128|256) AES-(128|256)),
  27. camellia: %w(CAMELLIA(128|256)),
  28. seed: %w(SEED),
  29. idea: %w(IDEA),
  30. chacha20: %w(CHACHA20),
  31. #cbc: %w(CBC),
  32. gcm: %w(GCM),
  33. ccm: %w(CCM)
  34. }
  35. attr_reader :method, :name
  36. def initialize(method, name)
  37. @method, @name = method, name
  38. end
  39. extend Enumerable
  40. def self.each(&block)
  41. SUPPORTED.each &block
  42. end
  43. def self.[](method)
  44. SUPPORTED[method]
  45. end
  46. TYPES.each do |name, ciphers|
  47. class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
  48. def self.#{name}?(cipher)
  49. #{ciphers}.any? { |c| /(^|-)#\{c\}(-|$)/ =~ cipher }
  50. end
  51. def #{name}?
  52. #{ciphers}.any? { |c| /(^|-)#\{c\}(-|$)/ =~ @name }
  53. end
  54. RUBY_EVAL
  55. end
  56. def self.cbc?(cipher)
  57. !aead? cipher
  58. end
  59. def cbc?
  60. !aead?
  61. end
  62. def self.aead?(cipher)
  63. gcm?(cipher) or ccm?(cipher)
  64. end
  65. def aead?
  66. gcm? or ccm?
  67. end
  68. def ssl?
  69. sslv2? or sslv3?
  70. end
  71. def tls?
  72. tlsv1? or tlsv1_1? or tlsv1_2?
  73. end
  74. def pfs?
  75. dhe? or ecdhe?
  76. end
  77. def ecc?
  78. ecdsa? or ecdhe? or ecdh?
  79. end
  80. def sweet32?
  81. size = self.block_size
  82. return false unless size # Not block encryption
  83. size <= 64
  84. end
  85. def to_s(type = :long)
  86. case type
  87. when :long
  88. states = self.states.collect { |k, vs| vs.select { |_, c| c == true }.collect { |v| v.first.to_s.colorize k } }.flatten.join ' '
  89. "#{@method} #{@name.colorize self.status} [#{states}]"
  90. when :short
  91. @name.colorize self.status
  92. end
  93. end
  94. def to_h
  95. hmac = self.hmac
  96. {
  97. protocol: @method, name: self.name, key_exchange: self.kex, authentication: self.auth,
  98. encryption: { name: self.encryption, mode: self.mode, block_size: self.block_size },
  99. hmac: { name: hmac.first, size: hmac.last }, states: self.states
  100. }
  101. end
  102. def <=>(other)
  103. compare = State.compare self, other
  104. return compare unless compare == 0
  105. size_a, size_b = a.size, b.size
  106. compare = size_b <=> size_a
  107. return compare unless compare == 0
  108. dh_a, dh_b = a.dh, b.dh
  109. return -1 if not dh_a and dh_b
  110. return 1 if dh_a and not dh_b
  111. return a.name <=> b.name if not dh_a and not dh_b
  112. compare = b.dh.size <=> a.dh.size
  113. return compare unless compare == 0
  114. a.name <=> b.name
  115. end
  116. def self.list(cipher_suite = 'ALL:COMPLEMENTOFALL', method: :TLSv1_2)
  117. context = OpenSSL::SSL::SSLContext.new method
  118. context.ciphers = cipher_suite
  119. ciphers = context.ciphers.collect { |c| self.new method, c }
  120. self.sort ciphers
  121. end
  122. def kex
  123. case
  124. when ecdhe? || ecdh?
  125. :ecdh
  126. when dhe? || dh?
  127. :dh
  128. when dss?
  129. :dss
  130. else
  131. :rsa
  132. end
  133. end
  134. def auth
  135. case
  136. when ecdsa?
  137. :ecdsa
  138. when rsa?
  139. :rsa
  140. when dss?
  141. :dss
  142. when anonymous?
  143. nil
  144. else
  145. :rsa
  146. end
  147. end
  148. def encryption
  149. case
  150. when chacha20?
  151. :chacha20
  152. when aes?
  153. :aes
  154. when camellia?
  155. :camellia
  156. when seed?
  157. :seed
  158. when idea?
  159. :idea
  160. when des3?
  161. :'3des'
  162. when des?
  163. :des
  164. when rc4?
  165. :rc4
  166. when rc2?
  167. :rc2
  168. end
  169. end
  170. def mode
  171. case
  172. when gcm?
  173. :gcm
  174. when ccm?
  175. :ccm
  176. when rc4? || chacha20?
  177. nil
  178. else
  179. :cbc
  180. end
  181. end
  182. def block_size
  183. case self.encryption
  184. when :'3des', :idea, :rc2
  185. 64
  186. when :aes, :camellia, :seed
  187. 128
  188. else
  189. nil
  190. end
  191. end
  192. def hmac
  193. case
  194. when poly1305?
  195. [:poly1305, 128]
  196. when sha384?
  197. [:sha384, 384]
  198. when sha256?
  199. [:sha256, 256]
  200. when sha1?
  201. [:sha1, 160]
  202. when md5?
  203. [:md5, 128]
  204. end
  205. end
  206. protected
  207. include State
  208. CHECKS = [
  209. [:dss, :critical, -> (c) { c.dss? }],
  210. [:anonymous, :critical, -> (c) { c.anonymous? }],
  211. [:null, :critical, -> (c) { c.null? }],
  212. [:export, :critical, -> (c) { c.export? }],
  213. [:des, :critical, -> (c) { c.des? }],
  214. [:md5, :critical, -> (c) { c.md5? }],
  215. [:rc4, :critical, -> (c) { c.rc4? }],
  216. [:sweet32, :critical, -> (c) { c.sweet32? }],
  217. [:pfs, :error, -> (c) { not c.pfs? }],
  218. [:dhe, :warning, -> (c) { c.dhe? }],
  219. [:aead, :good, -> (c) { c.aead? }]
  220. ].freeze
  221. def available_checks
  222. CHECKS
  223. end
  224. def <=>(other)
  225. status = State.compare self, other
  226. return status if status != 0
  227. @name <=> other.name
  228. end
  229. ALL = 'ALL:COMPLEMENTOFALL'
  230. SUPPORTED = Method.collect do |m|
  231. context = ::OpenSSL::SSL::SSLContext.new m.to_sym
  232. context.ciphers = ALL
  233. ciphers = context.ciphers.collect { |c| Cipher.new m, c.first }
  234. [m, ciphers.sort]
  235. end.to_h.freeze
  236. end
  237. end
  238. end