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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8804406a3e47d37b89da4315eb021d33d447ecf7
4
- data.tar.gz: 9409c19e851ef2036b1fc843eeeaa3667c64ed40
3
+ metadata.gz: d1a9a636263c7a8b720c8f54649b22a02d3afd4e
4
+ data.tar.gz: f5a8c4e5ed511dc60a7df47ed4239eeff3437513
5
5
  SHA512:
6
- metadata.gz: ade14cfd65ca8cab5f24b5329448db0e0ee19d04fa71ec43fda67300197563c361c20f2229dba79f0b6b4b01cf9440482cae460324ce069fa782c66e3430b284
7
- data.tar.gz: 391d322825bf7905f41800ae0ed77f7d29b0c27018ac06e65bcf3d04288fe579191a4278beac44e5a2f09e2e9350c22da4cab0189c126fef258888d4e1f53516
6
+ metadata.gz: 89419348db067439802b64b2018ba3706e904be1f164255e046d3a39879b874e88b6772ebd6777b6634b2447ef4daae8300c710a59b637b809019774561b2968
7
+ data.tar.gz: 6e5c379c6de44614898d587aa09e810a968b163121859627e5f9f8dfcfa4bf5a31720af2300f9671272515a7c92528a84a6d74e8a959b5b6d6978546039a1bab
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  /.bundle/
2
+ /.minitest-perf.db
2
3
  /.yardoc
3
4
  /Gemfile.lock
4
5
  /_yardoc/
@@ -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 "pry"
6
- gem "pry-doc"
7
- # gem "redcarpet"
8
- # gem "yard"
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 "simplecov", require: false
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=2592000)](https://travis-ci.org/potatosalad/ruby-jose) [![Gem](https://img.shields.io/gem/v/jose.svg?maxAge=2592000)](https://rubygems.org/gems/jose) [![Docs](https://img.shields.io/badge/yard-docs-blue.svg?maxAge=2592000)](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)
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)
@@ -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.urlsafe_decode64(binary)
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.urlsafe_encode64(binary).tr('=', '')
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::X25519.keypair(secret)
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::X25519.sk_to_pk(sk)
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::X25519.shared_secret(pk, sk)
62
+ return JOSE::JWA::X25519_RbNaCl.shared_secret(pk, sk)
63
63
  end
64
64
 
65
65
  end
@@ -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 and secret.bytesize != C_legacysecretbytes
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 and secret.bytesize != C_legacysecretbytes
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 and sk.bytesize != C_legacysecretkeybytes
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 and sk.bytesize != C_legacysecretkeybytes
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]
@@ -106,7 +106,7 @@ class JOSE::JWA::FieldElement
106
106
  end
107
107
 
108
108
  def sign
109
- return @x%2
109
+ return @x % 2.to_bn
110
110
  end
111
111
 
112
112
  def value
@@ -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({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", { "alg" => "ECDH-ES+A192KW", "enc" => "A192GCM" }).compact
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({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhesa192kw_alice2bob).first
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({jwk_ec256_bob_pk, jwk_ec256_alice_sk}, "{}", { "alg" => "ECDH-ES+A256KW", "enc" => "A256GCM" }).compact
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({jwk_ec256_alice_pk, jwk_ec256_bob_sk}, encrypted_ecdhesa256kw_alice2bob).first
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.
@@ -1,8 +1,13 @@
1
1
  module JOSE::JWE::ALG
2
2
 
3
- extend self
4
-
5
- def generate_key(parameters, algorithm, encryption)
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,
@@ -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
- return from(jwk).block_decrypt(encrypted)
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
- return from(jwk).block_encrypt(plain_text, jwe)
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
- other_public_key, my_private_key = box_keys
692
- return from(other_public_key).box_encrypt(plain_text, my_private_jwk, jwe)
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] my_private_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, my_private_jwk = nil, jwe = nil)
706
- generated_jwk = nil
707
- other_public_jwk = self
708
- if my_private_jwk.nil?
709
- generated_jwk = my_private_jwk = other_public_jwk.generate_key
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 my_private_jwk.is_a?(JOSE::JWK)
712
- my_private_jwk = JOSE::JWK.from(my_private_jwk)
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 = other_public_jwk.block_encryptor
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', my_private_jwk.fields['kid'] || my_private_jwk.thumbprint)
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', other_public_jwk.fields['kid'] || other_public_jwk.thumbprint)
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', my_private_jwk.to_public_map)
741
+ jwe = jwe.put('epk', jwk_secret.to_public_map)
729
742
  end
730
743
  end
731
- if generated_jwk
732
- return JOSE::JWE.block_encrypt([other_public_jwk, my_private_jwk], plain_text, jwe), generated_jwk
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([other_public_jwk, my_private_jwk], plain_text, jwe)
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(jwk, plain_text, jws = nil, header = nil)
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
 
@@ -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'
@@ -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
@@ -134,7 +134,7 @@ class JOSE::JWK::KTY_OKP_Ed25519ph < Struct.new(:okp)
134
134
  end
135
135
 
136
136
  def to_okp
137
- return [:Ed25519, okp]
137
+ return [:Ed25519ph, okp]
138
138
  end
139
139
 
140
140
  def to_openssh_key(fields)
@@ -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 or secret.bytesize == LEGACY_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 or secret.bytesize == LEGACY_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 and okp.bytesize != LEGACY_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 (okp.bytesize == SK_BYTES or okp.bytesize == LEGACY_SK_BYTES) and fields and fields['use'] == 'sig' and not fields['alg'].nil?
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 (okp.bytesize == SK_BYTES or okp.bytesize == LEGACY_SK_BYTES)
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 or okp.bytesize == LEGACY_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 or okp[1].bytesize == LEGACY_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 or secret.bytesize == LEGACY_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 or secret.bytesize == LEGACY_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 and okp.bytesize != LEGACY_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 (okp.bytesize == SK_BYTES or okp.bytesize == LEGACY_SK_BYTES) and fields and fields['use'] == 'sig' and not fields['alg'].nil?
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 (okp.bytesize == SK_BYTES or okp.bytesize == LEGACY_SK_BYTES)
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 or okp.bytesize == LEGACY_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 or okp[1].bytesize == LEGACY_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]"
@@ -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
@@ -0,0 +1,7 @@
1
+ require 'delegate'
2
+
3
+ class JOSE::JWK::PKeyProxy < SimpleDelegator
4
+ def ==(other)
5
+ __getobj__.export == other.export
6
+ end
7
+ end
@@ -1,8 +1,12 @@
1
1
  module JOSE::JWS::ALG
2
2
 
3
- extend self
4
-
5
- def generate_key(parameters, algorithm)
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'
@@ -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(jwk, plain_text, jws, header)
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.
@@ -1,3 +1,3 @@
1
1
  module JOSE
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.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: 1.0.0
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-07 00:00:00.000000000 Z
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