# File ../../auditor/lib/kasp_checker.rb, line 342
    def check_kasp_file(kasp_file)
      begin
        File.open((kasp_file.to_s+"").untaint, 'r') {|file|
          begin
            doc = REXML::Document.new(file)
          rescue Exception => e
            log(LOG_CRIT, "Can't understand #{file} - exiting")
            exit(1)
          end

          # Run the following checks on kasp.xml :
          policy_names = []
          doc.elements.each('KASP/Policy') {|policy|
            name = policy.attributes['name']
            # Check if two policies exist with the same name
            if (policy_names.include?name)
              log(LOG_ERR, "Two policies exist with the same name (#{name})")
            end
            policy_names.push(name)

            #   2. For all policies, check that the "Re-sign" interval is less than the "Refresh" interval.
            resign_secs = get_duration(policy,'Signatures/Resign', kasp_file)
            refresh_secs = get_duration(policy, 'Signatures/Refresh', kasp_file)
            if (refresh_secs != 0 && refresh_secs <= resign_secs)
              log(LOG_ERR, "The Refresh interval (#{refresh_secs} seconds) for " +
                  "#{name} Policy in #{kasp_file} is less than or equal to the Resign interval" +
                  " (#{resign_secs} seconds)")
            end

            #   3. Ensure that the "Default" and "Denial" validity periods are greater than the "Refresh" interval.
            default_secs = get_duration(policy, 'Signatures/Validity/Default', kasp_file)
            denial_secs = get_duration(policy, 'Signatures/Validity/Denial', kasp_file)
            if (default_secs <= refresh_secs)
              log(LOG_ERR, "Validity/Default (#{default_secs} seconds) for #{name} " +
                  "policy in #{kasp_file} is less than the Refresh interval " +
                  "(#{refresh_secs} seconds)")
            end
            if (denial_secs <= refresh_secs)
              log(LOG_ERR, "Validity/Denial (#{denial_secs} seconds) for #{name} " +
                  "policy in #{kasp_file} is less than or equal to the Refresh interval " +
                  "(#{refresh_secs} seconds)")
            end

            #   5. Warn if "Jitter" is greater than 50% of the maximum of the "default" and "Denial" period. (This is a bit arbitrary. The point is to get the user to realise that there will be a large spread in the signature lifetimes.)
            jitter_secs = get_duration(policy, 'Signatures/Jitter', kasp_file)
            max_default_denial=[default_secs, denial_secs].max
            max_default_denial_type = max_default_denial == default_secs ? "Default" : "Denial"
            if (jitter_secs > (max_default_denial * 0.5))
              log(LOG_WARNING, "Jitter time (#{jitter_secs} seconds) is large" +
                  " compared to Validity/#{max_default_denial_type} " +
                  "(#{max_default_denial} seconds) for #{name} policy in #{kasp_file}")
            end

            # 14. Error if jitter is greater than either Default or Denial Validity
            if (jitter_secs > default_secs)
              log(LOG_ERR, "Jitter time (#{jitter_secs}) is greater than the Default Validity (#{default_secs}) for #{name} policy in #{kasp_file}")
            end
            if (jitter_secs > denial_secs)
              log(LOG_ERR, "Jitter time (#{jitter_secs}) is greater than the Denial Validity (#{denial_secs}) for #{name} policy in #{kasp_file}")
            end

            #   6. Warn if the InceptionOffset is greater than one hour. (Again arbitrary - but do we really expect the times on two systems to differ by more than this?)
            inception_offset_secs = get_duration(policy, 'Signatures/InceptionOffset', kasp_file)
            if (inception_offset_secs > (60 * 60))
              log(LOG_WARNING, "InceptionOffset is higher than expected " +
                  "(#{inception_offset_secs} seconds) for #{name} policy in #{kasp_file}")
            end

            #   7. Warn if the "PublishSafety" and "RetireSafety" margins are less than 0.1 * TTL or more than 5 * TTL.
            publish_safety_secs = get_duration(policy, 'Keys/PublishSafety', kasp_file)
            retire_safety_secs = get_duration(policy, 'Keys/RetireSafety', kasp_file)
            ttl_secs = get_duration(policy, 'Keys/TTL', kasp_file)
            [{publish_safety_secs => "Keys/PublishSafety"}, {retire_safety_secs => "Keys/RetireSafety"}].each {|pair|
              pair.each {|time, label|
                if (time < (0.1 * ttl_secs))
                  log(LOG_WARNING, "#{label} (#{time} seconds) in #{name} policy" +
                      " in #{kasp_file} is less than 0.1 * TTL (#{ttl_secs} seconds)")
                end
                if (time > (5 * ttl_secs))
                  log(LOG_WARNING, "#{label} (#{time} seconds) in #{name} policy" +
                      " in #{kasp_file} is more than 5 * TTL (#{ttl_secs} seconds)")
                end
              }
            }

            # Get the denial type (NSEC or NSEC3)
            denial_type = nil
            if (policy.elements['Denial/NSEC'])
              denial_type = "NSEC"
            else
              denial_type = "NSEC3"
              # Now check that the algorithm is correct
              policy.each_element('Denial/NSEC3/Hash/') {|hash|
                alg = hash.elements["Algorithm"].text
                if (alg.to_i != 1)
                  log(LOG_ERR, "NSEC3 Hash algorithm is #{alg} but should be 1");
                end
              }
            end

            # For all keys (if any are configured)...
            max = 9999999999999999
            ksk_lifetime = max
            zsk_lifetime = max
            policy.each_element('Keys/ZSK') {|zsk|
              check_key(zsk, "ZSK", name, kasp_file, denial_type)
              zskl = get_duration(zsk, 'Lifetime', kasp_file)
              zsk_lifetime = [zsk_lifetime, zskl].min
            }
            policy.each_element('Keys/KSK') {|ksk|
              check_key(ksk, "KSK", name, kasp_file, denial_type)
              kskl = get_duration(ksk, 'Lifetime', kasp_file)
              ksk_lifetime = [ksk_lifetime, kskl].min
            }

            #  12. Warn if for any zone, the KSK lifetime is less than the ZSK lifetime.
            if ((ksk_lifetime != max) && (zsk_lifetime != max) && (ksk_lifetime < zsk_lifetime))
              log(LOG_WARNING, "KSK minimum lifetime (#{ksk_lifetime} seconds)" +
                  " is less than ZSK minimum lifetime (#{zsk_lifetime} seconds)"+
                  " for #{name} Policy in #{kasp_file}")
            end

            # 15. Warn if resalt is less than resign interval.
            if (denial_type == "NSEC3")
              resign_secs = get_duration(policy,'Signatures/Resign', kasp_file)
              resalt_secs = get_duration(policy,'Denial/NSEC3/Resalt', kasp_file)
              if (resalt_secs)
                if (resalt_secs < resign_secs)
                  log(LOG_WARNING, "NSEC3 resalt interval (#{resalt_secs}) is less than" +
                      " signature resign interval (#{resign_secs})" +
                      " for #{name} Policy in #{kasp_file}")
                end
              end
            end

            #   9. If datecounter is used for serial, then no more than 99 signings should be done per day (there are only two digits to play with in the version number).
            resigns_per_day = (60 * 60 * 24) / resign_secs
            if (resigns_per_day > 99)
              # Check if the datecounter is used - if so, warn
              policy.each_element('Zone/SOA/Serial') {|serial|
                if (serial.text.downcase == "datecounter")
                  log(LOG_ERR, "In #{kasp_file}, policy #{name}, serial type datecounter used"+
                      " but #{resigns_per_day} re-signs requested."+
                      " No more than 99 re-signs per day should be used with datecounter"+
                      " as only 2 digits are allocated for the version number")
                  #  13. Check that the value of the "Serial" tag is valid.
                elsif !(["unixtime", "datecounter", "keep", "counter"].include?serial.text.downcase)
                  log(LOG_ERR, "In #{kasp_file}, policy #{name}, unknown Serial type encountered ('#{serial.text}')." +
                      " Should be either 'unixtime', 'counter', 'datecounter' or 'keep'")
                end
              }
            end
            ["Signatures/Resign", "Signatures/Refresh", "Signatures/Validity/Default",
              "Signatures/Validity/Denial", "Signatures/Jitter",
              "Signatures/InceptionOffset", "Keys/RetireSafety", "Keys/PublishSafety",
              "Keys/Purge", "NSEC3/Resalt", "SOA/Minimum", "ZSK/Lifetime",
              "KSK/Lifetime", "TTL", "PropagationDelay"].each {|element|
              policy.each_element(element) {|el| check_duration_element_proc(el, name, element, kasp_file)}
            }
          }

          #   1. Warn if a policy named "default" does not exist.
          if (!policy_names.include?"default")
            log(LOG_WARNING, "No policy named 'default' in #{kasp_file}. This " +
                "means you will need to refer explicitly to the policy for each zone")
          end

        }
      rescue Errno::ENOENT
        log(LOG_ERR, "Can't find KASP config file : #{kasp_file}")
      end
    end