jose 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: