jose 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +8 -5
- data/README.md +2 -1
- data/lib/jose.rb +4 -2
- data/lib/jose/jwa/curve25519_rbnacl.rb +3 -3
- data/lib/jose/jwa/ed448.rb +4 -12
- data/lib/jose/jwa/field_element.rb +1 -1
- data/lib/jose/jwe.rb +4 -4
- data/lib/jose/jwe/alg.rb +8 -3
- data/lib/jose/jwk.rb +47 -21
- data/lib/jose/jwk/kty.rb +3 -0
- data/lib/jose/jwk/kty_ec.rb +4 -3
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +1 -1
- data/lib/jose/jwk/kty_okp_ed448.rb +7 -16
- data/lib/jose/jwk/kty_okp_ed448ph.rb +7 -16
- data/lib/jose/jwk/kty_rsa.rb +5 -4
- data/lib/jose/jwk/pkey_proxy.rb +7 -0
- data/lib/jose/jws/alg.rb +7 -3
- data/lib/jose/jwt.rb +2 -2
- data/lib/jose/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d1a9a636263c7a8b720c8f54649b22a02d3afd4e
|
4
|
+
data.tar.gz: f5a8c4e5ed511dc60a7df47ed4239eeff3437513
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 89419348db067439802b64b2018ba3706e904be1f164255e046d3a39879b874e88b6772ebd6777b6634b2447ef4daae8300c710a59b637b809019774561b2968
|
7
|
+
data.tar.gz: 6e5c379c6de44614898d587aa09e810a968b163121859627e5f9f8dfcfa4bf5a31720af2300f9671272515a7c92528a84a6d74e8a959b5b6d6978546039a1bab
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.1.0 (2016-05-10)
|
4
|
+
|
5
|
+
* Enhancements
|
6
|
+
* Test coverage is now slightly above 90%.
|
7
|
+
* Removed legacy support for 32-byte Ed448 and Ed448ph secret keys.
|
8
|
+
* Improved behavior of ECDH-ES encryption.
|
9
|
+
|
10
|
+
* Fixes
|
11
|
+
* X25519 uses RbNaCl when available.
|
12
|
+
* Various argument order fixes.
|
13
|
+
|
3
14
|
## 1.0.0 (2016-05-07)
|
4
15
|
|
5
16
|
* Enhancements
|
data/Gemfile
CHANGED
@@ -2,15 +2,18 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
platforms :ruby do
|
4
4
|
group :development do
|
5
|
-
gem
|
6
|
-
gem
|
7
|
-
# gem
|
8
|
-
|
5
|
+
gem 'pry'
|
6
|
+
gem 'pry-doc'
|
7
|
+
# gem 'redcarpet'
|
8
|
+
gem 'yard'
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
group :test do
|
13
|
-
gem
|
13
|
+
gem 'minitest-focus', require: false
|
14
|
+
gem 'minitest-perf', require: false
|
15
|
+
gem 'rantly', github: 'abargnesi/rantly', ref: '4d219ea3eb340a5153a43d051b2e22ccb2bce274', require: false
|
16
|
+
gem 'simplecov', require: false
|
14
17
|
if ENV['CI']
|
15
18
|
gem 'coveralls', require: false
|
16
19
|
end
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# JOSE
|
2
2
|
|
3
|
-
[![Travis](https://img.shields.io/travis/potatosalad/ruby-jose.svg?maxAge=
|
3
|
+
[![Travis](https://img.shields.io/travis/potatosalad/ruby-jose.svg?maxAge=86400)](https://travis-ci.org/potatosalad/ruby-jose) [![Coverage Status](https://coveralls.io/repos/github/potatosalad/ruby-jose/badge.svg?branch=master)](https://coveralls.io/github/potatosalad/ruby-jose?branch=master) [![Gem](https://img.shields.io/gem/v/jose.svg?maxAge=86400)](https://rubygems.org/gems/jose) [![Docs](https://img.shields.io/badge/yard-docs-blue.svg?maxAge=86400)](http://www.rubydoc.info/gems/jose) [![Inline docs](http://inch-ci.org/github/potatosalad/ruby-jose.svg?branch=master&style=shields)](http://inch-ci.org/github/potatosalad/ruby-jose)
|
4
4
|
|
5
5
|
JSON Object Signing and Encryption (JOSE) for Ruby.
|
6
6
|
|
@@ -45,6 +45,7 @@ verified, message, = jwk.verify(signed)
|
|
45
45
|
```
|
46
46
|
|
47
47
|
More details and examples:
|
48
|
+
|
48
49
|
- [Getting Started](http://www.rubydoc.info/gems/jose/file/docs/GettingStarted.md)
|
49
50
|
- [Key Generation](http://www.rubydoc.info/gems/jose/file/docs/KeyGeneration.md)
|
50
51
|
- [Encryption Algorithms](http://www.rubydoc.info/gems/jose/file/docs/EncryptionAlgorithms.md)
|
data/lib/jose.rb
CHANGED
@@ -4,6 +4,7 @@ require 'base64'
|
|
4
4
|
require 'hamster/hash'
|
5
5
|
require 'json'
|
6
6
|
require 'openssl'
|
7
|
+
require 'securerandom'
|
7
8
|
require 'thread'
|
8
9
|
|
9
10
|
# JOSE stands for JSON Object Signing and Encryption which is a is a set of
|
@@ -129,20 +130,21 @@ module JOSE
|
|
129
130
|
# @param [String] binary
|
130
131
|
# @return [String]
|
131
132
|
def self.urlsafe_decode64(binary)
|
133
|
+
binary = binary.tr('-_', '+/')
|
132
134
|
case binary.bytesize % 4
|
133
135
|
when 2
|
134
136
|
binary += '=='
|
135
137
|
when 3
|
136
138
|
binary += '='
|
137
139
|
end
|
138
|
-
return Base64.
|
140
|
+
return Base64.decode64(binary)
|
139
141
|
end
|
140
142
|
|
141
143
|
# Returns the Base64Url encoded version of `binary` without padding.
|
142
144
|
# @param [String] binary
|
143
145
|
# @return [String]
|
144
146
|
def self.urlsafe_encode64(binary)
|
145
|
-
return Base64.
|
147
|
+
return Base64.strict_encode64(binary).tr('+/', '-_').delete('=')
|
146
148
|
end
|
147
149
|
|
148
150
|
private
|
@@ -51,15 +51,15 @@ module JOSE::JWA::Curve25519_RbNaCl
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def x25519_keypair(secret = nil)
|
54
|
-
return JOSE::JWA::
|
54
|
+
return JOSE::JWA::X25519_RbNaCl.keypair(secret)
|
55
55
|
end
|
56
56
|
|
57
57
|
def x25519_secret_to_public(sk)
|
58
|
-
return JOSE::JWA::
|
58
|
+
return JOSE::JWA::X25519_RbNaCl.sk_to_pk(sk)
|
59
59
|
end
|
60
60
|
|
61
61
|
def x25519_shared_secret(pk, sk)
|
62
|
-
return JOSE::JWA::
|
62
|
+
return JOSE::JWA::X25519_RbNaCl.shared_secret(pk, sk)
|
63
63
|
end
|
64
64
|
|
65
65
|
end
|
data/lib/jose/jwa/ed448.rb
CHANGED
@@ -5,15 +5,13 @@ module JOSE::JWA::Ed448
|
|
5
5
|
C_bits = 456
|
6
6
|
C_bytes = (C_bits + 7) / 8
|
7
7
|
C_secretbytes = C_bytes
|
8
|
-
C_legacysecretbytes = 32
|
9
8
|
C_publickeybytes = C_bytes
|
10
9
|
C_secretkeybytes = C_secretbytes + C_publickeybytes
|
11
|
-
C_legacysecretkeybytes = C_legacysecretbytes + C_publickeybytes
|
12
10
|
C_signaturebytes = C_bytes + C_bytes
|
13
11
|
C_B = JOSE::JWA::Edwards448Point.stdbase.freeze
|
14
12
|
|
15
13
|
def secret_to_curve448(secret)
|
16
|
-
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
14
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
17
15
|
curve448_scalar = JOSE::JWA::SHA3.shake256(secret, 114)[0, 56]
|
18
16
|
curve448_scalar.setbyte(0, curve448_scalar.getbyte(0) & 252)
|
19
17
|
curve448_scalar.setbyte(55, curve448_scalar.getbyte(55) | 128)
|
@@ -21,7 +19,7 @@ module JOSE::JWA::Ed448
|
|
21
19
|
end
|
22
20
|
|
23
21
|
def secret_to_pk(secret)
|
24
|
-
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
22
|
+
raise ArgumentError, "secret must be #{C_secretbytes} bytes" if secret.bytesize != C_secretbytes
|
25
23
|
return (C_B * OpenSSL::BN.new(secret_to_curve448(secret).reverse, 2).to_i).encode()
|
26
24
|
end
|
27
25
|
|
@@ -34,13 +32,11 @@ module JOSE::JWA::Ed448
|
|
34
32
|
|
35
33
|
def sk_to_secret(sk)
|
36
34
|
return sk[0, C_secretbytes] if sk.bytesize == C_secretkeybytes
|
37
|
-
return sk[0, C_legacysecretbytes] if sk.bytesize == C_legacysecretkeybytes
|
38
35
|
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes"
|
39
36
|
end
|
40
37
|
|
41
38
|
def sk_to_pk(sk)
|
42
39
|
return sk[C_secretbytes, C_secretkeybytes] if sk.bytesize == C_secretkeybytes
|
43
|
-
return sk[C_legacysecretbytes, C_legacysecretkeybytes] if sk.bytesize == C_legacysecretkeybytes
|
44
40
|
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes"
|
45
41
|
end
|
46
42
|
|
@@ -56,14 +52,12 @@ module JOSE::JWA::Ed448
|
|
56
52
|
end
|
57
53
|
|
58
54
|
def sign(m, sk, ctx = nil)
|
59
|
-
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
55
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
60
56
|
ctx ||= ''
|
61
57
|
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
62
58
|
secret, pk = nil, nil
|
63
59
|
if sk.bytesize == C_secretkeybytes
|
64
60
|
secret, pk = sk[0, 57], sk[57, 114]
|
65
|
-
elsif sk.bytesize == C_legacysecretkeybytes
|
66
|
-
secret, pk = sk[0, 32], sk[32, 89]
|
67
61
|
end
|
68
62
|
khash = JOSE::JWA::SHA3.shake256(secret, 114)
|
69
63
|
curve448_scalar, seed = khash[0, 57], khash[57, 114]
|
@@ -83,15 +77,13 @@ module JOSE::JWA::Ed448
|
|
83
77
|
end
|
84
78
|
|
85
79
|
def sign_ph(m, sk, ctx = nil)
|
86
|
-
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
80
|
+
raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes
|
87
81
|
ctx ||= ''
|
88
82
|
raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
|
89
83
|
m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('a*CCa*a*'), 64)
|
90
84
|
secret, pk = nil, nil
|
91
85
|
if sk.bytesize == C_secretkeybytes
|
92
86
|
secret, pk = sk[0, 57], sk[57, 114]
|
93
|
-
elsif sk.bytesize == C_legacysecretkeybytes
|
94
|
-
secret, pk = sk[0, 32], sk[32, 89]
|
95
87
|
end
|
96
88
|
khash = JOSE::JWA::SHA3.shake256(secret, 114)
|
97
89
|
curve448_scalar, seed = khash[0, 57], khash[57, 114]
|
data/lib/jose/jwe.rb
CHANGED
@@ -231,15 +231,15 @@ module JOSE
|
|
231
231
|
# # => "{}"
|
232
232
|
#
|
233
233
|
# # ECDH-ES+A192KW with EC keypairs
|
234
|
-
# encrypted_ecdhesa192kw_alice2bob = JOSE::JWE.block_encrypt(
|
234
|
+
# encrypted_ecdhesa192kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A192KW", "enc" => "A192GCM" }).compact
|
235
235
|
# # => "eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.S9LZ1i_Lua_if4I83WcaCQ9yT5qqPI_NhCFR7tMiZDQ.kG3taKEjGeKDRTzs.H1s.oVGBFP63z4gd3e-R2d1cmA"
|
236
|
-
# JOSE::JWE.block_decrypt(
|
236
|
+
# JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa192kw_alice2bob).first
|
237
237
|
# # => "{}"
|
238
238
|
#
|
239
239
|
# # ECDH-ES+A256KW with EC keypairs
|
240
|
-
# encrypted_ecdhesa256kw_alice2bob = JOSE::JWE.block_encrypt(
|
240
|
+
# encrypted_ecdhesa256kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A256KW", "enc" => "A256GCM" }).compact
|
241
241
|
# # => "eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.4KWy1-vRiJyNINF6mWYbUPPTVNG9ADfvvfpSDbddPTftz7GmUHUsuQ.IkRhtGH23R-9dFF3.9yk.RnALhnqWMHWCZFxqc-DU4A"
|
242
|
-
# JOSE::JWE.block_decrypt(
|
242
|
+
# JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa256kw_alice2bob).first
|
243
243
|
# # => "{}"
|
244
244
|
#
|
245
245
|
# See {JOSE::JWK.box_encrypt JOSE::JWK.box_encrypt} for generating an Ephemeral Public Key based on the same curve as the supplied other party key in the same step.
|
data/lib/jose/jwe/alg.rb
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
module JOSE::JWE::ALG
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# Generates a new {JOSE::JWK JOSE::JWK} based on the `parameters`, `algorithm`, and `encryption`.
|
4
|
+
#
|
5
|
+
# @see JOSE::JWK.generate_key
|
6
|
+
# @param [Array] parameters
|
7
|
+
# @param [String] algorithm
|
8
|
+
# @param [String] encryption
|
9
|
+
# @return [JOSE::JWK]
|
10
|
+
def self.generate_key(parameters, algorithm, encryption)
|
6
11
|
return JOSE::JWK.generate_key(parameters).merge({
|
7
12
|
'alg' => algorithm,
|
8
13
|
'enc' => encryption,
|
data/lib/jose/jwk.rb
CHANGED
@@ -597,7 +597,16 @@ module JOSE
|
|
597
597
|
# @param [JOSE::EncryptedBinary, JOSE::EncryptedMap] encrypted
|
598
598
|
# @return [[String, JOSE::JWE]]
|
599
599
|
def self.block_decrypt(jwk, encrypted)
|
600
|
-
|
600
|
+
if jwk.is_a?(Array)
|
601
|
+
public_jwk, secret_jwk = from(jwk)
|
602
|
+
if secret_jwk.nil?
|
603
|
+
secret_jwk = public_jwk
|
604
|
+
public_jwk = nil
|
605
|
+
end
|
606
|
+
return box_decrypt(secret_jwk, encrypted, public_jwk)
|
607
|
+
else
|
608
|
+
return from(jwk).block_decrypt(encrypted)
|
609
|
+
end
|
601
610
|
end
|
602
611
|
|
603
612
|
# Decrypts the `encrypted` binary or map using the `jwk`.
|
@@ -617,7 +626,11 @@ module JOSE
|
|
617
626
|
# @param [JOSE::JWE] jwe
|
618
627
|
# @return [JOSE::EncryptedMap]
|
619
628
|
def self.block_encrypt(jwk, plain_text, jwe = nil)
|
620
|
-
|
629
|
+
if jwk.is_a?(Array)
|
630
|
+
return box_encrypt(plain_text, from(jwk), jwe)
|
631
|
+
else
|
632
|
+
return from(jwk).block_encrypt(plain_text, jwe)
|
633
|
+
end
|
621
634
|
end
|
622
635
|
|
623
636
|
# Encrypts the `plain_text` using the `jwk` and algorithms specified by the `jwe`.
|
@@ -688,8 +701,8 @@ module JOSE
|
|
688
701
|
# @param [JOSE::JWE] jwe
|
689
702
|
# @return [JOSE::EncryptedMap, [JOSE::EncryptedMap, JOSE::JWK]]
|
690
703
|
def self.box_encrypt(plain_text, box_keys, jwe = nil)
|
691
|
-
|
692
|
-
return
|
704
|
+
jwk_public, jwk_secret = from(box_keys)
|
705
|
+
return jwk_public.box_encrypt(plain_text, jwk_secret, jwe)
|
693
706
|
end
|
694
707
|
|
695
708
|
# Key Agreement encryption of `plain_text` by generating an ephemeral private key based on `other_public_jwk` curve.
|
@@ -699,42 +712,51 @@ module JOSE
|
|
699
712
|
# @see JOSE::JWK.box_decrypt
|
700
713
|
# @see JOSE::JWE.block_encrypt
|
701
714
|
# @param [String] plain_text
|
702
|
-
# @param [JOSE::JWK]
|
715
|
+
# @param [JOSE::JWK] jwk_secret
|
703
716
|
# @param [JOSE::JWE] jwe
|
704
717
|
# @return [JOSE::EncryptedMap, [JOSE::EncryptedMap, JOSE::JWK]]
|
705
|
-
def box_encrypt(plain_text,
|
706
|
-
|
707
|
-
|
708
|
-
if
|
709
|
-
|
718
|
+
def box_encrypt(plain_text, jwk_secret = nil, jwe = nil)
|
719
|
+
epk_secret = nil
|
720
|
+
jwk_public = self
|
721
|
+
if jwk_secret.nil?
|
722
|
+
epk_secret = jwk_secret = jwk_public.generate_key
|
710
723
|
end
|
711
|
-
if not
|
712
|
-
|
724
|
+
if not jwk_secret.is_a?(JOSE::JWK)
|
725
|
+
jwk_secret = JOSE::JWK.from(jwk_secret)
|
713
726
|
end
|
714
727
|
if jwe.nil?
|
715
|
-
jwe =
|
728
|
+
jwe = jwk_public.block_encryptor
|
716
729
|
end
|
717
730
|
if jwe.is_a?(Hash)
|
718
731
|
jwe = JOSE::Map.new(jwe)
|
719
732
|
end
|
720
733
|
if jwe.is_a?(JOSE::Map)
|
721
734
|
if jwe['apu'].nil?
|
722
|
-
jwe = jwe.put('apu',
|
735
|
+
jwe = jwe.put('apu', jwk_secret.fields['kid'] || jwk_secret.thumbprint)
|
723
736
|
end
|
724
737
|
if jwe['apv'].nil?
|
725
|
-
jwe = jwe.put('apv',
|
738
|
+
jwe = jwe.put('apv', jwk_public.fields['kid'] || jwk_public.thumbprint)
|
726
739
|
end
|
727
740
|
if jwe['epk'].nil?
|
728
|
-
jwe = jwe.put('epk',
|
741
|
+
jwe = jwe.put('epk', jwk_secret.to_public_map)
|
729
742
|
end
|
730
743
|
end
|
731
|
-
if
|
732
|
-
return JOSE::JWE.block_encrypt([
|
744
|
+
if epk_secret
|
745
|
+
return JOSE::JWE.block_encrypt([jwk_public, jwk_secret], plain_text, jwe), epk_secret
|
733
746
|
else
|
734
|
-
return JOSE::JWE.block_encrypt([
|
747
|
+
return JOSE::JWE.block_encrypt([jwk_public, jwk_secret], plain_text, jwe)
|
735
748
|
end
|
736
749
|
end
|
737
750
|
|
751
|
+
# Derives a key (typically just returns a binary representation of the key).
|
752
|
+
#
|
753
|
+
# @param [JOSE::JWK] jwk
|
754
|
+
# @param [*Object] args
|
755
|
+
# @return [String]
|
756
|
+
def self.derive_key(jwk, *args)
|
757
|
+
return from(jwk).derive_key(*args)
|
758
|
+
end
|
759
|
+
|
738
760
|
# Derives a key (typically just returns a binary representation of the key).
|
739
761
|
#
|
740
762
|
# @param [*Object] args
|
@@ -858,12 +880,16 @@ module JOSE
|
|
858
880
|
# Signs the `plain_text` using the `jwk` and the default signer algorithm `jws` for the key type.
|
859
881
|
#
|
860
882
|
# @see JOSE::JWS.sign
|
861
|
-
# @param [JOSE::JWK] jwk
|
862
883
|
# @param [String] plain_text
|
863
884
|
# @param [JOSE::JWS] jws
|
885
|
+
# @param [JOSE::JWK] jwk
|
864
886
|
# @param [JOSE::Map] header
|
865
887
|
# @return [JOSE::SignedMap]
|
866
|
-
def self.sign(
|
888
|
+
def self.sign(plain_text, jws, jwk = nil, header = nil)
|
889
|
+
if jwk.nil?
|
890
|
+
jwk = jws
|
891
|
+
jws = nil
|
892
|
+
end
|
867
893
|
return from(jwk).sign(plain_text, jws, header)
|
868
894
|
end
|
869
895
|
|
data/lib/jose/jwk/kty.rb
CHANGED
@@ -3,6 +3,7 @@ module JOSE::JWK::KTY
|
|
3
3
|
extend self
|
4
4
|
|
5
5
|
def from_key(object)
|
6
|
+
object = object.__getobj__ if object.is_a?(JOSE::JWK::PKeyProxy)
|
6
7
|
case object
|
7
8
|
when OpenSSL::PKey::EC
|
8
9
|
return JOSE::JWK::KTY_EC.from_key(object)
|
@@ -29,6 +30,8 @@ module JOSE::JWK::KTY
|
|
29
30
|
|
30
31
|
end
|
31
32
|
|
33
|
+
require 'jose/jwk/pkey_proxy'
|
34
|
+
|
32
35
|
require 'jose/jwk/kty_ec'
|
33
36
|
require 'jose/jwk/kty_oct'
|
34
37
|
require 'jose/jwk/kty_okp_ed25519'
|
data/lib/jose/jwk/kty_ec.rb
CHANGED
@@ -24,14 +24,14 @@ class JOSE::JWK::KTY_EC < Struct.new(:key)
|
|
24
24
|
if fields['d'].is_a?(String)
|
25
25
|
ec.private_key = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)
|
26
26
|
end
|
27
|
-
return JOSE::JWK::KTY_EC.new(ec), fields.except('kty', 'crv', 'd', 'x', 'y')
|
27
|
+
return JOSE::JWK::KTY_EC.new(JOSE::JWK::PKeyProxy.new(ec)), fields.except('kty', 'crv', 'd', 'x', 'y')
|
28
28
|
else
|
29
29
|
raise ArgumentError, "invalid 'EC' JWK"
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
def to_key
|
34
|
-
return key
|
34
|
+
return key.__getobj__
|
35
35
|
end
|
36
36
|
|
37
37
|
def to_map(fields)
|
@@ -211,9 +211,10 @@ class JOSE::JWK::KTY_EC < Struct.new(:key)
|
|
211
211
|
# API functions
|
212
212
|
|
213
213
|
def self.from_key(key)
|
214
|
+
key = key.__getobj__ if key.is_a?(JOSE::JWK::PKeyProxy)
|
214
215
|
case key
|
215
216
|
when OpenSSL::PKey::EC
|
216
|
-
return JOSE::JWK::KTY_EC.new(key), JOSE::Map[]
|
217
|
+
return JOSE::JWK::KTY_EC.new(JOSE::JWK::PKeyProxy.new(key)), JOSE::Map[]
|
217
218
|
else
|
218
219
|
raise ArgumentError, "'key' must be a OpenSSL::PKey::EC"
|
219
220
|
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
2
2
|
|
3
3
|
SECRET_BYTES = 57
|
4
|
-
LEGACY_SECRET_BYTES = 32
|
5
4
|
PK_BYTES = 57
|
6
5
|
SK_BYTES = SECRET_BYTES + PK_BYTES
|
7
|
-
LEGACY_SK_BYTES = LEGACY_SECRET_BYTES + PK_BYTES
|
8
6
|
|
9
7
|
# JOSE::JWK callbacks
|
10
8
|
|
@@ -15,7 +13,7 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
15
13
|
if fields['d'].is_a?(String)
|
16
14
|
secret = JOSE.urlsafe_decode64(fields['d'])
|
17
15
|
end
|
18
|
-
if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES
|
16
|
+
if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES)
|
19
17
|
if secret.nil?
|
20
18
|
return JOSE::JWK::KTY_OKP_Ed448.new(pk), fields.except('kty', 'crv', 'x')
|
21
19
|
else
|
@@ -38,13 +36,6 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
38
36
|
put('d', JOSE.urlsafe_encode64(secret)).
|
39
37
|
put('kty', 'OKP').
|
40
38
|
put('x', JOSE.urlsafe_encode64(pk))
|
41
|
-
elsif okp.bytesize == LEGACY_SK_BYTES
|
42
|
-
secret, pk = okp[0, LEGACY_SECRET_BYTES], okp[LEGACY_SECRET_BYTES, LEGACY_SK_BYTES]
|
43
|
-
return fields.
|
44
|
-
put('crv', 'Ed448').
|
45
|
-
put('d', JOSE.urlsafe_encode64(secret)).
|
46
|
-
put('kty', 'OKP').
|
47
|
-
put('x', JOSE.urlsafe_encode64(pk))
|
48
39
|
else
|
49
40
|
pk = okp
|
50
41
|
return fields.
|
@@ -71,7 +62,7 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
71
62
|
elsif okp_params.is_a?(String)
|
72
63
|
secret = okp_params
|
73
64
|
end
|
74
|
-
if secret.nil? or (secret.is_a?(String) and (secret.bytesize == SECRET_BYTES
|
65
|
+
if secret.nil? or (secret.is_a?(String) and (secret.bytesize == SECRET_BYTES))
|
75
66
|
return from_okp([:Ed448, JOSE::JWA::Curve448.ed448_keypair(secret)[1]])
|
76
67
|
else
|
77
68
|
raise ArgumentError, "'secret' must be nil or a String of #{SECRET_BYTES} bytes"
|
@@ -89,14 +80,14 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
89
80
|
|
90
81
|
def sign(message, digest_type)
|
91
82
|
raise ArgumentError, "'digest_type' must be :Ed448" if digest_type != :Ed448
|
92
|
-
raise NotImplementedError, "Ed448 public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
83
|
+
raise NotImplementedError, "Ed448 public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
93
84
|
return JOSE::JWA::Curve448.ed448_sign(message, okp)
|
94
85
|
end
|
95
86
|
|
96
87
|
def signer(fields = nil)
|
97
|
-
if
|
88
|
+
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
98
89
|
return JOSE::Map['alg' => fields['alg']]
|
99
|
-
elsif
|
90
|
+
elsif okp.bytesize == SK_BYTES
|
100
91
|
return JOSE::Map['alg' => 'Ed448']
|
101
92
|
else
|
102
93
|
raise ArgumentError, "signing not supported for public keys"
|
@@ -114,14 +105,14 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
114
105
|
def verify(message, digest_type, signature)
|
115
106
|
raise ArgumentError, "'digest_type' must be :Ed448" if digest_type != :Ed448
|
116
107
|
pk = okp
|
117
|
-
pk = JOSE::JWA::Curve448.ed448_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
108
|
+
pk = JOSE::JWA::Curve448.ed448_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
118
109
|
return JOSE::JWA::Curve448.ed448_verify(signature, message, pk)
|
119
110
|
end
|
120
111
|
|
121
112
|
# API functions
|
122
113
|
|
123
114
|
def self.from_okp(okp)
|
124
|
-
if okp.is_a?(Array) and okp.length == 2 and okp[0] == :Ed448 and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES
|
115
|
+
if okp.is_a?(Array) and okp.length == 2 and okp[0] == :Ed448 and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES)
|
125
116
|
return JOSE::JWK::KTY_OKP_Ed448.new(okp[1]), JOSE::Map[]
|
126
117
|
else
|
127
118
|
raise ArgumentError, "'okp' must be an Array in the form of [:Ed448, String]"
|
@@ -1,10 +1,8 @@
|
|
1
1
|
class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
2
2
|
|
3
3
|
SECRET_BYTES = 57
|
4
|
-
LEGACY_SECRET_BYTES = 32
|
5
4
|
PK_BYTES = 57
|
6
5
|
SK_BYTES = SECRET_BYTES + PK_BYTES
|
7
|
-
LEGACY_SK_BYTES = LEGACY_SECRET_BYTES + PK_BYTES
|
8
6
|
|
9
7
|
# JOSE::JWK callbacks
|
10
8
|
|
@@ -15,7 +13,7 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
15
13
|
if fields['d'].is_a?(String)
|
16
14
|
secret = JOSE.urlsafe_decode64(fields['d'])
|
17
15
|
end
|
18
|
-
if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES
|
16
|
+
if pk.bytesize == PK_BYTES and (secret.nil? or secret.bytesize == SECRET_BYTES)
|
19
17
|
if secret.nil?
|
20
18
|
return JOSE::JWK::KTY_OKP_Ed448ph.new(pk), fields.except('kty', 'crv', 'x')
|
21
19
|
else
|
@@ -38,13 +36,6 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
38
36
|
put('d', JOSE.urlsafe_encode64(secret)).
|
39
37
|
put('kty', 'OKP').
|
40
38
|
put('x', JOSE.urlsafe_encode64(pk))
|
41
|
-
elsif okp.bytesize == LEGACY_SK_BYTES
|
42
|
-
secret, pk = okp[0, LEGACY_SECRET_BYTES], okp[LEGACY_SECRET_BYTES, LEGACY_SK_BYTES]
|
43
|
-
return fields.
|
44
|
-
put('crv', 'Ed448ph').
|
45
|
-
put('d', JOSE.urlsafe_encode64(secret)).
|
46
|
-
put('kty', 'OKP').
|
47
|
-
put('x', JOSE.urlsafe_encode64(pk))
|
48
39
|
else
|
49
40
|
pk = okp
|
50
41
|
return fields.
|
@@ -71,7 +62,7 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
71
62
|
elsif okp_params.is_a?(String)
|
72
63
|
secret = okp_params
|
73
64
|
end
|
74
|
-
if secret.nil? or (secret.is_a?(String) and (secret.bytesize == SECRET_BYTES
|
65
|
+
if secret.nil? or (secret.is_a?(String) and (secret.bytesize == SECRET_BYTES))
|
75
66
|
return from_okp([:Ed448ph, JOSE::JWA::Curve448.ed448ph_keypair(secret)[1]])
|
76
67
|
else
|
77
68
|
raise ArgumentError, "'secret' must be nil or a String of #{SECRET_BYTES} bytes"
|
@@ -89,14 +80,14 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
89
80
|
|
90
81
|
def sign(message, digest_type)
|
91
82
|
raise ArgumentError, "'digest_type' must be :Ed448ph" if digest_type != :Ed448ph
|
92
|
-
raise NotImplementedError, "Ed448ph public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
83
|
+
raise NotImplementedError, "Ed448ph public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
93
84
|
return JOSE::JWA::Curve448.ed448ph_sign(message, okp)
|
94
85
|
end
|
95
86
|
|
96
87
|
def signer(fields = nil)
|
97
|
-
if
|
88
|
+
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
98
89
|
return JOSE::Map['alg' => fields['alg']]
|
99
|
-
elsif
|
90
|
+
elsif okp.bytesize == SK_BYTES
|
100
91
|
return JOSE::Map['alg' => 'Ed448ph']
|
101
92
|
else
|
102
93
|
raise ArgumentError, "signing not supported for public keys"
|
@@ -114,14 +105,14 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
114
105
|
def verify(message, digest_type, signature)
|
115
106
|
raise ArgumentError, "'digest_type' must be :Ed448ph" if digest_type != :Ed448ph
|
116
107
|
pk = okp
|
117
|
-
pk = JOSE::JWA::Curve448.ed448ph_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
108
|
+
pk = JOSE::JWA::Curve448.ed448ph_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
118
109
|
return JOSE::JWA::Curve448.ed448ph_verify(signature, message, pk)
|
119
110
|
end
|
120
111
|
|
121
112
|
# API functions
|
122
113
|
|
123
114
|
def self.from_okp(okp)
|
124
|
-
if okp.is_a?(Array) and okp.length == 2 and okp[0] == :Ed448ph and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES
|
115
|
+
if okp.is_a?(Array) and okp.length == 2 and okp[0] == :Ed448ph and okp[1].is_a?(String) and (okp[1].bytesize == PK_BYTES or okp[1].bytesize == SK_BYTES)
|
125
116
|
return JOSE::JWK::KTY_OKP_Ed448ph.new(okp[1]), JOSE::Map[]
|
126
117
|
else
|
127
118
|
raise ArgumentError, "'okp' must be an Array in the form of [:Ed448ph, String]"
|
data/lib/jose/jwk/kty_rsa.rb
CHANGED
@@ -17,7 +17,7 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
17
17
|
rsa.p = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['p']), 2)
|
18
18
|
rsa.q = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['q']), 2)
|
19
19
|
rsa.iqmp = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['qi']), 2)
|
20
|
-
return JOSE::JWK::KTY_RSA.new(rsa), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
|
20
|
+
return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
|
21
21
|
else
|
22
22
|
raise ArgumentError, "invalid 'RSA' JWK"
|
23
23
|
end
|
@@ -25,7 +25,7 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
25
25
|
rsa = OpenSSL::PKey::RSA.new
|
26
26
|
rsa.e = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)
|
27
27
|
rsa.n = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)
|
28
|
-
return JOSE::JWK::KTY_RSA.new(rsa), fields.except('kty', 'e', 'n')
|
28
|
+
return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'e', 'n')
|
29
29
|
end
|
30
30
|
else
|
31
31
|
raise ArgumentError, "invalid 'RSA' JWK"
|
@@ -33,7 +33,7 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def to_key
|
36
|
-
return key
|
36
|
+
return key.__getobj__
|
37
37
|
end
|
38
38
|
|
39
39
|
def to_map(fields)
|
@@ -187,9 +187,10 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
187
187
|
# API functions
|
188
188
|
|
189
189
|
def self.from_key(key)
|
190
|
+
key = key.__getobj__ if key.is_a?(JOSE::JWK::PKeyProxy)
|
190
191
|
case key
|
191
192
|
when OpenSSL::PKey::RSA
|
192
|
-
return JOSE::JWK::KTY_RSA.new(key), JOSE::Map[]
|
193
|
+
return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(key)), JOSE::Map[]
|
193
194
|
else
|
194
195
|
raise ArgumentError, "'key' must be a OpenSSL::PKey::RSA"
|
195
196
|
end
|
data/lib/jose/jws/alg.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
module JOSE::JWS::ALG
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# Generates a new {JOSE::JWK JOSE::JWK} based on the `parameters` and `algorithm`.
|
4
|
+
#
|
5
|
+
# @see JOSE::JWK.generate_key
|
6
|
+
# @param [Array] parameters
|
7
|
+
# @param [String] algorithm
|
8
|
+
# @return [JOSE::JWK]
|
9
|
+
def self.generate_key(parameters, algorithm)
|
6
10
|
return JOSE::JWK.generate_key(parameters).merge({
|
7
11
|
'alg' => algorithm,
|
8
12
|
'use' => 'sig'
|
data/lib/jose/jwt.rb
CHANGED
@@ -218,7 +218,7 @@ module JOSE
|
|
218
218
|
plain_text = to_binary
|
219
219
|
if jwe.nil?
|
220
220
|
jwk = JOSE::JWK.from(jwk)
|
221
|
-
jwe = jwk.block_encryptor
|
221
|
+
jwe = (jwk.is_a?(Array) ? jwk.last : jwk).block_encryptor
|
222
222
|
end
|
223
223
|
if jwe.is_a?(Hash)
|
224
224
|
jwe = JOSE::Map.new(jwe)
|
@@ -316,7 +316,7 @@ module JOSE
|
|
316
316
|
if not jws.has_key?('typ')
|
317
317
|
jws = jws.put('typ', 'JWT')
|
318
318
|
end
|
319
|
-
return JOSE::JWK.sign(
|
319
|
+
return JOSE::JWK.sign(plain_text, jws, jwk, header)
|
320
320
|
end
|
321
321
|
|
322
322
|
# Verifies the `signed` using the `jwk` and calls {JOSE::JWT.from JOST::JWT.from} on the payload.
|
data/lib/jose/version.rb
CHANGED
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: 1.
|
4
|
+
version: 1.1.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: 2016-05-
|
11
|
+
date: 2016-05-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hamster
|
@@ -168,6 +168,7 @@ files:
|
|
168
168
|
- lib/jose/jwk/kty_rsa.rb
|
169
169
|
- lib/jose/jwk/openssh_key.rb
|
170
170
|
- lib/jose/jwk/pem.rb
|
171
|
+
- lib/jose/jwk/pkey_proxy.rb
|
171
172
|
- lib/jose/jwk/set.rb
|
172
173
|
- lib/jose/jws.rb
|
173
174
|
- lib/jose/jws/alg.rb
|