Module: JWS
- Defined in:
- lib/jws.rb
Defined Under Namespace
Classes: DecodeError, VerificationError
Class Method Summary collapse
- .base64url_decode(str) ⇒ Object
- .base64url_encode(str) ⇒ Object
- .decode(jws, key = nil, verify = true, options = {}, &keyfinder) ⇒ Object
- .decode_header_and_payload(header_segment, payload_segment) ⇒ Object
- .decode_json(encoded_json) ⇒ Object
- .decoded_segments(jws, verify = true) ⇒ Object
- .encode(payload, key, algorithm = 'HS256', header_fields = {}) ⇒ Object
- .encode_json(raw) ⇒ Object
- .encoded_header(algorithm = 'HS256', header_fields = {}) ⇒ Object
- .encoded_payload(payload) ⇒ Object
- .encoded_signature(signing_input, key, algorithm) ⇒ Object
- .raw_segments(jws, verify = true) ⇒ Object
- .secure_compare(a, b) ⇒ Object
- .sign(algorithm, msg, key) ⇒ Object
- .sign_hmac(algorithm, msg, key) ⇒ Object
- .signature_algorithm_and_key(header, key, &keyfinder) ⇒ Object
- .verify_signature(algo, key, signing_input, signature) ⇒ Object
Class Method Details
.base64url_decode(str) ⇒ Object
23 24 25 26 |
# File 'lib/jws.rb', line 23 def base64url_decode(str) str += '=' *(4 - str.length.modulo(4)) Base64.decode64(str.tr('-_', '+/')) end |
.base64url_encode(str) ⇒ Object
28 29 30 |
# File 'lib/jws.rb', line 28 def base64url_encode(str) Base64.encode64(str).tr('+/', '-_').gsub(/[\n=]/, '') end |
.decode(jws, key = nil, verify = true, options = {}, &keyfinder) ⇒ Object
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/jws.rb', line 80 def decode(jws, key=nil, verify=true, ={}, &keyfinder) raise JWS::DecodeError.new('Nil JSON Web Signature') unless jws header, payload, signature, signing_input = decoded_segments(jws, verify) raise JWS::DecodeError.new('Not enough or too many segments') unless header && payload if verify algo, key = signature_algorithm_and_key(header, key, &keyfinder) if [:algorithm] && algo != [:algorithm] raise JWS::IncorrectAlgorithm.new('Expected a different algorithm') end verify_signature(algo, key, signing_input, signature) end return payload, header end |
.decode_header_and_payload(header_segment, payload_segment) ⇒ Object
66 67 68 69 70 |
# File 'lib/jws.rb', line 66 def decode_header_and_payload(header_segment, payload_segment) header = decode_json(base64url_decode(header_segment)) payload = decode_json(base64url_decode(payload_segment)) [header, payload] end |
.decode_json(encoded_json) ⇒ Object
126 127 128 129 130 |
# File 'lib/jws.rb', line 126 def decode_json(encoded_json) JSON.parse(encoded_json) rescue JSON::ParseError raise JOSE::DecodeError.new("Invalid encoding") end |
.decoded_segments(jws, verify = true) ⇒ Object
72 73 74 75 76 77 78 |
# File 'lib/jws.rb', line 72 def decoded_segments(jws, verify=true) header_segment, payload_segment, crypto_segment = raw_segments(jws, verify) header, payload = decode_header_and_payload(header_segment, payload_segment) signature = base64url_decode(crypto_segment.to_s) if verify signing_input = [header_segment, payload_segment].join('.') [header, payload, signature, signing_input] end |
.encode(payload, key, algorithm = 'HS256', header_fields = {}) ⇒ Object
50 51 52 53 54 55 56 57 |
# File 'lib/jws.rb', line 50 def encode(payload, key, algorithm='HS256', header_fields={}) algorithm ||= 'none' segments = [] segments << encoded_header(algorithm, header_fields) segments << encoded_payload(payload) segments << encoded_signature(segments.join('.'), key, algorithm) segments.join('.') end |
.encode_json(raw) ⇒ Object
132 133 134 |
# File 'lib/jws.rb', line 132 def encode_json(raw) JSON.generate(raw) end |
.encoded_header(algorithm = 'HS256', header_fields = {}) ⇒ Object
32 33 34 35 |
# File 'lib/jws.rb', line 32 def encoded_header(algorithm='HS256', header_fields={}) header = {'typ' => 'JWS', 'alg' => algorithm}.merge(header_fields) base64url_encode(encode_json(header)) end |
.encoded_payload(payload) ⇒ Object
37 38 39 |
# File 'lib/jws.rb', line 37 def encoded_payload(payload) base64url_encode(encode_json(payload)) end |
.encoded_signature(signing_input, key, algorithm) ⇒ Object
41 42 43 44 45 46 47 48 |
# File 'lib/jws.rb', line 41 def encoded_signature(signing_input, key, algorithm) if algorithm == 'none' '' else signature = sign(algorithm, signing_input, key) base64url_encode(signature) end end |
.raw_segments(jws, verify = true) ⇒ Object
59 60 61 62 63 64 |
# File 'lib/jws.rb', line 59 def raw_segments(jws, verify=true) segments = jws.split('.') required_number_of_segments = verify ? [3] :[2,3] raise JWS::DecodeError.new('Not enough or too many segments') unless required_number_of_segments.include? segments.length segments end |
.secure_compare(a, b) ⇒ Object
117 118 119 120 121 122 123 124 |
# File 'lib/jws.rb', line 117 def secure_compare(a, b) return false if a.nil? || b.nil? || a.empty? || b.empty? || a.bytesize != b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end |
.sign(algorithm, msg, key) ⇒ Object
11 12 13 14 15 16 17 |
# File 'lib/jws.rb', line 11 def sign(algorithm, msg, key) if ['HS256', 'HS384', 'HS512'].include?(algorithm) sign_hmac(algorithm, msg, key) else raise NotImplementedError.new("Unsupported signing mehtod") end end |
.sign_hmac(algorithm, msg, key) ⇒ Object
19 20 21 |
# File 'lib/jws.rb', line 19 def sign_hmac(algorithm, msg, key) OpenSSL::HMAC.digest(OpenSSL::Digest.new(algorithm.sub('HS', 'SHA')), key, msg) end |
.signature_algorithm_and_key(header, key, &keyfinder) ⇒ Object
96 97 98 99 100 101 |
# File 'lib/jws.rb', line 96 def signature_algorithm_and_key(header, key, &keyfinder) if keyfinder key = keyfinder.call(header) end [header['alg'], key] end |
.verify_signature(algo, key, signing_input, signature) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/jws.rb', line 103 def verify_signature(algo, key, signing_input, signature) begin if ['HS256', 'HS384', 'HS512'].include?(algo) raise JWS::VerificationError.new('Signature verification failed') unless secure_compare(signature, sign_hmac(algo, signing_input, key)) else raise JWS::VerificationError.new('Algorithm not supported') end rescue OpenSSL::Pkey::PkeyError raise JWS::VerificationError.new('Signature verification failed') ensure OpenSSL.errors.clear end end |