#! /usr/bin/env crystal
#
# This helper fetches the Mozilla recommendations for default TLS ciphers
# (https://wiki.mozilla.org/Security/Server_Side_TLS) and automatically places
# them in src/openssl/ssl/defaults.cr

require "http"
require "json"

struct Configuration
  include JSON::Serializable

  getter oldest_clients : Array(String)
  getter ciphersuites : Array(String)
  @[JSON::Field(root: "openssl")]
  getter ciphers : Array(String)
end

struct Guidelines
  include JSON::Serializable

  @[JSON::Field(converter: String::RawConverter)]
  getter version : String
  getter href : String
  getter configurations : Hash(String, Configuration)
end

url = ARGV.shift? || "https://ssl-config.mozilla.org/guidelines/latest.json"
DEFAULTS_FILE = File.expand_path("../src/openssl/ssl/defaults.cr", __DIR__)

guidelines = Guidelines.from_json(HTTP::Client.get(url).body)
disabled_ciphers = %w(!RC4 !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS)

File.open(DEFAULTS_FILE, "w") do |file|
  file.puts <<-CRYSTAL
  # THIS FILE WAS AUTOMATICALLY GENERATED BY scripts/#{File.basename(__FILE__)}
  # on #{Time.utc}.

  abstract class OpenSSL::SSL::Context
  CRYSTAL

  guidelines.configurations.join(file, '\n') do |(level, configuration)|
    clients = configuration.oldest_clients
    ciphersuites = configuration.ciphersuites
    ciphers = configuration.ciphers
    all_ciphers = ciphersuites + ciphers + disabled_ciphers

    file.puts <<-CRYSTAL
      # The list of secure ciphers on **#{level}** compatibility level as per Mozilla
      # recommendations.
      #
      # The oldest clients supported by this configuration are:
      # * #{clients.join("\n  # * ")}
      #
      # This list represents version #{guidelines.version} of the #{level} configuration
      # available at #{guidelines.href}.
      #
      # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.
      @[Deprecated("Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org")]
      CIPHERS_#{level.upcase} = "#{all_ciphers.join(":")}"

      # The list of secure ciphersuites on **#{level}** compatibility level as per Mozilla
      # recommendations.
      #
      # The oldest clients supported by this configuration are:
      # * #{clients.join("\n  # * ")}
      #
      # This list represents version #{guidelines.version} of the #{level} configuration
      # available at #{guidelines.href}.
      #
      # See https://wiki.mozilla.org/Security/Server_Side_TLS for details.
      @[Deprecated("Deprecated with no replacement. Prefer setting a security level, global system configuration, or build your own from https://ssl-config.mozilla.org")]
      CIPHER_SUITES_#{level.upcase} = "#{ciphersuites.join(":")}"
    CRYSTAL
  end
  file.puts "end"
end
