jose 0.1.0 → 0.2.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.
@@ -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