Class: Shugoi::Pow
- Inherits:
-
Object
- Object
- Shugoi::Pow
- Defined in:
- lib/shugoi/pow.rb
Constant Summary collapse
- POW_OK_TTL_MS =
30 * 24 * 3600 * 1000
- AUTHORIZED_TTL_MS =
120_000
Instance Method Summary collapse
- #challenge ⇒ Object
-
#initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) ⇒ Pow
constructor
A new instance of Pow.
- #sg_authorized_cookie ⇒ Object
- #sg_authorized_valid?(cookie_val) ⇒ Boolean
- #sg_authorized_value ⇒ Object
- #sg_ok_cookie(proof, ip = "", ua = "") ⇒ Object
- #sg_ok_valid?(cookie_val, ip = "", ua = "") ⇒ Boolean
- #sg_ok_value(ip = "", ua = "") ⇒ Object
- #valid?(proof) ⇒ Boolean
Constructor Details
#initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) ⇒ Pow
Returns a new instance of Pow.
8 9 10 11 12 |
# File 'lib/shugoi/pow.rb', line 8 def initialize(signing_secret, difficulty = 14, ttl_ms = 60_000) @secret = signing_secret.to_s @difficulty = difficulty @ttl_ms = ttl_ms end |
Instance Method Details
#challenge ⇒ Object
14 15 16 17 |
# File 'lib/shugoi/pow.rb', line 14 def challenge ts = Utils.now_sec { ts: ts, nonce: nonce, salt: salt(ts, nonce), difficulty: @difficulty } end |
#sg_authorized_cookie ⇒ Object
78 79 80 81 |
# File 'lib/shugoi/pow.rb', line 78 def secure = production? ? "; Secure" : "" "__sg_authorized=#{}; Path=/; HttpOnly; SameSite=Strict; Max-Age=120#{secure}" end |
#sg_authorized_valid?(cookie_val) ⇒ Boolean
65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/shugoi/pow.rb', line 65 def () return false if @secret.empty? || .to_s.empty? ts_str, sig = .to_s.split(":", 2) return false if ts_str.nil? || sig.nil? ts = ts_str.to_i return false if ts.zero? return false if Utils.now_ms - (ts * 1000) > AUTHORIZED_TTL_MS return false if ts * 1000 > Utils.now_ms + 60_000 Utils.secure_equals(sig, Utils.hmac_hex(@secret, "sg_authorized:#{ts_str}")) end |
#sg_authorized_value ⇒ Object
60 61 62 63 |
# File 'lib/shugoi/pow.rb', line 60 def ts = Utils.now_sec "#{ts}:#{Utils.hmac_hex(@secret, "sg_authorized:#{ts}")}" end |
#sg_ok_cookie(proof, ip = "", ua = "") ⇒ Object
54 55 56 57 58 |
# File 'lib/shugoi/pow.rb', line 54 def (proof, ip = "", ua = "") return nil unless valid?(proof) secure = production? ? "; Secure" : "" "__sg_ok=#{sg_ok_value(ip, ua)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=#{POW_OK_TTL_MS / 1000}#{secure}" end |
#sg_ok_valid?(cookie_val, ip = "", ua = "") ⇒ Boolean
40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/shugoi/pow.rb', line 40 def sg_ok_valid?(, ip = "", ua = "") return false if @secret.empty? || .to_s.empty? ts_str, bucket, fp, sig = .to_s.split(":", 4) return false if ts_str.nil? || bucket.nil? || fp.nil? || sig.nil? ts = ts_str.to_i return false if ts.zero? return false if Utils.now_ms - (ts * 1000) > POW_OK_TTL_MS return false if ts * 1000 > Utils.now_ms + 60_000 return false unless bucket == ip_bucket(ip) && fp == ua_fp(ua) Utils.secure_equals(sig, Utils.hmac_hex(@secret, "sg_ok:#{ts_str}:#{bucket}:#{fp}")) end |
#sg_ok_value(ip = "", ua = "") ⇒ Object
33 34 35 36 37 38 |
# File 'lib/shugoi/pow.rb', line 33 def sg_ok_value(ip = "", ua = "") ts = Utils.now_sec bucket = ip_bucket(ip) fp = ua_fp(ua) "#{ts}:#{bucket}:#{fp}:#{Utils.hmac_hex(@secret, "sg_ok:#{ts}:#{bucket}:#{fp}")}" end |
#valid?(proof) ⇒ Boolean
19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/shugoi/pow.rb', line 19 def valid?(proof) return false if proof.to_s.empty? || @secret.empty? ts_str, nonce, solution = proof.to_s.split(":", 3) return false if ts_str.nil? || nonce.nil? || solution.nil? return false unless nonce.match?(/\A[0-9a-f]{16}\z/) ts = ts_str.to_i return false if ts.zero? return false if (Utils.now_ms - (ts * 1000)).abs > @ttl_ms digest = Utils.sha256_hex("#{salt(ts_str, nonce)}:#{solution}") Utils.leading_zero_bits(digest) >= @difficulty end |