Class: Sqreen::SqreenSignedVerifier

Inherits:
Object
  • Object
show all
Defined in:
lib/sqreen/sqreen_signed_verifier.rb

Overview

Normalize and verify a rule

Constant Summary collapse

REQUIRED_SIGNED_KEYS =
%w[hookpoint name callbacks conditions].freeze
SIGNATURE_KEY =
'signature'.freeze
SIGNATURE_VALUE_KEY =
'value'.freeze
SIGNED_KEYS_KEY =
'keys'.freeze
SIGNATURE_VERSION =
'v0_9'.freeze
PUBLIC_KEY =
<<-KEY.gsub(/^ */, '').freeze
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQA39oWMHR8sxb9LRaM5evZ7mw03iwJ
WNHuDeGqgPo1HmvuMfLnAyVLwaMXpGPuvbqhC1U65PG90bTJLpvNokQf0VMA5Tpi
m+NXwl7bjqa03vO/HErLbq3zBRysrZnC4OhJOF1jazkAg0psQOea2r5HcMcPHgMK
fnWXiKWnZX+uOWPuerE=
-----END PUBLIC KEY-----
KEY

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(required_keys = REQUIRED_SIGNED_KEYS, public_key = PUBLIC_KEY, digest = OpenSSL::Digest::SHA512.new, use_oj_gem = nil) ⇒ SqreenSignedVerifier

Returns a new instance of SqreenSignedVerifier.



47
48
49
50
51
52
53
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 47

def initialize(required_keys = REQUIRED_SIGNED_KEYS,
               public_key    = PUBLIC_KEY,
               digest        = OpenSSL::Digest::SHA512.new, use_oj_gem = nil)
  @required_signed_keys = required_keys
  @signature_verifier   = SignatureVerifier.new(public_key, digest)
  @use_oj = use_oj_gem.nil? ? OJ_LOADED : use_oj_gem
end

Instance Attribute Details

#digestObject

Returns the value of attribute digest.



44
45
46
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 44

def digest
  @digest
end

#pub_keyObject

Returns the value of attribute pub_key.



42
43
44
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 42

def pub_key
  @pub_key
end

#required_signed_keysObject

Returns the value of attribute required_signed_keys.



43
44
45
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 43

def required_signed_keys
  @required_signed_keys
end

#use_ojObject

Returns the value of attribute use_oj.



45
46
47
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 45

def use_oj
  @use_oj
end

Instance Method Details

#get_sig_infos_or_fail(hash_rule) ⇒ Object

Raises:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 121

def get_sig_infos_or_fail(hash_rule)
  raise Sqreen::Exception, 'non hash argument' unless hash_rule.is_a?(Hash)

  sigs = hash_rule[SIGNATURE_KEY]
  raise Sqreen::Exception, 'no signature found' unless sigs

  sig = sigs[SIGNATURE_VERSION]
  msg = "signature #{SIGNATURE_VERSION} not found (#{sigs})"
  raise Sqreen::Exception, msg unless sig

  sig_value = sig[SIGNATURE_VALUE_KEY]
  raise Sqreen::Exception, 'no signature value found' unless sig_value

  signed_keys = sig[SIGNED_KEYS_KEY]
  raise Sqreen::Exception, "no signed keys found (#{sig})" unless signed_keys

  inc = Set.new(signed_keys).superset?(Set.new(@required_signed_keys))
  raise Sqreen::Exception, 'signed keys miss equired keys' unless inc

  [signed_keys, sig_value]
end

#normalize(hash_rule, signed_keys = nil, level = 20) ⇒ Object

Raises:



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 94

def normalize(hash_rule, signed_keys = nil, level = 20)
  # Normalize the provided hash to a string:
  #  - sort keys lexicographically, recursively
  #  - convert each scalar to its JSON representation
  #  - convert hash to '{key:value}'
  #  - convert array [v1,v2] to '[v1,v2]' and [] to '[]'
  # Two hash with different key ordering should have the same normalized
  # value.

  raise Sqreen::Exception, 'recursion level too deep' if level == 0
  unless hash_rule.is_a?(Hash)
    raise Sqreen::Exception, "wrong hash type #{hash_rule.class}"
  end

  res = []
  hash_rule.sort.each do |k, v|
    # Only keep signed keys
    next if signed_keys && !signed_keys.include?(k)

    k = normalize_key(k)
    v = normalize_val(v, level - 1)

    res << "#{k}:#{v}"
  end
  "{#{res.join(',')}}"
end

#normalize_key(key) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 79

def normalize_key(key)
  case key
  when String, Integer
    return Oj.dump(key, :mode => :compat, :escape_mode => :json) if use_oj
    begin
      JSON.dump(key)
    rescue JSON::GeneratorError
      JSON.generate(key, :quirks_mode => true)
    end
  else
    msg = "JSON hash parsing error (wrong key type: #{key.class})"
    raise Sqreen::Exception, msg
  end
end

#normalize_val(val, level) ⇒ Object

Raises:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 55

def normalize_val(val, level)
  raise Sqreen::Exception, 'recursion level too deep' if level == 0

  case val
  when Hash
    normalize(val, nil, level - 1)
  when Array
    ary = val.map do |i|
      normalize_val(i, level - 1)
    end
    "[#{ary.join(',')}]"
  when String, Integer
    return Oj.dump(val, :mode => :compat, :escape_mode => :json) if use_oj
    begin
      JSON.dump(val)
    rescue JSON::GeneratorError
      JSON.generate(val, :quirks_mode => true)
    end
  else
    msg = "JSON hash parsing error (wrong value type: #{val.class})"
    raise Sqreen::Exception.new, msg
  end
end

#verify(hash_rule) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
# File 'lib/sqreen/sqreen_signed_verifier.rb', line 143

def verify(hash_rule)
  # Return true if rule signature is correct, else false

  signed_keys, sig_value = get_sig_infos_or_fail(hash_rule)

  norm_str = normalize(hash_rule, signed_keys)
  bin_sig = Base64.decode64(sig_value)
  @signature_verifier.verify(bin_sig, norm_str)
rescue OpenSSL::PKey::ECError
  false
end