jose 1.0.0 → 1.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.
- 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
|
-
[](https://travis-ci.org/potatosalad/ruby-jose) [](https://coveralls.io/github/potatosalad/ruby-jose?branch=master) [](https://rubygems.org/gems/jose) [](http://www.rubydoc.info/gems/jose) [](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
|