Class: CsrPeek::Policy

Inherits:
Object
  • Object
show all
Defined in:
lib/csr_peek/policy.rb

Overview

An issuance policy: the rules a key or certificate must satisfy to be acceptable, as opposed to whether a key is cryptographically weak.

These are deliberately separate ideas. An Ed25519 key is not weak, but a policy scoped to the CA/Browser Forum Baseline Requirements does not permit it for public TLS. #weak_key? answers the maths; a Policy answers "may I issue against this?". Pass your own Policy to raise the bar (3072-bit RSA, a curve allowlist, a compromised-key blocklist) without monkey-patching.

strict = CsrPeek::Policy.new(min_rsa_bits: 3072, allowed_curves: %w[secp384r1])
csr.acceptable_key?(strict)          # => false
csr.key_policy_violations(strict)     # => [:rsa_too_small]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(min_rsa_bits: 2048, min_ec_bits: 256, min_dsa_bits: 2048, allowed_key_types: %w[RSA EC],, allowed_curves: nil, weak_signature_hashes: %w[md2 md4 md5 sha1],, blocked_spki_fingerprints: []) ⇒ Policy

allowed_key_types / allowed_curves default to nil meaning "no restriction on this axis". blocked_spki_fingerprints is matched case-insensitively and ignoring colons, so "AA:BB" and "aabb" are the same entry.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/csr_peek/policy.rb', line 23

def initialize(min_rsa_bits: 2048, min_ec_bits: 256, min_dsa_bits: 2048,
  allowed_key_types: %w[RSA EC], allowed_curves: nil,
  weak_signature_hashes: %w[md2 md4 md5 sha1],
  blocked_spki_fingerprints: [])
  @min_rsa_bits = min_rsa_bits
  @min_ec_bits = min_ec_bits
  @min_dsa_bits = min_dsa_bits
  @allowed_key_types = allowed_key_types&.map(&:to_s)&.to_set&.freeze
  @allowed_curves = allowed_curves&.map(&:to_s)&.to_set&.freeze
  @weak_signature_hashes = weak_signature_hashes.map { |h| h.to_s.downcase }.freeze
  @blocked_spki_fingerprints = blocked_spki_fingerprints
    .map { |f| normalize_fingerprint(f) }.to_set.freeze
  freeze
end

Instance Attribute Details

#allowed_curvesObject (readonly)

Returns the value of attribute allowed_curves.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def allowed_curves
  @allowed_curves
end

#allowed_key_typesObject (readonly)

Returns the value of attribute allowed_key_types.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def allowed_key_types
  @allowed_key_types
end

#blocked_spki_fingerprintsObject (readonly)

Returns the value of attribute blocked_spki_fingerprints.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def blocked_spki_fingerprints
  @blocked_spki_fingerprints
end

#min_dsa_bitsObject (readonly)

Returns the value of attribute min_dsa_bits.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def min_dsa_bits
  @min_dsa_bits
end

#min_ec_bitsObject (readonly)

Returns the value of attribute min_ec_bits.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def min_ec_bits
  @min_ec_bits
end

#min_rsa_bitsObject (readonly)

Returns the value of attribute min_rsa_bits.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def min_rsa_bits
  @min_rsa_bits
end

#weak_signature_hashesObject (readonly)

Returns the value of attribute weak_signature_hashes.



17
18
19
# File 'lib/csr_peek/policy.rb', line 17

def weak_signature_hashes
  @weak_signature_hashes
end

Instance Method Details

#blocked?(spki_fingerprint) ⇒ Boolean

Returns:

  • (Boolean)


58
59
60
61
62
# File 'lib/csr_peek/policy.rb', line 58

def blocked?(spki_fingerprint)
  return false if spki_fingerprint.nil? || blocked_spki_fingerprints.empty?

  blocked_spki_fingerprints.include?(normalize_fingerprint(spki_fingerprint))
end

#curve_allowed?(curve) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/csr_peek/policy.rb', line 54

def curve_allowed?(curve)
  allowed_curves.nil? || (curve && allowed_curves.include?(curve.to_s))
end

#key_type_allowed?(type) ⇒ Boolean

Returns:

  • (Boolean)


50
51
52
# File 'lib/csr_peek/policy.rb', line 50

def key_type_allowed?(type)
  allowed_key_types.nil? || allowed_key_types.include?(type.to_s)
end

#key_violations(type:, bits:, curve:, spki_fingerprint: nil) ⇒ Object

The reasons this key fails the policy, as an array of symbols. Empty means acceptable. Inputs are the plain values an inspector already computes.



40
41
42
43
44
45
46
47
48
# File 'lib/csr_peek/policy.rb', line 40

def key_violations(type:, bits:, curve:, spki_fingerprint: nil)
  return [:key_unreadable] if type.nil? || type == "unknown"

  violations = []
  violations << :unsupported_key_type unless key_type_allowed?(type)
  violations.concat(size_violations(type, bits, curve))
  violations << :key_blocklisted if blocked?(spki_fingerprint)
  violations
end

#weak_signature?(signature_algorithm) ⇒ Boolean

True when a certificate's signature algorithm uses a hash this policy rejects (MD5, SHA-1, ...), matched as a substring of the lower-cased name. An unknown (blank) algorithm fails closed - "cannot verify" is not "safe", the same stance #weak_key? takes for an unloadable key.

Returns:

  • (Boolean)


68
69
70
71
72
73
# File 'lib/csr_peek/policy.rb', line 68

def weak_signature?(signature_algorithm)
  name = signature_algorithm.to_s.downcase
  return true if name.empty?

  weak_signature_hashes.any? { |h| name.include?(h) }
end