jose 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,120 @@
1
+ class JOSE::JWK::KTY_OKP_X25519 < Struct.new(:okp)
2
+
3
+ SECRET_BYTES = 32
4
+ PK_BYTES = 32
5
+ SK_BYTES = SECRET_BYTES + PK_BYTES
6
+
7
+ # JOSE::JWK callbacks
8
+
9
+ def self.from_map(fields)
10
+ if fields['kty'] == 'OKP' and fields['crv'] == 'X25519' and fields['x'].is_a?(String)
11
+ pk = JOSE.urlsafe_decode64(fields['x'])
12
+ secret = nil
13
+ if fields['d'].is_a?(String)
14
+ secret = JOSE.urlsafe_decode64(fields['d'])
15
+ end
16
+ if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES)
17
+ if secret.nil?
18
+ return JOSE::JWK::KTY_OKP_X25519.new(pk), fields.except('kty', 'crv', 'x')
19
+ else
20
+ return JOSE::JWK::KTY_OKP_X25519.new(secret + pk), fields.except('kty', 'crv', 'x', 'd')
21
+ end
22
+ end
23
+ end
24
+ raise ArgumentError, "invalid 'OKP' crv 'X25519' JWK"
25
+ end
26
+
27
+ def to_key
28
+ return okp
29
+ end
30
+
31
+ def to_map(fields)
32
+ if okp.bytesize == SK_BYTES
33
+ secret, pk = okp[0, SECRET_BYTES], okp[SECRET_BYTES, SK_BYTES]
34
+ return fields.
35
+ put('crv', 'X25519').
36
+ put('d', JOSE.urlsafe_encode64(secret)).
37
+ put('kty', 'OKP').
38
+ put('x', JOSE.urlsafe_encode64(pk))
39
+ else
40
+ pk = okp
41
+ return fields.
42
+ put('crv', 'X25519').
43
+ put('kty', 'OKP').
44
+ put('x', JOSE.urlsafe_encode64(pk))
45
+ end
46
+ end
47
+
48
+ def to_public_map(fields)
49
+ return to_map(fields).except('d')
50
+ end
51
+
52
+ def to_thumbprint_map(fields)
53
+ return to_public_map(fields).slice('crv', 'kty', 'x')
54
+ end
55
+
56
+ # JOSE::JWK::KTY callbacks
57
+
58
+ def block_encryptor(fields, plain_text)
59
+ return JOSE::Map[
60
+ 'alg' => 'ECDH-ES',
61
+ 'enc' => 'A128GCM'
62
+ ]
63
+ end
64
+
65
+ def derive_key(my_sk)
66
+ if my_sk.is_a?(JOSE::JWK) and my_sk.respond_to?(:to_okp)
67
+ my_sk_type, my_sk = my_sk.to_okp
68
+ raise ArgumentError, "derive_key requires a secret key of type :X25519 as an argument" if my_sk_type != :X25519
69
+ end
70
+ if my_sk.is_a?(String) and (my_sk.bytesize == SK_BYTES or my_sk.bytesize == SECRET_BYTES)
71
+ my_secret = my_sk
72
+ my_secret = my_sk[0, SECRET_BYTES] if my_sk.bytesize == SK_BYTES
73
+ your_pk = okp
74
+ your_pk = okp[SECRET_BYTES, SK_BYTES] if okp.bytesize == SK_BYTES
75
+ return JOSE::JWA::Curve25519.x25519_shared_secret(your_pk, my_secret)
76
+ else
77
+ raise ArgumentError, "derive_key requires a secret key as an argument"
78
+ end
79
+ end
80
+
81
+ def self.generate_key(okp_params)
82
+ secret = nil
83
+ if okp_params.is_a?(Array) and (okp_params.length == 2 or okp_params.length == 3) and okp_params[0] == :okp and okp_params[1] == :X25519
84
+ secret = okp_params[2] if okp_params.length == 3
85
+ elsif okp_params.is_a?(String)
86
+ secret = okp_params
87
+ end
88
+ if secret.nil? or (secret.is_a?(String) and secret.bytesize == SECRET_BYTES)
89
+ pk, secret = JOSE::JWA::Curve25519.x25519_keypair(secret)
90
+ sk = secret + pk
91
+ return from_okp([:X25519, sk])
92
+ else
93
+ raise ArgumentError, "'secret' must be nil or a String of #{SECRET_BYTES} bytes"
94
+ end
95
+ end
96
+
97
+ def generate_key(fields)
98
+ kty, other_fields = JOSE::JWK::KTY_OKP_X25519.generate_key([:okp, :X25519])
99
+ return kty, fields.delete('kid').merge(other_fields)
100
+ end
101
+
102
+ def key_encryptor(fields, key)
103
+ return JOSE::JWK::KTY.key_encryptor(self, fields, key)
104
+ end
105
+
106
+ # API functions
107
+
108
+ def self.from_okp(okp)
109
+ if okp.is_a?(Array) and okp.length == 2 and okp[0] == :X25519 and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES)
110
+ return JOSE::JWK::KTY_OKP_X25519.new(okp[1]), JOSE::Map[]
111
+ else
112
+ raise ArgumentError, "'okp' must be an Array in the form of [:X25519, String]"
113
+ end
114
+ end
115
+
116
+ def to_okp
117
+ return [:X25519, okp]
118
+ end
119
+
120
+ end
@@ -0,0 +1,120 @@
1
+ class JOSE::JWK::KTY_OKP_X448 < Struct.new(:okp)
2
+
3
+ SECRET_BYTES = 56
4
+ PK_BYTES = 56
5
+ SK_BYTES = SECRET_BYTES + PK_BYTES
6
+
7
+ # JOSE::JWK callbacks
8
+
9
+ def self.from_map(fields)
10
+ if fields['kty'] == 'OKP' and fields['crv'] == 'X448' and fields['x'].is_a?(String)
11
+ pk = JOSE.urlsafe_decode64(fields['x'])
12
+ secret = nil
13
+ if fields['d'].is_a?(String)
14
+ secret = JOSE.urlsafe_decode64(fields['d'])
15
+ end
16
+ if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES)
17
+ if secret.nil?
18
+ return JOSE::JWK::KTY_OKP_X448.new(pk), fields.except('kty', 'crv', 'x')
19
+ else
20
+ return JOSE::JWK::KTY_OKP_X448.new(secret + pk), fields.except('kty', 'crv', 'x', 'd')
21
+ end
22
+ end
23
+ end
24
+ raise ArgumentError, "invalid 'OKP' crv 'X448' JWK"
25
+ end
26
+
27
+ def to_key
28
+ return okp
29
+ end
30
+
31
+ def to_map(fields)
32
+ if okp.bytesize == SK_BYTES
33
+ secret, pk = okp[0, SECRET_BYTES], okp[SECRET_BYTES, SK_BYTES]
34
+ return fields.
35
+ put('crv', 'X448').
36
+ put('d', JOSE.urlsafe_encode64(secret)).
37
+ put('kty', 'OKP').
38
+ put('x', JOSE.urlsafe_encode64(pk))
39
+ else
40
+ pk = okp
41
+ return fields.
42
+ put('crv', 'X448').
43
+ put('kty', 'OKP').
44
+ put('x', JOSE.urlsafe_encode64(pk))
45
+ end
46
+ end
47
+
48
+ def to_public_map(fields)
49
+ return to_map(fields).except('d')
50
+ end
51
+
52
+ def to_thumbprint_map(fields)
53
+ return to_public_map(fields).slice('crv', 'kty', 'x')
54
+ end
55
+
56
+ # JOSE::JWK::KTY callbacks
57
+
58
+ def block_encryptor(fields, plain_text)
59
+ return JOSE::Map[
60
+ 'alg' => 'ECDH-ES',
61
+ 'enc' => 'A128GCM'
62
+ ]
63
+ end
64
+
65
+ def derive_key(my_sk)
66
+ if my_sk.is_a?(JOSE::JWK) and my_sk.respond_to?(:to_okp)
67
+ my_sk_type, my_sk = my_sk.to_okp
68
+ raise ArgumentError, "derive_key requires a secret key of type :X448 as an argument" if my_sk_type != :X448
69
+ end
70
+ if my_sk.is_a?(String) and (my_sk.bytesize == SK_BYTES or my_sk.bytesize == SECRET_BYTES)
71
+ my_secret = my_sk
72
+ my_secret = my_sk[0, SECRET_BYTES] if my_sk.bytesize == SK_BYTES
73
+ your_pk = okp
74
+ your_pk = okp[SECRET_BYTES, SK_BYTES] if okp.bytesize == SK_BYTES
75
+ return JOSE::JWA::Curve448.x448_shared_secret(your_pk, my_secret)
76
+ else
77
+ raise ArgumentError, "derive_key requires a secret key as an argument"
78
+ end
79
+ end
80
+
81
+ def self.generate_key(okp_params)
82
+ secret = nil
83
+ if okp_params.is_a?(Array) and (okp_params.length == 2 or okp_params.length == 3) and okp_params[0] == :okp and okp_params[1] == :X448
84
+ secret = okp_params[2] if okp_params.length == 3
85
+ elsif okp_params.is_a?(String)
86
+ secret = okp_params
87
+ end
88
+ if secret.nil? or (secret.is_a?(String) and secret.bytesize == SECRET_BYTES)
89
+ pk, secret = JOSE::JWA::Curve448.x448_keypair(secret)
90
+ sk = secret + pk
91
+ return from_okp([:X448, sk])
92
+ else
93
+ raise ArgumentError, "'secret' must be nil or a String of #{SECRET_BYTES} bytes"
94
+ end
95
+ end
96
+
97
+ def generate_key(fields)
98
+ kty, other_fields = JOSE::JWK::KTY_OKP_X448.generate_key([:okp, :X448])
99
+ return kty, fields.delete('kid').merge(other_fields)
100
+ end
101
+
102
+ def key_encryptor(fields, key)
103
+ return JOSE::JWK::KTY.key_encryptor(self, fields, key)
104
+ end
105
+
106
+ # API functions
107
+
108
+ def self.from_okp(okp)
109
+ if okp.is_a?(Array) and okp.length == 2 and okp[0] == :X448 and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES)
110
+ return JOSE::JWK::KTY_OKP_X448.new(okp[1]), JOSE::Map[]
111
+ else
112
+ raise ArgumentError, "'okp' must be an Array in the form of [:X448, String]"
113
+ end
114
+ end
115
+
116
+ def to_okp
117
+ return [:X448, okp]
118
+ end
119
+
120
+ end
data/lib/jose/jws.rb CHANGED
@@ -192,6 +192,8 @@ module JOSE
192
192
 
193
193
  private
194
194
 
195
+ EDDSA_ALG_LIST = ['Ed25519'.freeze, 'Ed25519ph'.freeze, 'Ed448'.freeze, 'Ed448ph'.freeze].freeze
196
+
195
197
  def self.from_fields(jws, modules)
196
198
  if jws.fields.has_key?('b64')
197
199
  jws.b64 = jws.fields['b64']
@@ -207,6 +209,8 @@ module JOSE
207
209
  JOSE::JWS::ALG_RSA_PSS
208
210
  when jws.fields['alg'].start_with?('RS')
209
211
  JOSE::JWS::ALG_RSA_PKCS1_V1_5
212
+ when EDDSA_ALG_LIST.include?(jws.fields['alg'])
213
+ JOSE::JWS::ALG_EDDSA
210
214
  when jws.fields['alg'] == 'none'
211
215
  JOSE::JWS::ALG_none
212
216
  else
data/lib/jose/jws/alg.rb CHANGED
@@ -5,6 +5,7 @@ module JOSE::JWS::ALG
5
5
  end
6
6
 
7
7
  require 'jose/jws/alg_ecdsa'
8
+ require 'jose/jws/alg_eddsa'
8
9
  require 'jose/jws/alg_hmac'
9
10
  require 'jose/jws/alg_rsa_pkcs1_v1_5'
10
11
  require 'jose/jws/alg_rsa_pss'
@@ -0,0 +1,35 @@
1
+ class JOSE::JWS::ALG_EDDSA < Struct.new(:sign_type)
2
+
3
+ # JOSE::JWS callbacks
4
+
5
+ def self.from_map(fields)
6
+ case fields['alg']
7
+ when 'Ed25519'
8
+ return new(:Ed25519), fields.delete('alg')
9
+ when 'Ed25519ph'
10
+ return new(:Ed25519ph), fields.delete('alg')
11
+ when 'Ed448'
12
+ return new(:Ed448), fields.delete('alg')
13
+ when 'Ed448ph'
14
+ return new(:Ed448ph), fields.delete('alg')
15
+ else
16
+ raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
17
+ end
18
+ end
19
+
20
+ def to_map(fields)
21
+ alg = sign_type.to_s
22
+ return fields.put('alg', alg)
23
+ end
24
+
25
+ # JOSE::JWS::ALG callbacks
26
+
27
+ def sign(jwk, message)
28
+ return jwk.kty.sign(message, sign_type)
29
+ end
30
+
31
+ def verify(jwk, message, signature)
32
+ return jwk.kty.verify(message, sign_type, signature)
33
+ end
34
+
35
+ end
data/lib/jose/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module JOSE
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Bennett
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-11-25 00:00:00.000000000 Z
11
+ date: 2016-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hamster
@@ -30,28 +30,28 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.10'
33
+ version: '1.11'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.10'
40
+ version: '1.11'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '10.0'
47
+ version: '10.5'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '10.0'
54
+ version: '10.5'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: minitest
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rbnacl-libsodium
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: JSON Object Signing and Encryption
84
98
  email:
85
99
  - andrew@pixid.com
@@ -92,6 +106,7 @@ files:
92
106
  - ".ruby-gemset"
93
107
  - ".ruby-version"
94
108
  - ".travis.yml"
109
+ - CHANGELOG.md
95
110
  - CODE_OF_CONDUCT.md
96
111
  - Gemfile
97
112
  - LICENSE.txt
@@ -104,8 +119,24 @@ files:
104
119
  - lib/jose/jwa.rb
105
120
  - lib/jose/jwa/aes_kw.rb
106
121
  - lib/jose/jwa/concat_kdf.rb
122
+ - lib/jose/jwa/curve25519.rb
123
+ - lib/jose/jwa/curve25519_rbnacl.rb
124
+ - lib/jose/jwa/curve25519_ruby.rb
125
+ - lib/jose/jwa/curve25519_unsupported.rb
126
+ - lib/jose/jwa/curve448.rb
127
+ - lib/jose/jwa/curve448_ruby.rb
128
+ - lib/jose/jwa/curve448_unsupported.rb
129
+ - lib/jose/jwa/ed25519.rb
130
+ - lib/jose/jwa/ed25519_rbnacl.rb
131
+ - lib/jose/jwa/ed448.rb
132
+ - lib/jose/jwa/edwards_point.rb
133
+ - lib/jose/jwa/field_element.rb
107
134
  - lib/jose/jwa/pkcs1.rb
108
135
  - lib/jose/jwa/pkcs7.rb
136
+ - lib/jose/jwa/sha3.rb
137
+ - lib/jose/jwa/x25519.rb
138
+ - lib/jose/jwa/x25519_rbnacl.rb
139
+ - lib/jose/jwa/x448.rb
109
140
  - lib/jose/jwe.rb
110
141
  - lib/jose/jwe/alg.rb
111
142
  - lib/jose/jwe/alg_aes_gcm_kw.rb
@@ -123,12 +154,19 @@ files:
123
154
  - lib/jose/jwk/kty.rb
124
155
  - lib/jose/jwk/kty_ec.rb
125
156
  - lib/jose/jwk/kty_oct.rb
157
+ - lib/jose/jwk/kty_okp_ed25519.rb
158
+ - lib/jose/jwk/kty_okp_ed25519ph.rb
159
+ - lib/jose/jwk/kty_okp_ed448.rb
160
+ - lib/jose/jwk/kty_okp_ed448ph.rb
161
+ - lib/jose/jwk/kty_okp_x25519.rb
162
+ - lib/jose/jwk/kty_okp_x448.rb
126
163
  - lib/jose/jwk/kty_rsa.rb
127
164
  - lib/jose/jwk/pem.rb
128
165
  - lib/jose/jwk/set.rb
129
166
  - lib/jose/jws.rb
130
167
  - lib/jose/jws/alg.rb
131
168
  - lib/jose/jws/alg_ecdsa.rb
169
+ - lib/jose/jws/alg_eddsa.rb
132
170
  - lib/jose/jws/alg_hmac.rb
133
171
  - lib/jose/jws/alg_rsa_pkcs1_v1_5.rb
134
172
  - lib/jose/jws/alg_rsa_pss.rb
@@ -154,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
192
  version: '0'
155
193
  requirements: []
156
194
  rubyforge_project:
157
- rubygems_version: 2.4.5.1
195
+ rubygems_version: 2.5.1
158
196
  signing_key:
159
197
  specification_version: 4
160
198
  summary: JSON Object Signing and Encryption