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.

286 lines
5.7KB

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