Class: Tep::Jwt

Inherits:
Object
  • Object
show all
Defined in:
lib/tep/jwt.rb

Constant Summary collapse

HEADER_B64U =

base64url-encoded ‘“alg”:“HS256”,“typ”:“JWT”`.

"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"

Class Method Summary collapse

Class Method Details

.decode_payload(token) ⇒ Object

Pull the JSON-encoded payload back out of a token. No signature verification – call ‘verify_hs256` first if you haven’t, OR use the wrapped ‘verify_and_decode` form.



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/tep/jwt.rb', line 82

def self.decode_payload(token)
  d1 = Tep.str_find(token, ".", 0)
  if d1 < 0
    return ""
  end
  d2 = Tep.str_find(token, ".", d1 + 1)
  if d2 < 0
    return ""
  end
  payload_b64 = token[d1 + 1, d2 - d1 - 1]
  Crypto.sp_crypto_b64url_decode(payload_b64)
end

.encode_hs256(payload_json, secret) ⇒ Object

Build a token from a JSON-encoded payload string and the signing secret. Returns the three-segment ‘header.payload.sig` string.



55
56
57
58
59
60
# File 'lib/tep/jwt.rb', line 55

def self.encode_hs256(payload_json, secret)
  payload_b64 = Crypto.sp_crypto_b64url_encode(payload_json)
  signing_input = HEADER_B64U + "." + payload_b64
  sig = Crypto.sp_crypto_hmac_sha256_b64url(secret, signing_input)
  signing_input + "." + sig
end

.timing_safe_eq(a, b) ⇒ Object

Constant-time string compare. Returns true iff strings are byte-identical. Used so a token-signature mismatch leaks no timing info about how many leading bytes matched.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/tep/jwt.rb', line 108

def self.timing_safe_eq(a, b)
  if a.length != b.length
    return false
  end
  diff = 0
  i = 0
  n = a.length
  while i < n
    # getbyte(i), NOT bytes[i] -- the same O(n^2)-allocation +
    # GC-pressure hazard fixed in Tep::Session.timing_safe_eq
    # (allocation storm can free the FFI-returned signature local
    # mid-compare; #1052-family spinel rooting gap, see tep#157).
    diff = diff | (a.getbyte(i) ^ b.getbyte(i))
    i += 1
  end
  diff == 0
end

.verify_and_decode(token, secret) ⇒ Object

One-shot: verify, then decode. Returns the JSON payload on success, “” on bad signature / malformed token. Saves callers an explicit early-return check.



98
99
100
101
102
103
# File 'lib/tep/jwt.rb', line 98

def self.verify_and_decode(token, secret)
  if !Jwt.verify_hs256(token, secret)
    return ""
  end
  Jwt.decode_payload(token)
end

.verify_hs256(token, secret) ⇒ Object

Verify the signature on a token. Returns true / false. Does NOT check claim semantics (exp / nbf / iss / aud).



64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/tep/jwt.rb', line 64

def self.verify_hs256(token, secret)
  d1 = Tep.str_find(token, ".", 0)
  if d1 < 0
    return false
  end
  d2 = Tep.str_find(token, ".", d1 + 1)
  if d2 < 0
    return false
  end
  signing_input = token[0, d2]
  provided_sig = token[d2 + 1, token.length - d2 - 1]
  expected_sig = Crypto.sp_crypto_hmac_sha256_b64url(secret, signing_input)
  Jwt.timing_safe_eq(provided_sig, expected_sig)
end