jose 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.gitignore +9 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +4 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +20 -0
  9. data/LICENSE.txt +373 -0
  10. data/README.md +41 -0
  11. data/Rakefile +10 -0
  12. data/bin/console +14 -0
  13. data/bin/setup +7 -0
  14. data/jose.gemspec +36 -0
  15. data/lib/jose.rb +61 -0
  16. data/lib/jose/jwa.rb +21 -0
  17. data/lib/jose/jwa/aes_kw.rb +105 -0
  18. data/lib/jose/jwa/concat_kdf.rb +50 -0
  19. data/lib/jose/jwa/pkcs1.rb +269 -0
  20. data/lib/jose/jwa/pkcs7.rb +23 -0
  21. data/lib/jose/jwe.rb +290 -0
  22. data/lib/jose/jwe/alg.rb +12 -0
  23. data/lib/jose/jwe/alg_aes_gcm_kw.rb +98 -0
  24. data/lib/jose/jwe/alg_aes_kw.rb +57 -0
  25. data/lib/jose/jwe/alg_dir.rb +40 -0
  26. data/lib/jose/jwe/alg_ecdh_es.rb +123 -0
  27. data/lib/jose/jwe/alg_pbes2.rb +90 -0
  28. data/lib/jose/jwe/alg_rsa.rb +63 -0
  29. data/lib/jose/jwe/enc.rb +8 -0
  30. data/lib/jose/jwe/enc_aes_cbc_hmac.rb +80 -0
  31. data/lib/jose/jwe/enc_aes_gcm.rb +68 -0
  32. data/lib/jose/jwe/zip.rb +7 -0
  33. data/lib/jose/jwe/zip_def.rb +28 -0
  34. data/lib/jose/jwk.rb +347 -0
  35. data/lib/jose/jwk/kty.rb +34 -0
  36. data/lib/jose/jwk/kty_ec.rb +179 -0
  37. data/lib/jose/jwk/kty_oct.rb +104 -0
  38. data/lib/jose/jwk/kty_rsa.rb +185 -0
  39. data/lib/jose/jwk/pem.rb +19 -0
  40. data/lib/jose/jwk/set.rb +2 -0
  41. data/lib/jose/jws.rb +242 -0
  42. data/lib/jose/jws/alg.rb +10 -0
  43. data/lib/jose/jws/alg_ecdsa.rb +41 -0
  44. data/lib/jose/jws/alg_hmac.rb +41 -0
  45. data/lib/jose/jws/alg_rsa_pkcs1_v1_5.rb +41 -0
  46. data/lib/jose/jws/alg_rsa_pss.rb +41 -0
  47. data/lib/jose/jwt.rb +145 -0
  48. data/lib/jose/version.rb +3 -0
  49. metadata +162 -0
@@ -0,0 +1,10 @@
1
+ module JOSE::JWS::ALG
2
+
3
+ extend self
4
+
5
+ end
6
+
7
+ require 'jose/jws/alg_ecdsa'
8
+ require 'jose/jws/alg_hmac'
9
+ require 'jose/jws/alg_rsa_pkcs1_v1_5'
10
+ require 'jose/jws/alg_rsa_pss'
@@ -0,0 +1,41 @@
1
+ class JOSE::JWS::ALG_ECDSA < Struct.new(:digest)
2
+
3
+ # JOSE::JWS callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['alg']
7
+ when 'ES256'
8
+ return new(OpenSSL::Digest::SHA256), fields.delete('alg')
9
+ when 'ES384'
10
+ return new(OpenSSL::Digest::SHA384), fields.delete('alg')
11
+ when 'ES512'
12
+ return new(OpenSSL::Digest::SHA512), fields.delete('alg')
13
+ else
14
+ raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
15
+ end
16
+ end
17
+
18
+ def to_map(fields)
19
+ alg = if digest == OpenSSL::Digest::SHA256
20
+ 'ES256'
21
+ elsif digest == OpenSSL::Digest::SHA384
22
+ 'ES384'
23
+ elsif digest == OpenSSL::Digest::SHA512
24
+ 'ES512'
25
+ else
26
+ raise ArgumentError, "unhandled ECDSA digest type: #{digest.inspect}"
27
+ end
28
+ return fields.put('alg', alg)
29
+ end
30
+
31
+ # JOSE::JWS::ALG callbacks
32
+
33
+ def sign(jwk, message)
34
+ return jwk.kty.sign(message, digest)
35
+ end
36
+
37
+ def verify(jwk, message, signature)
38
+ return jwk.kty.verify(message, digest, signature)
39
+ end
40
+
41
+ end
@@ -0,0 +1,41 @@
1
+ class JOSE::JWS::ALG_HMAC < Struct.new(:hmac)
2
+
3
+ # JOSE::JWS callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['alg']
7
+ when 'HS256'
8
+ return new(OpenSSL::Digest::SHA256), fields.delete('alg')
9
+ when 'HS384'
10
+ return new(OpenSSL::Digest::SHA384), fields.delete('alg')
11
+ when 'HS512'
12
+ return new(OpenSSL::Digest::SHA512), fields.delete('alg')
13
+ else
14
+ raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
15
+ end
16
+ end
17
+
18
+ def to_map(fields)
19
+ alg = if hmac == OpenSSL::Digest::SHA256
20
+ 'HS256'
21
+ elsif hmac == OpenSSL::Digest::SHA384
22
+ 'HS384'
23
+ elsif hmac == OpenSSL::Digest::SHA512
24
+ 'HS512'
25
+ else
26
+ raise ArgumentError, "unhandled HMAC digest type: #{hmac.inspect}"
27
+ end
28
+ return fields.put('alg', alg)
29
+ end
30
+
31
+ # JOSE::JWS::ALG callbacks
32
+
33
+ def sign(jwk, message)
34
+ return jwk.kty.sign(message, hmac)
35
+ end
36
+
37
+ def verify(jwk, message, signature)
38
+ return jwk.kty.verify(message, hmac, signature)
39
+ end
40
+
41
+ end
@@ -0,0 +1,41 @@
1
+ class JOSE::JWS::ALG_RSA_PKCS1_V1_5 < Struct.new(:digest)
2
+
3
+ # JOSE::JWS callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['alg']
7
+ when 'RS256'
8
+ return new(OpenSSL::Digest::SHA256), fields.delete('alg')
9
+ when 'RS384'
10
+ return new(OpenSSL::Digest::SHA384), fields.delete('alg')
11
+ when 'RS512'
12
+ return new(OpenSSL::Digest::SHA512), fields.delete('alg')
13
+ else
14
+ raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
15
+ end
16
+ end
17
+
18
+ def to_map(fields)
19
+ alg = if digest == OpenSSL::Digest::SHA256
20
+ 'RS256'
21
+ elsif digest == OpenSSL::Digest::SHA384
22
+ 'RS384'
23
+ elsif digest == OpenSSL::Digest::SHA512
24
+ 'RS512'
25
+ else
26
+ raise ArgumentError, "unhandled RSA_PKCS1_v1_5 digest type: #{digest.inspect}"
27
+ end
28
+ return fields.put('alg', alg)
29
+ end
30
+
31
+ # JOSE::JWS::ALG callbacks
32
+
33
+ def sign(jwk, message)
34
+ return jwk.kty.sign(message, digest, padding: :rsa_pkcs1_padding)
35
+ end
36
+
37
+ def verify(jwk, message, signature)
38
+ return jwk.kty.verify(message, digest, signature, padding: :rsa_pkcs1_padding)
39
+ end
40
+
41
+ end
@@ -0,0 +1,41 @@
1
+ class JOSE::JWS::ALG_RSA_PSS < Struct.new(:digest)
2
+
3
+ # JOSE::JWS callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['alg']
7
+ when 'PS256'
8
+ return new(OpenSSL::Digest::SHA256), fields.delete('alg')
9
+ when 'PS384'
10
+ return new(OpenSSL::Digest::SHA384), fields.delete('alg')
11
+ when 'PS512'
12
+ return new(OpenSSL::Digest::SHA512), fields.delete('alg')
13
+ else
14
+ raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
15
+ end
16
+ end
17
+
18
+ def to_map(fields)
19
+ alg = if digest == OpenSSL::Digest::SHA256
20
+ 'PS256'
21
+ elsif digest == OpenSSL::Digest::SHA384
22
+ 'PS384'
23
+ elsif digest == OpenSSL::Digest::SHA512
24
+ 'PS512'
25
+ else
26
+ raise ArgumentError, "unhandled RSA_PSS digest type: #{digest.inspect}"
27
+ end
28
+ return fields.put('alg', alg)
29
+ end
30
+
31
+ # JOSE::JWS::ALG callbacks
32
+
33
+ def sign(jwk, message)
34
+ return jwk.kty.sign(message, digest, padding: :rsa_pkcs1_pss_padding)
35
+ end
36
+
37
+ def verify(jwk, message, signature)
38
+ return jwk.kty.verify(message, digest, signature, padding: :rsa_pkcs1_pss_padding)
39
+ end
40
+
41
+ end
data/lib/jose/jwt.rb ADDED
@@ -0,0 +1,145 @@
1
+ module JOSE
2
+
3
+ class JWT < Struct.new(:fields)
4
+
5
+ # Decode API
6
+
7
+ def self.from(object, modules = {})
8
+ case object
9
+ when JOSE::Map, Hash
10
+ return from_map(object, modules)
11
+ when String
12
+ return from_binary(object, modules)
13
+ when JOSE::JWT
14
+ return object
15
+ else
16
+ raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWT"
17
+ end
18
+ end
19
+
20
+ def self.from_binary(object, modules = {})
21
+ case object
22
+ when String
23
+ return from_map(JOSE.decode(object), modules)
24
+ else
25
+ raise ArgumentError, "'object' must be a String"
26
+ end
27
+ end
28
+
29
+ def self.from_file(file, modules = {})
30
+ return from_binary(File.binread(file), modules)
31
+ end
32
+
33
+ def self.from_map(object, modules = {})
34
+ case object
35
+ when JOSE::Map, Hash
36
+ return from_fields(JOSE::JWT.new(JOSE::Map.new(object)), modules)
37
+ else
38
+ raise ArgumentError, "'object' must be a Hash"
39
+ end
40
+ end
41
+
42
+ # Encode API
43
+
44
+ def self.to_binary(jwt)
45
+ return from(jwt).to_binary
46
+ end
47
+
48
+ def to_binary
49
+ return JOSE.encode(to_map)
50
+ end
51
+
52
+ def self.to_file(jwt, file)
53
+ return from(jwt).to_file(file)
54
+ end
55
+
56
+ def to_file(file)
57
+ return File.binwrite(file, to_binary)
58
+ end
59
+
60
+ def self.to_map(jwt)
61
+ return from(jwt).to_map
62
+ end
63
+
64
+ def to_map
65
+ return fields
66
+ end
67
+
68
+ # API
69
+
70
+ def self.decrypt(jwk, encrypted)
71
+ decrypted, jwe = JOSE::JWK.block_decrypt(jwk, encrypted)
72
+ return from_binary(decrypted), jwe
73
+ end
74
+
75
+ def self.encrypt(jwt, jwk, jwe = nil)
76
+ return from(jwt).encrypt(jwk, jwe)
77
+ end
78
+
79
+ def encrypt(jwk, jwe = nil)
80
+ plain_text = to_binary
81
+ if jwe.nil?
82
+ jwk = JOSE::JWK.from(jwk)
83
+ jwe = jwk.kty.block_encryptor(jwk.fields, plain_text)
84
+ end
85
+ if jwe.is_a?(Hash)
86
+ jwe = JOSE::Map.new(jwe)
87
+ end
88
+ if jwe.is_a?(JOSE::Map) and not jwe.has_key?('typ')
89
+ jwe = jwe.put('typ', 'JWT')
90
+ end
91
+ return JOSE::JWK.block_encrypt(jwk, plain_text, jwe)
92
+ end
93
+
94
+ def self.peek_payload(signed)
95
+ return JOSE::Map.new(JOSE.decode(JOSE::JWS.peek_payload(signed)))
96
+ end
97
+
98
+ def self.peek_protected(signed)
99
+ return JOSE::JWS.peek_protected(signed)
100
+ end
101
+
102
+ def self.sign(jwt, jwk, jws = nil, header = nil)
103
+ return from(jwt).sign(jwk, jws)
104
+ end
105
+
106
+ def sign(jwk, jws = nil, header = nil)
107
+ plain_text = to_binary
108
+ if jws.nil?
109
+ jwk = JOSE::JWK.from(jwk)
110
+ jws = jwk.kty.signer(jwk.fields, plain_text)
111
+ end
112
+ if jws.is_a?(Hash)
113
+ jws = JOSE::Map.new(jws)
114
+ end
115
+ if jws.is_a?(JOSE::Map) and not jws.has_key?('typ')
116
+ jws = jws.put('typ', 'JWT')
117
+ end
118
+ return JOSE::JWK.sign(jwk, plain_text, jws, header)
119
+ end
120
+
121
+ def self.verify(jwk, signed)
122
+ verified, payload, jws = JOSE::JWK.verify(signed, jwk)
123
+ jwt = from_binary(payload)
124
+ return verified, jwt, jws
125
+ end
126
+
127
+ def self.verify_strict(jwk, allow, signed)
128
+ verified, payload, jws = JOSE::JWK.verify(signed, allow, jwk)
129
+ jwt = payload
130
+ if verified
131
+ jwt = from_binary(payload)
132
+ end
133
+ return verified, jwt, jws
134
+ end
135
+
136
+ private
137
+
138
+ def self.from_fields(jwt, modules)
139
+ return jwt
140
+ end
141
+
142
+ end
143
+ end
144
+
145
+ require 'jose/jws/alg'
@@ -0,0 +1,3 @@
1
+ module JOSE
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jose
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Bennett
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-11-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: hamster
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: JSON Object Signing and Encryption
84
+ email:
85
+ - andrew@pixid.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".editorconfig"
91
+ - ".gitignore"
92
+ - ".ruby-gemset"
93
+ - ".ruby-version"
94
+ - ".travis.yml"
95
+ - CODE_OF_CONDUCT.md
96
+ - Gemfile
97
+ - LICENSE.txt
98
+ - README.md
99
+ - Rakefile
100
+ - bin/console
101
+ - bin/setup
102
+ - jose.gemspec
103
+ - lib/jose.rb
104
+ - lib/jose/jwa.rb
105
+ - lib/jose/jwa/aes_kw.rb
106
+ - lib/jose/jwa/concat_kdf.rb
107
+ - lib/jose/jwa/pkcs1.rb
108
+ - lib/jose/jwa/pkcs7.rb
109
+ - lib/jose/jwe.rb
110
+ - lib/jose/jwe/alg.rb
111
+ - lib/jose/jwe/alg_aes_gcm_kw.rb
112
+ - lib/jose/jwe/alg_aes_kw.rb
113
+ - lib/jose/jwe/alg_dir.rb
114
+ - lib/jose/jwe/alg_ecdh_es.rb
115
+ - lib/jose/jwe/alg_pbes2.rb
116
+ - lib/jose/jwe/alg_rsa.rb
117
+ - lib/jose/jwe/enc.rb
118
+ - lib/jose/jwe/enc_aes_cbc_hmac.rb
119
+ - lib/jose/jwe/enc_aes_gcm.rb
120
+ - lib/jose/jwe/zip.rb
121
+ - lib/jose/jwe/zip_def.rb
122
+ - lib/jose/jwk.rb
123
+ - lib/jose/jwk/kty.rb
124
+ - lib/jose/jwk/kty_ec.rb
125
+ - lib/jose/jwk/kty_oct.rb
126
+ - lib/jose/jwk/kty_rsa.rb
127
+ - lib/jose/jwk/pem.rb
128
+ - lib/jose/jwk/set.rb
129
+ - lib/jose/jws.rb
130
+ - lib/jose/jws/alg.rb
131
+ - lib/jose/jws/alg_ecdsa.rb
132
+ - lib/jose/jws/alg_hmac.rb
133
+ - lib/jose/jws/alg_rsa_pkcs1_v1_5.rb
134
+ - lib/jose/jws/alg_rsa_pss.rb
135
+ - lib/jose/jwt.rb
136
+ - lib/jose/version.rb
137
+ homepage: https://github.com/potatosalad/ruby-jose
138
+ licenses:
139
+ - MPL-2.0
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.4.5.1
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: JSON Object Signing and Encryption
161
+ test_files: []
162
+ has_rdoc: