jose 0.3.1 → 1.0.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.
@@ -11,6 +11,8 @@ module JOSE::JWA::ConcatKDF
11
11
  end
12
12
  if other_info.is_a?(Array)
13
13
  algorithm_id, party_u_info, party_v_info, supp_pub_info, supp_priv_info = other_info
14
+ party_u_info ||= ''
15
+ party_v_info ||= ''
14
16
  supp_pub_info ||= ''
15
17
  supp_priv_info ||= ''
16
18
  other_info = [
@@ -3,7 +3,7 @@ module JOSE::JWA::Curve25519_Ruby
3
3
  extend self
4
4
 
5
5
  def __ruby__?; true; end
6
- def __supported__?; JOSE.__crypto_fallback__; end
6
+ def __supported__?; JOSE.crypto_fallback; end
7
7
 
8
8
  def ed25519_keypair(secret = nil)
9
9
  return JOSE::JWA::Ed25519.keypair(secret)
@@ -3,7 +3,7 @@ module JOSE::JWA::Curve448_Ruby
3
3
  extend self
4
4
 
5
5
  def __ruby__?; true; end
6
- def __supported__?; JOSE.__crypto_fallback__; end
6
+ def __supported__?; JOSE.crypto_fallback; end
7
7
 
8
8
  def ed448_keypair(secret = nil)
9
9
  return JOSE::JWA::Ed448.keypair(secret)
@@ -72,10 +72,10 @@ module JOSE::JWA::Ed448
72
72
  curve448_scalar.setbyte(56, 0)
73
73
  a_s = OpenSSL::BN.new(curve448_scalar.reverse, 2).to_i
74
74
  # Calculate r_s and r (r only used in encoded form)
75
- r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, seed, m].pack('A*CCA*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
75
+ r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, seed, m].pack('a*CCa*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
76
76
  r = (C_B * r_s).encode()
77
77
  # Calculate h.
78
- h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
78
+ h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('a*CCa*a*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
79
79
  # Calculate s.
80
80
  s = OpenSSL::BN.new((r_s+h*a_s) % JOSE::JWA::Edwards448Point::L).to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse
81
81
  # The final signature is concatenation of r and s.
@@ -86,7 +86,7 @@ module JOSE::JWA::Ed448
86
86
  raise ArgumentError, "sk must be #{C_secretkeybytes} bytes" if sk.bytesize != C_secretkeybytes and sk.bytesize != C_legacysecretkeybytes
87
87
  ctx ||= ''
88
88
  raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
89
- m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('A*CCA*A*'), 64)
89
+ m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('a*CCa*a*'), 64)
90
90
  secret, pk = nil, nil
91
91
  if sk.bytesize == C_secretkeybytes
92
92
  secret, pk = sk[0, 57], sk[57, 114]
@@ -100,10 +100,10 @@ module JOSE::JWA::Ed448
100
100
  curve448_scalar.setbyte(56, 0)
101
101
  a_s = OpenSSL::BN.new(curve448_scalar.reverse, 2).to_i
102
102
  # Calculate r_s and r (r only used in encoded form)
103
- r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, seed, m].pack('A*CCA*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
103
+ r_s = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, seed, m].pack('a*CCa*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
104
104
  r = (C_B * r_s).encode()
105
105
  # Calculate h.
106
- h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
106
+ h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('a*CCa*a*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
107
107
  # Calculate s.
108
108
  s = OpenSSL::BN.new((r_s+h*a_s) % JOSE::JWA::Edwards448Point::L).to_s(2).rjust(C_bytes, JOSE::JWA::ZERO_PAD).reverse
109
109
  # The final signature is concatenation of r and s.
@@ -124,7 +124,7 @@ module JOSE::JWA::Ed448
124
124
  # Check parse results.
125
125
  return false if r_p.nil? or a_p.nil? or s_s > JOSE::JWA::Edwards448Point::L
126
126
  # Calculate h.
127
- h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
127
+ h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 0, ctx.bytesize, ctx, r, pk, m].pack('a*CCa*a*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
128
128
  # Calculate left and right sides of check eq.
129
129
  rhs = r_p + (a_p * h)
130
130
  lhs = C_B * s_s
@@ -139,7 +139,7 @@ module JOSE::JWA::Ed448
139
139
  def verify_ph(sig, m, pk, ctx = nil)
140
140
  ctx ||= ''
141
141
  raise ArgumentError, "ctx must be 255 bytes or smaller" if ctx.bytesize > 255
142
- m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('A*CCA*A*'), 64)
142
+ m = JOSE::JWA::SHA3.shake256(['SigEd448', 2, ctx.bytesize, ctx, m].pack('a*CCa*a*'), 64)
143
143
  # Sanity-check sizes.
144
144
  return false if sig.bytesize != C_signaturebytes
145
145
  return false if pk.bytesize != C_publickeybytes
@@ -151,7 +151,7 @@ module JOSE::JWA::Ed448
151
151
  # Check parse results.
152
152
  return false if r_p.nil? or a_p.nil? or s_s > JOSE::JWA::Edwards448Point::L
153
153
  # Calculate h.
154
- h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('A*CCA*A*A*A*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
154
+ h = (OpenSSL::BN.new(JOSE::JWA::SHA3.shake256(['SigEd448', 1, ctx.bytesize, ctx, r, pk, m].pack('a*CCa*a*a*a*'), 114).reverse, 2) % JOSE::JWA::Edwards448Point::L).to_i
155
155
  # Calculate left and right sides of check eq.
156
156
  rhs = r_p + (a_p * h)
157
157
  lhs = C_B * s_s
@@ -1,21 +1,430 @@
1
1
  module JOSE
2
2
 
3
3
  class EncryptedBinary < ::String
4
+ # Expands a compacted encrypted binary or list of encrypted binaries into a map.
5
+ # @see JOSE::JWE.expand
6
+ # @return [JOSE::EncryptedMap]
4
7
  def expand
5
8
  return JOSE::JWE.expand(self)
6
9
  end
10
+
11
+ # Returns the decoded ciphertext portion of a encrypted binary or map without decrypting the ciphertext.
12
+ # @see JOSE::JWE.peek_ciphertext
13
+ # @return [String]
14
+ def peek_ciphertext
15
+ return JOSE::JWE.peek_ciphertext(self)
16
+ end
17
+
18
+ # Returns the decoded encrypted key portion of a encrypted binary or map without decrypting the ciphertext.
19
+ # @see JOSE::JWE.peek_encrypted_key
20
+ # @return [String]
21
+ def peek_encrypted_key
22
+ return JOSE::JWE.peek_encrypted_key(self)
23
+ end
24
+
25
+ # Returns the decoded initialization vector portion of a encrypted binary or map without decrypting the ciphertext.
26
+ # @see JOSE::JWE.peek_iv
27
+ # @return [String]
28
+ def peek_iv
29
+ return JOSE::JWE.peek_iv(self)
30
+ end
31
+
32
+ # Returns the decoded protected portion of a encrypted binary or map without decrypting the ciphertext.
33
+ # @see JOSE::JWE.peek_protected
34
+ # @return [JOSE::Map]
35
+ def peek_protected
36
+ return JOSE::JWE.peek_protected(self)
37
+ end
38
+
39
+ # Returns the decoded tag portion of a encrypted binary or map without decrypting the ciphertext.
40
+ # @see JOSE::JWE.peek_tag
41
+ # @return [String]
42
+ def peek_tag
43
+ return JOSE::JWE.peek_tag(self)
44
+ end
7
45
  end
8
46
 
47
+ # Immutable encrypted Map structure based on {JOSE::Map JOSE::Map}.
9
48
  class EncryptedMap < JOSE::Map
49
+ # Compacts an expanded encrypted map into a binary.
50
+ # @see JOSE::JWE.compact
51
+ # @return [JOSE::EncryptedBinary]
10
52
  def compact
11
53
  return JOSE::JWE.compact(self)
12
54
  end
13
55
  end
14
56
 
57
+ # JWE stands for JSON Web Encryption which is defined in [RFC 7516](https://tools.ietf.org/html/rfc7516).
58
+ #
59
+ # ## Key Derivation Algorithms
60
+ #
61
+ # The following key derivation algorithms for the `"alg"` field are currently supported by {JOSE::JWE JOSE::JWE} (some may need the {JOSE.crypto_fallback= JOSE.crypto_fallback=} option to be enabled):
62
+ #
63
+ # * `"A128GCMKW"`
64
+ # * `"A192GCMKW"`
65
+ # * `"A256GCMKW"`
66
+ # * `"A128KW"`
67
+ # * `"A192KW"`
68
+ # * `"A256KW"`
69
+ # * `"dir"`
70
+ # * `"ECDH-ES"`
71
+ # * `"ECDH-ES+A128KW"`
72
+ # * `"ECDH-ES+A192KW"`
73
+ # * `"ECDH-ES+A256KW"`
74
+ # * `"PBES2-HS256+A128KW"`
75
+ # * `"PBES2-HS384+A192KW"`
76
+ # * `"PBES2-HS512+A256KW"`
77
+ # * `"RSA1_5"`
78
+ # * `"RSA-OAEP"`
79
+ # * `"RSA-OAEP-256"`
80
+ #
81
+ # ## Encryption Algorithms
82
+ #
83
+ # The following encryption algorithms for the `"enc"` field are currently supported by {JOSE::JWE JOSE::JWE} (some may need the {JOSE.crypto_fallback= JOSE.crypto_fallback=} option to be enabled):
84
+ #
85
+ # * `"A128CBC-HS256"`
86
+ # * `"A192CBC-HS384"`
87
+ # * `"A256CBC-HS512"`
88
+ # * `"A128GCM"`
89
+ # * `"A192GCM"`
90
+ # * `"A256GCM"`
91
+ #
92
+ # ## Compression Algorithms
93
+ #
94
+ # The following compression algorithms for the `"zip"` field are currently supported by {JOSE::JWE JOSE::JWE}:
95
+ #
96
+ # * `"DEF"`
97
+ #
98
+ # ## Key Derivation Examples
99
+ #
100
+ # All of the examples below will use `"enc"` set to `"A128GCM"`, `"A192GCM"`, or `"A256GCM"` depending on the derived key size.
101
+ #
102
+ # The octet key used will typically be all zeroes of the required size in the form of `([0]*16).pack('C*')` (for a 128-bit key).
103
+ #
104
+ # All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d)
105
+ #
106
+ # !!!ruby
107
+ # # octet keys we'll use below
108
+ # jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
109
+ # jwk_oct192 = JOSE::JWK.from_oct(([0]*24).pack('C*'))
110
+ # jwk_oct256 = JOSE::JWK.from_oct(([0]*32).pack('C*'))
111
+ # jwk_secret = JOSE::JWK.from_oct("secret")
112
+ #
113
+ # # EC keypairs we'll use below
114
+ # jwk_ec256_alice_sk = JOSE::JWK.generate_key([:ec, "P-256"])
115
+ # jwk_ec256_alice_pk = JOSE::JWK.to_public(jwk_ec256_alice_sk)
116
+ # jwk_ec256_bob_sk = JOSE::JWK.generate_key([:ec, "P-256"])
117
+ # jwk_ec256_bob_pk = JOSE::JWK.to_public(jwk_ec256_bob_sk)
118
+ #
119
+ # # X25519 keypairs we'll use below
120
+ # jwk_x25519_alice_sk = JOSE::JWK.generate_key([:okp, :X25519])
121
+ # jwk_x25519_alice_pk = JOSE::JWK.to_public(jwk_x25519_alice_sk)
122
+ # jwk_x25519_bob_sk = JOSE::JWK.generate_key([:okp, :X25519])
123
+ # jwk_x25519_bob_pk = JOSE::JWK.to_public(jwk_x25519_bob_sk)
124
+ #
125
+ # # X448 keypairs we'll use below
126
+ # jwk_x448_alice_sk = JOSE::JWK.generate_key([:okp, :X448])
127
+ # jwk_x448_alice_pk = JOSE::JWK.to_public(jwk_x448_alice_sk)
128
+ # jwk_x448_bob_sk = JOSE::JWK.generate_key([:okp, :X448])
129
+ # jwk_x448_bob_pk = JOSE::JWK.to_public(jwk_x448_bob_sk)
130
+ #
131
+ # # RSA keypairs we'll use below
132
+ # jwk_rsa_sk = JOSE::JWK.generate_key([:rsa, 4096])
133
+ # jwk_rsa_pk = JOSE::JWK.to_public(jwk_rsa_sk)
134
+ #
135
+ # ### <a name="AESGCMKW-group">A128GCMKW, A192GCMKW, and A256GCMKW</a>
136
+ #
137
+ # !!!ruby
138
+ # # A128GCMKW
139
+ # encrypted_a128gcmkw = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "A128GCMKW", "enc" => "A128GCM" }).compact
140
+ # # => "eyJhbGciOiJBMTI4R0NNS1ciLCJlbmMiOiJBMTI4R0NNIiwiaXYiOiJzODNFNjhPNjhsWlM5ZVprIiwidGFnIjoieF9Ea2M5dm1LMk5RQV8tU2hvTkFRdyJ9.8B2qX8fVEa-s61RsZXqkCg.J7yJ8sKLbUlzyor6.FRs.BhBwImTv9B14NwVuxmfU6A"
141
+ # JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128gcmkw).first
142
+ # # => "{}"
143
+ #
144
+ # # A192GCMKW
145
+ # encrypted_a192gcmkw = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "A192GCMKW", "enc" => "A192GCM" }).compact
146
+ # # => "eyJhbGciOiJBMTkyR0NNS1ciLCJlbmMiOiJBMTkyR0NNIiwiaXYiOiIxMkduZWQyTDB6NE5LZG83IiwidGFnIjoiM0thbG9iaER1Wmx5dE1YSjhjcXhZZyJ9.jJC4E1c6augIhvGDp3fquRfO-mnnud4F.S2NkKNGxBKTsCnKo.gZA.MvfhqSTeEN75H8HDyvfzRQ"
147
+ # JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192gcmkw).first
148
+ # # => "{}"
149
+ #
150
+ # # A256GCMKW
151
+ # encrypted_a256gcmkw = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "A256GCMKW", "enc" => "A256GCM" }).compact
152
+ # # => "eyJhbGciOiJBMjU2R0NNS1ciLCJlbmMiOiJBMjU2R0NNIiwiaXYiOiJHU3lFMTBLQURxZTczNUMzIiwidGFnIjoiR3dVbDJCbXRNWlVseDlXNEMtY0tQZyJ9.sSsbFw9z8WTkzBLvPMywSedTXXygFxfP9g5U2qpzUX8.eiVFfe7iojfK0AXb._v8.YVfk9dNrtS7wxbGqCVge-g"
153
+ # JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256gcmkw).first
154
+ # # => "{}"
155
+ #
156
+ # ### <a name="AESKW-group">A128KW, A192KW, and A256KW</a>
157
+ #
158
+ # !!!ruby
159
+ # # A128KW
160
+ # encrypted_a128kw = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "A128KW", "enc" => "A128GCM" }).compact
161
+ # # => "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw"
162
+ # JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128kw).first
163
+ # # => "{}"
164
+ #
165
+ # # A192KW
166
+ # encrypted_a192kw = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "A192KW", "enc" => "A192GCM" }).compact
167
+ # # => "eyJhbGciOiJBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIn0.edpvNrztlNADbkwfq5YBJgqFBSH_Znv1Y1uXKNQ_13w.yCkEYTCPOKH6CoxZ.siw.zP_ZM9OEeX1FIdFjqNawtQ"
168
+ # JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192kw).first
169
+ # # => "{}"
170
+ #
171
+ # # A256KW
172
+ # encrypted_a256kw = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "A256KW", "enc" => "A256GCM" }).compact
173
+ # # => "eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIn0.OvAhC1a2BoP_2SMIiZXwIHWPoIkD-Cosgp3nlpiTs8ySUBPfPzwG1g.4GeackYJbuBksAWA.HPE.vG0sGC2kuklH5xk8KXhyNA"
174
+ # JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256kw).first
175
+ # # => "{}"
176
+ #
177
+ # ### <a href="direct-group">dir</a>
178
+ #
179
+ # The `"dir"` key derivation algorithm is essentially just a pass-through to the underlying `"enc"` algorithm.
180
+ #
181
+ # The `"encrypted_key"` is not included in the protected header, so the key must be fully known by both parties.
182
+ #
183
+ # !!!ruby
184
+ # # dir
185
+ # encrypted_dir = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM" }).compact
186
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..HdRR8O0kk_SvOjAS.rxo.JTMPGPKZZKVNlWV0RexsmQ"
187
+ # JOSE::JWE.block_decrypt(jwk_oct128, encrypted_dir).first
188
+ # # => "{}"
189
+ #
190
+ # ### <a name="ECDH-ES-group">ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW, and ECDH-ES+A256KW</a>
191
+ #
192
+ # The `"ECDH-ES"` key derivation algorithm does not include the `"encrypted_key"` field in the protected header, similar to how `"dir"` functions.
193
+ #
194
+ # The size of the generated key is dependent on the `"enc"` setting (for example, `"A128GCM"` will generate a 128-bit key, `"A256GCM"` a 256-bit key, etc).
195
+ #
196
+ # !!!ruby
197
+ # # ECDH-ES with EC keypairs
198
+ # encrypted_ecdhes_ec256_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
199
+ # # => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJQLTI1NiIsImt0eSI6IkVDIiwieCI6IjQ4UVUzUTBDeVN4d0piRXdXckpyWVhscDg4X2RWcEhUeHE0YXZjNjZoNVEiLCJ5IjoiWnpxcklOdE1NeEh4US1RQjcyUk1jZGxtRHNPSXdsS2hNcVZtX2dZV0MxNCJ9fQ..UssNrY5qEeFdluZY.R6g.32nlr0wHF2TwfL1UnBtIow"
200
+ # JOSE::JWE.block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhes_ec256_alice2bob).first
201
+ # # => "{}"
202
+ #
203
+ # # ECDH-ES with X25519 keypairs
204
+ # encrypted_ecdhes_x25519_alice2bob = JOSE::JWE.block_encrypt([jwk_x25519_bob_pk, jwk_x25519_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
205
+ # # => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYMjU1MTkiLCJrdHkiOiJPS1AiLCJ4IjoiZ0g3TjJwT0duenZfd0tBLUhqREZKTlVSZVhfdG05XzdiMkZSUjI3cXFYcyJ9fQ..T-0q42FPCUy3hlla.MHU.9TNP2jG5bN1vSvaesijdww"
206
+ # JOSE::JWE.block_decrypt([jwk_x25519_alice_pk, jwk_x25519_bob_sk], encrypted_ecdhes_x25519_alice2bob).first
207
+ # # => "{}"
208
+ #
209
+ # # ECDH-ES with X448 keypairs
210
+ # encrypted_ecdhes_x448_alice2bob = JOSE::JWE.block_encrypt([jwk_x448_bob_pk, jwk_x448_alice_sk], "{}", { "alg" => "ECDH-ES", "enc" => "A128GCM" }).compact
211
+ # # => "eyJhbGciOiJFQ0RILUVTIiwiZW5jIjoiQTEyOEdDTSIsImVwayI6eyJjcnYiOiJYNDQ4Iiwia3R5IjoiT0tQIiwieCI6ImFFaHZISGxFM2V1Y3lsY0RNNzBMd1paY2dDRk9acXExNWM3YXZNMjJkcWZIUEtja1FZNmo3LXFfM19kMGI1cGVWZEFoNVoyQWZIWSJ9fQ..T-UNE-wOApuRH71r.Uj8.l8bIfhC1UPAPVWBV3wkc6A"
212
+ # JOSE::JWE.block_decrypt([jwk_x448_alice_pk, jwk_x448_bob_sk], encrypted_ecdhes_x448_alice2bob).first
213
+ # # => "{}"
214
+ #
215
+ # When decrypting with any of the `"ECDH-ES"` related algorithms, the other party's public key is recommended, but not required for decryption (the embedded Ephemeral Public Key will be used instead):
216
+ #
217
+ # !!!ruby
218
+ # # decrypting the X448 example with and without the public key specified
219
+ # JOSE::JWE.block_decrypt([jwk_x448_alice_pk, jwk_x448_bob_sk], encrypted_ecdhes_x448_alice2bob).first
220
+ # # => "{}"
221
+ # JOSE::JWE.block_decrypt(jwk_x448_bob_sk, encrypted_ecdhes_x448_alice2bob).first
222
+ # # => "{}"
223
+ #
224
+ # The `"ECDH-ES+A128KW"`, `"ECDH-ES+A192KW"`, and `"ECDH-ES+A256KW"` key derivation algorithms do include the `"encrypted_key"` and the suffix after `"ECDH-ES+"` determines the key size (so `"ECDH-ES+A128KW"` computes a 128-bit key).
225
+ #
226
+ # !!!ruby
227
+ # # ECDH-ES+A128KW with EC keypairs
228
+ # encrypted_ecdhesa128kw_alice2bob = JOSE::JWE.block_encrypt([jwk_ec256_bob_pk, jwk_ec256_alice_sk], "{}", { "alg" => "ECDH-ES+A128KW", "enc" => "A128GCM" }).compact
229
+ # # => "eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhHQ00iLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiI0OFFVM1EwQ3lTeHdKYkV3V3JKcllYbHA4OF9kVnBIVHhxNGF2YzY2aDVRIiwieSI6Ilp6cXJJTnRNTXhIeFEtUUI3MlJNY2RsbURzT0l3bEtoTXFWbV9nWVdDMTQifX0.ZwuqXf7svd3SH0M-XYLjWz5JsN6xX03C.l8tt83EJjy86IovL.i5A.nw05dPUA0a18xdtvmHbhHA"
230
+ # JOSE::JWE::block_decrypt([jwk_ec256_alice_pk, jwk_ec256_bob_sk], encrypted_ecdhesa128kw_alice2bob).first
231
+ # # => "{}"
232
+ #
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
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
237
+ # # => "{}"
238
+ #
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
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
243
+ # # => "{}"
244
+ #
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.
246
+ #
247
+ # ### <a name="PBES2-group">PBES2-HS256+A128KW, PBES2-HS384+A192KW, and PBES2-HS512+A256KW</a>
248
+ #
249
+ # !!!ruby
250
+ # # PBES2-HS256+A128KW
251
+ # encrypted_pbes2hs256a128kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS256+A128KW", "enc" => "A128GCM" }).compact
252
+ # # => "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMnMiOiJRR0laNTlzbjRnQThySHBWYjFrSkd3In0.8WMQ0fysLiHU8AjpjkcqJGpYe53VRf2s.vVEb2ZtKmtPIw8M-.Cmg.GCjDtdKV6khqEuyZy2gUxw"
253
+ # JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs256a128kw).first
254
+ # # => "{}"
255
+ #
256
+ # # PBES2-HS384+A192KW
257
+ # encrypted_pbes2hs384a192kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS384+A192KW", "enc" => "A192GCM" }).compact
258
+ # # => "eyJhbGciOiJQQkVTMi1IUzM4NCtBMTkyS1ciLCJlbmMiOiJBMTkyR0NNIiwicDJjIjo2MTQ0LCJwMnMiOiJKSDRjZ0hlNTZiU0prZ1d6VktpWWJCb0FzWEJBY1A1NiJ9.Ck5GvgXxmyac3jzs0lRavoRh6tI9nEs3lYkx8sdDzGw.IdxaPATMkQ8FYiYQ.uHk.rDU6ltWsTsw9vuvA73bgJQ"
259
+ # JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs384a192kw).first
260
+ # # => "{}"
261
+ #
262
+ # # PBES2-HS512+A256KW
263
+ # encrypted_pbes2hs512a256kw = JOSE::JWE.block_encrypt(jwk_secret, "{}", { "alg" => "PBES2-HS512+A256KW", "enc" => "A256GCM" }).compact
264
+ # # => "eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJlbmMiOiJBMjU2R0NNIiwicDJjIjo4MTkyLCJwMnMiOiJ6YWRiMVNmT1F4V1gyTHJrSVgwWDFGM2QzNlBIdUdxRVFzUDVhbWVnTk00In0.6SUVO9sSevqZrZ5yPX-JvJNJrzfIQeTTzrkWBHEqHra1_AITtwEe0A.0AaF_3ZlJOkRlqgb.W8I.jFWob73QTn52IFSIPEWHFA"
265
+ # JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2hs512a256kw).first
266
+ # # => "{}"
267
+ #
268
+ # The `"p2s"` and `"p2i"` fields may also be specified to control the Salt and Iterations of the PBES2 Key Derivation Function, respectively.
269
+ #
270
+ # The default Salt is a randomly generated binary the same length of bytes as the key wrap (for example, `"PBES2-HS256+A128KW"` will generate a 16-byte Salt).
271
+ #
272
+ # The default Iterations is 32 times the number of bits specified by the key wrap (for example, `"PBES2-HS256+A128KW"` will have 4096 Iterations).
273
+ #
274
+ # !!!ruby
275
+ # # let's setup the JWE header
276
+ # iterations = 8192
277
+ # salt = ([0]*32).pack('C*') # all zero 256-bit salt, for example usage only
278
+ # jwe = {
279
+ # "alg" => "PBES2-HS256+A128KW",
280
+ # "enc" => "A128GCM",
281
+ # "p2i" => iterations,
282
+ # "p2s" => JOSE.urlsafe_encode64(salt)
283
+ # }
284
+ # # PBES2-HS256+A128KW
285
+ # encrypted_pbes2 = JOSE::JWE.block_encrypt(jwk_secret, "{}", jwe).compact
286
+ # # => "eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwicDJjIjo0MDk2LCJwMmkiOjgxOTIsInAycyI6IkFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUEifQ.I7wcBmg7O_rOWpg1aak7wQWX84YtED6k.Rgh3f6Kzl5SZ1z7x.FNo.eyK1ySx4SGR-xC2EYNySQA"
287
+ # JOSE::JWE.block_decrypt(jwk_secret, encrypted_pbes2).first
288
+ # # => "{}"
289
+ #
290
+ # ### <a name="RSA-group">RSA1_5, RSA-OAEP, and RSA-OAEP-256</a>
291
+ #
292
+ # !!!ruby
293
+ # # RSA1_5
294
+ # encrypted_rsa1_5 = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA1_5", "enc" => "A128GCM" }).compact
295
+ # # => "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4R0NNIn0.NlndPTqULN1vArshEzfEXY0nHCf4ubsTK9iHAeIxL85fReYrYG8EDB2_IirUneavvHSa-hsVLXNzBu0F9OY3TRFAIuJ8Jt1tqZZEhHZ97vzTEIjdlPNctGNI11-mhNCJ0doSvx9T4ByngaAFtJnRoR2cqbJkJFGja60fHtO0CfKLW5XzPf0NAhr8Tof-5IJfbNpMcC_LdCItJ6i8cuj4i5pG_CikOKDrNzbaBP72200_kl_-YaLDMA4tVb2YjWksY5Vau0Hz16QvI9QwDIcIDLYPAlTlDrU7s_FfmO_89S9Z69-lc_OBG7x2CYzIhB-0wzx753nZRl_WNJKi1Ya_AV552FEqVUhR-SuKcyrTA9OwkKC2JoL3lFqsCL9jkZkBrVREQlT0cxNI_AInyx5FHNLBbdtkz0JQbvzMJ854RP0V_eTlI5u8DZ42aOTRMBLHPi-4gP0J_CGWyKDQreXEEF6LSuLJb1cGk-NX1Vd85aARstQPuOoy7pWJjPvBEKEib70fjkUuMA0Atid-5BusQLKc1H-D6c5HIFH0DgYtXhN6AtQ_fmqw1F_X1JrGnYiYGzJCD2hh0Yt2UJZoCuHlPKk8aM5L3lNU3AISb1soSQl3hfX8Skb817ffC7jYezdhZc12cRNzOPAYqJYjN2eDlQhx-gpFjVzc-W1bFG8Yijo.grliT3M1iZ48aSY9.F4Y.pBRqIGZ4Q_fI1kmeAggvRg"
296
+ # JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsa1_5).first
297
+ # # => "{}"
298
+ #
299
+ # # RSA-OAEP
300
+ # encrypted_rsaoaep = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA-OAEP", "enc" => "A128GCM" }).compact
301
+ # # => "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.YZfGKTTU2KuvwIMpSYadbNmGzWIbLrwRYD8JvZAWkvcnFeky09S04VadRNPXmCBSl4EF1K7oBm0fiYXuvNbLFNKYT_Jo_y6Lb-XsP--BZKaEcq6wIdZ4-xTJ7YYX5dfco_cMknZLG8W2sQRwtWopisn9NyzSpfGNlYqeJqjpoJy0qnO8yZeEYeadwoVF9-XZfYwvMjEt7HORqBIPF1JIaOYTQ-LQBvya6XYhOR7dkSnuCZ_ITGW5ZbPvzOILSMW_3Ixe78ncfO2gxF6AiLh02oTLsOSrF9xDlJvuU0k1TdkNWtGroeP_WVbXEO7O_GI5LVW-cDzoVm5ZCQs2Df0018-qDxFyY9xhKS9aNDi_btiarstXMSz3EkOfPhWR_IzlVyUkYnzs3GS993gKLQ0Tk-ipvOT9Bcw9VTLLK3-f5YSkf51IA---hPFlxVlboH9bmTXlT4JzSbErQEYp3JuXjOP7FQn0OPko5Utqbbm41XBEJhUpBNhjrBGDspsMxML_eJdyzBgA5UyNfdCEQ2vM1pCegxG_hSKAhCKVNn71wW4O_y_eqUcoyhjB7HtVxiF29jzNUKF-y14171L4-mxsIpixaM1ofnayWMiherVP0Wz2MXkzWB0AUv8c3kNEJIh3oeyrczWwzpmeCh1Bq7-J4D6aaFjyGFcm-03_QZmfwho.ymxveKBeRuaZ8HzD.3H4.6oKLh2NouhPGpO1dmA-tTg"
302
+ # JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep).first
303
+ # # => "{}"
304
+ #
305
+ # # RSA-OAEP-256
306
+ # encrypted_rsaoaep256 = JOSE::JWE.block_encrypt(jwk_rsa_pk, "{}", { "alg" => "RSA-OAEP-256", "enc" => "A128GCM" }).compact
307
+ # # => "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMTI4R0NNIn0.OW9Hy9qpOIgVueODQXcWIUw_-Sm3UFGtxosyOAaI6JUQFt8q-iEtKkUp4NHrOlczO6tP5t8zRKdNXFfCm9QZk6F9PsSO-NzE2-DV1ANAMck-CDfGTK0mwG5U_KZwlObSgU0gxf87K49Wuno1rWlHWzJb__C_hCJXi_aQW17tLmbuTpJMkB0NTCKX3y6QaxvynP98jqwMJT6uGmE3AeuZYhPGzAOWbltbWyw-TqWqyLJirAUY_fvDNsKt1TDrTd9216TK5y7RQeUtdGfbuYK9lt2TIwfh9ycAHd7SANH_YJc2cKYa3e6CgqnQAjVpbhpogBz5sz5HaK95XYbXOdnYyHQ00gS44YquiQCvX331UgEWnthtmYwDZfnCxTkPydafGOBsjaagGvV2tQtxUKW3JmVChF97bNj5lQZ7rAkyooxx-k3IMT0005x6_74O5tXGN5fb7oyT3Mx_NZ5dKzlYAA_V8oOpNslaFhV5K5Q_-hRkUsEPWdaD5s2uS9Z7l7ot39CzzTKDj65f2eCTWFReFKOjhabCL4ZiFXbElB3dA3y5FdxXPAfe6N31G9ynalx1JIcrEaRb8sdqk6U6uC3s3DpkoRSnp3osBJOxxuk_Lgb-ZM9d8UuRVj4W78-qjfX_lcG1RlRmlYoDIU03ly0UfRWi-7HmpPECrGTsGZEfULg.J-txckmMXEi-bZVh.Rbw.D7UpSkticmDCGiNyLVggLg"
308
+ # JOSE::JWE.block_decrypt(jwk_rsa_sk, encrypted_rsaoaep256).first
309
+ # # => "{}"
310
+ #
311
+ # ## Encryption Examples
312
+ #
313
+ # All of the examples below will use `"alg"` set to `"dir"` passing the key directly to the Encryption Algorithm.
314
+ #
315
+ # The octet key used will typically be all zeroes of the required size in the form of `([0]*16).pack('C*')` (for a 128-bit key).
316
+ #
317
+ # All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d)
318
+ #
319
+ # !!!ruby
320
+ # # octet keys we'll use below
321
+ # jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
322
+ # jwk_oct192 = JOSE::JWK.from_oct(([0]*24).pack('C*'))
323
+ # jwk_oct256 = JOSE::JWK.from_oct(([0]*32).pack('C*'))
324
+ # jwk_oct384 = JOSE::JWK.from_oct(([0]*48).pack('C*'))
325
+ # jwk_oct512 = JOSE::JWK.from_oct(([0]*64).pack('C*'))
326
+ #
327
+ # ### <a name="AESCBC-group">A128CBC-HS256, A192CBC-HS384, and A256CBC-HS512</a>
328
+ #
329
+ # !!!ruby
330
+ # # A128CBC-HS256
331
+ # encrypted_a128cbchs256 = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "dir", "enc" => "A128CBC-HS256" }).compact
332
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..bxps64-UIQoFvhkjr05e9A.HrtJ3AqrqJ4f5PHjGseHYw.kopJoTDxk34IVhheoToLSA"
333
+ # JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a128cbchs256).first
334
+ # # => "{}"
335
+ #
336
+ # # A192CBC-HS384
337
+ # encrypted_a192cbchs384 = JOSE::JWE.block_encrypt(jwk_oct384, "{}", { "alg" => "dir", "enc" => "A192CBC-HS384" }).compact
338
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyQ0JDLUhTMzg0In0..3zSCHwvHrcxsNyssIgEBRA.XB70tUoQZlnOgY5ygMxfKA.Avl7Z8jCpShh3_iTcPcU3Woh6E9ykNyB"
339
+ # JOSE::JWE.block_decrypt(jwk_oct384, encrypted_a192cbchs384).first
340
+ # # => "{}"
341
+ #
342
+ # # A256CBC-HS512
343
+ # encrypted_a256cbchs512 = JOSE::JWE.block_encrypt(jwk_oct512, "{}", { "alg" => "dir", "enc" => "A256CBC-HS512" }).compact
344
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIn0..mqMhkWAMF7HmW_Nu1ERUzQ.bzd-tmykuru0Lu_rsNZ2ow.mlOFO8JcC_UJ35TsZgiUeEwAjRDs6cwfN7Umyzm7mmY"
345
+ # JOSE::JWE.block_decrypt(jwk_oct512, encrypted_a256cbchs512).first
346
+ # # => "{}"
347
+ #
348
+ # ### <a name="AESGCM-group">A128GCM, A192GCM, and A256GCM</a>
349
+ #
350
+ # !!!ruby
351
+ # # A128GCM
352
+ # encrypted_a128gcm = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM" }).compact
353
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIn0..pPF4SbzGZwxS1J-M.Ic0.qkHuC-hOO44HPlykBJLSsA"
354
+ # JOSE::JWE.block_decrypt(jwk_oct128, encrypted_a128gcm).first
355
+ # # => "{}"
356
+ #
357
+ # # A192GCM
358
+ # encrypted_a192gcm = JOSE::JWE.block_encrypt(jwk_oct192, "{}", { "alg" => "dir", "enc" => "A192GCM" }).compact
359
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTkyR0NNIn0..muNgk2GFW9ATwqqZ.bvE.gYvC0G6DAodJdyrUqLw7Iw"
360
+ # JOSE::JWE.block_decrypt(jwk_oct192, encrypted_a192gcm).first
361
+ # # => "{}"
362
+ #
363
+ # # A256GCM
364
+ # encrypted_a256gcm = JOSE::JWE.block_encrypt(jwk_oct256, "{}", { "alg" => "dir", "enc" => "A256GCM" }).compact
365
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIn0..rDTJhd5ja5pDAYtn.PrM.MQdLgiVXQsG_cLas93ZEHw"
366
+ # JOSE::JWE.block_decrypt(jwk_oct256, encrypted_a256gcm).first
367
+ # # => "{}"
368
+ #
369
+ # ## Compression Examples
370
+ #
371
+ # All of the examples below will use `"alg"` set to `"dir"` passing the key directly to the Encryption Algorithm (`"enc"` is set to `"A128GCM"`).
372
+ #
373
+ # The octet key used will typically be all zeroes of the required size in the form of `([0]*16).pack('C*')` (for a 128-bit key).
374
+ #
375
+ # All of the example keys generated below can be found here: [https://gist.github.com/potatosalad/dd140560b2bdbdab886d](https://gist.github.com/potatosalad/dd140560b2bdbdab886d)
376
+ #
377
+ # !!!ruby
378
+ # # octet keys we'll use below
379
+ # jwk_oct128 = JOSE::JWK.from_oct(([0]*16).pack('C*'))
380
+ #
381
+ # ### <a name="DEF-group">DEF</a>
382
+ #
383
+ # !!!ruby
384
+ # # DEF
385
+ # encrypted_def = JOSE::JWE.block_encrypt(jwk_oct128, "{}", { "alg" => "dir", "enc" => "A128GCM", "zip" => "DEF" }).compact
386
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0..Vvr0vlKWE9rAJ8CR.UpOz7w10Uc9pMg.Pctxzz0ijPSOY8zyRcbjww"
387
+ # JOSE::JWE.block_decrypt(jwk_oct128, encrypted_def).first
388
+ # # => "{}"
15
389
  class JWE < Struct.new(:alg, :enc, :zip, :fields)
16
390
 
17
391
  # Decode API
18
392
 
393
+ # Converts a binary or map into a {JOSE::JWE JOSE::JWE}.
394
+ #
395
+ # !!!ruby
396
+ # JOSE::JWE.from({ "alg" => "dir" })
397
+ # # => #<struct JOSE::JWE
398
+ # # alg=#<struct JOSE::JWE::ALG_dir direct=true>,
399
+ # # enc=nil,
400
+ # # zip=nil,
401
+ # # fields=JOSE::Map[]>
402
+ # JOSE::JWE.from("{\"alg\":\"dir\"}")
403
+ # # => #<struct JOSE::JWE
404
+ # # alg=#<struct JOSE::JWE::ALG_dir direct=true>,
405
+ # # enc=nil,
406
+ # # zip=nil,
407
+ # # fields=JOSE::Map[]>
408
+ #
409
+ # There are 3 keys which can have custom modules defined for them:
410
+ #
411
+ # * `"alg"` - must implement {JOSE::JWE::ALG JOSE::JWE::ALG}
412
+ # * `"enc"` - must implement {JOSE::JWE::ENC JOSE::JWE::ENC}
413
+ # * `"zip"` - must implement {JOSE::JWE::ZIP JOSE::JWE::ZIP}
414
+ #
415
+ # For example:
416
+ #
417
+ # !!!ruby
418
+ # JOSE::JWE.from({ "alg" => "dir", "zip" => "custom" }, { zip: MyCustomCompress })
419
+ # # => #<struct JOSE::JWE
420
+ # # alg=#<struct JOSE::JWE::ALG_dir direct=true>,
421
+ # # enc=nil,
422
+ # # zip=#<MyCustomAlgorithm:0x007f8c5419ff68>,
423
+ # # fields=JOSE::Map[]>
424
+ #
425
+ # @param [JOSE::Map, Hash, String, JOSE::JWE, Array<JOSE::Map, Hash, String, JOSE::JWE>] object
426
+ # @param [Hash] modules
427
+ # @return [JOSE::JWE, Array<JOSE::JWE>]
19
428
  def self.from(object, modules = {})
20
429
  case object
21
430
  when JOSE::Map, Hash
@@ -24,55 +433,98 @@ module JOSE
24
433
  return from_binary(object, modules)
25
434
  when JOSE::JWE
26
435
  return object
436
+ when Array
437
+ return object.map { |obj| from(obj, modules) }
27
438
  else
28
- raise ArgumentError, "'object' must be a Hash, String, or JOSE::JWE"
439
+ raise ArgumentError, "'object' must be a Hash, String, JOSE::JWE, or Array"
29
440
  end
30
441
  end
31
442
 
443
+ # Converts a binary into a {JOSE::JWE JOSE::JWE}.
444
+ # @param [String, Array<String>] object
445
+ # @param [Hash] modules
446
+ # @return [JOSE::JWE, Array<JOSE::JWE>]
32
447
  def self.from_binary(object, modules = {})
33
448
  case object
34
449
  when String
35
450
  return from_map(JOSE.decode(object), modules)
451
+ when Array
452
+ return object.map { |obj| from_binary(obj, modules) }
36
453
  else
37
- raise ArgumentError, "'object' must be a String"
454
+ raise ArgumentError, "'object' must be a String or Array"
38
455
  end
39
456
  end
40
457
 
458
+ # Reads file and calls {.from_binary} to convert into a {JOSE::JWE JOSE::JWE}.
459
+ # @param [String] file
460
+ # @param [Hash] modules
461
+ # @return [JOSE::JWE]
41
462
  def self.from_file(file, modules = {})
42
463
  return from_binary(File.binread(file), modules)
43
464
  end
44
465
 
466
+ # Converts a map into a {JOSE::JWE JOSE::JWE}.
467
+ # @param [JOSE::Map, Hash, Array<JOSE::Map, Hash>] object
468
+ # @param [Hash] modules
469
+ # @return [JOSE::JWE, Array<JOSE::JWE>]
45
470
  def self.from_map(object, modules = {})
46
471
  case object
47
472
  when JOSE::Map, Hash
48
473
  return from_fields(JOSE::JWE.new(nil, nil, nil, JOSE::Map.new(object)), modules)
474
+ when Array
475
+ return object.map { |obj| from_map(obj, modules) }
49
476
  else
50
- raise ArgumentError, "'object' must be a Hash"
477
+ raise ArgumentError, "'object' must be a Hash or Array"
51
478
  end
52
479
  end
53
480
 
54
481
  # Encode API
55
482
 
483
+ # Converts a {JOSE::JWE JOSE::JWE} into a binary.
484
+ # @param [JOSE::Map, Hash, String, JOSE::JWE, Array<JOSE::Map, Hash, String, JOSE::JWE>] jwe
485
+ # @return [String, Array<String>]
56
486
  def self.to_binary(jwe)
57
- return from(jwe).to_binary
487
+ if jwe.is_a?(Array)
488
+ return from(jwe).map { |obj| obj.to_binary }
489
+ else
490
+ return from(jwe).to_binary
491
+ end
58
492
  end
59
493
 
494
+ # Converts a {JOSE::JWE JOSE::JWE} into a binary.
495
+ # @return [String]
60
496
  def to_binary
61
497
  return JOSE.encode(to_map)
62
498
  end
63
499
 
500
+ # Calls {.to_binary} on a {JOSE::JWE JOSE::JWE} and then writes the binary to `file`.
501
+ # @param [JOSE::Map, Hash, String, JOSE::JWE] jwe
502
+ # @param [String] file
503
+ # @return [Fixnum] bytes written
64
504
  def self.to_file(jwe, file)
65
505
  return from(jwe).to_file(file)
66
506
  end
67
507
 
508
+ # Calls {#to_binary} on a {JOSE::JWE JOSE::JWE} and then writes the binary to `file`.
509
+ # @param [String] file
510
+ # @return [Fixnum] bytes written
68
511
  def to_file(file)
69
512
  return File.binwrite(file, to_binary)
70
513
  end
71
514
 
515
+ # Converts a {JOSE::JWE JOSE::JWE} into a map.
516
+ # @param [JOSE::Map, Hash, String, JOSE::JWE, Array<JOSE::Map, Hash, String, JOSE::JWE>] jwe
517
+ # @return [JOSE::Map, Array<JOSE::Map>]
72
518
  def self.to_map(jwe)
73
- return from(jwe).to_map
519
+ if jwe.is_a?(Array)
520
+ return from(jwe).map { |obj| obj.to_map }
521
+ else
522
+ return from(jwe).to_map
523
+ end
74
524
  end
75
525
 
526
+ # Converts a {JOSE::JWE JOSE::JWE} into a map.
527
+ # @return [JOSE::Map]
76
528
  def to_map
77
529
  if zip.nil?
78
530
  return alg.to_map(enc.to_map(fields))
@@ -83,6 +535,35 @@ module JOSE
83
535
 
84
536
  # API
85
537
 
538
+ # Decrypts the `encrypted` binary or map using the `key`.
539
+ #
540
+ # !!!ruby
541
+ # jwk = JOSE::JWK.from({"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"})
542
+ # # => #<struct JOSE::JWK
543
+ # # keys=nil,
544
+ # # kty=#<struct JOSE::JWK::KTY_oct oct="I9j\xB4\x83\xA1X\x98\xE8Vv#R<E-\x9E\xA87Z\x01\xEFD\x00Dd\x93Y\x0F\xF9^\x03">,
545
+ # # fields=JOSE::Map[]>
546
+ # JOSE::JWE.block_decrypt(jwk, "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA")
547
+ # # => ["{}",
548
+ # # #<struct JOSE::JWE
549
+ # # alg=#<struct JOSE::JWE::ALG_dir direct=true>,
550
+ # # enc=
551
+ # # #<struct JOSE::JWE::ENC_AES_CBC_HMAC
552
+ # # cipher_name="aes-128-cbc",
553
+ # # bits=256,
554
+ # # cek_len=32,
555
+ # # iv_len=16,
556
+ # # enc_len=16,
557
+ # # mac_len=16,
558
+ # # tag_len=16,
559
+ # # hmac=OpenSSL::Digest::SHA256>,
560
+ # # zip=nil,
561
+ # # fields=JOSE::Map[]>]
562
+ #
563
+ # @see JOSE::JWE.block_encrypt
564
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
565
+ # @param [JOSE::EncryptedBinary, JOSE::EncryptedMap, Hash, String] encrypted
566
+ # @return [[String, JOSE::JWE]]
86
567
  def self.block_decrypt(key, encrypted)
87
568
  if encrypted.is_a?(String)
88
569
  encrypted = JOSE::JWE.expand(encrypted)
@@ -107,24 +588,66 @@ module JOSE
107
588
  end
108
589
  end
109
590
 
591
+ # Decrypts the `cipher_text` binary using the `key`, `aad`, `cipher_tag`, `encrypted_key`, and `iv`.
592
+ # @see JOSE::JWE.block_decrypt
593
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
594
+ # @param [String] aad
595
+ # @param [String] cipher_text
596
+ # @param [String] cipher_tag
597
+ # @param [String] encrypted_key
598
+ # @param [String] iv
599
+ # @return [[String, JOSE::JWE]]
110
600
  def block_decrypt(key, aad, cipher_text, cipher_tag, encrypted_key, iv)
111
601
  cek = key_decrypt(key, encrypted_key)
112
602
  return uncompress(enc.block_decrypt([aad, cipher_text, cipher_tag], cek, iv))
113
603
  end
114
604
 
605
+ # Encrypts the `block` using the `key`, `cek`, `iv`, and algorithm specified by the `jwe`.
606
+ #
607
+ # !!!ruby
608
+ # jwk = JOSE::JWK.from({"k" => "STlqtIOhWJjoVnYjUjxFLZ6oN1oB70QARGSTWQ_5XgM", "kty" => "oct"})
609
+ # # => #<struct JOSE::JWK
610
+ # # keys=nil,
611
+ # # kty=#<struct JOSE::JWK::KTY_oct oct="I9j\xB4\x83\xA1X\x98\xE8Vv#R<E-\x9E\xA87Z\x01\xEFD\x00Dd\x93Y\x0F\xF9^\x03">,
612
+ # # fields=JOSE::Map[]>
613
+ # JOSE::JWE.block_encrypt(jwk, "{}", { "alg" => "dir", "enc" => "A128CBC-HS256" })
614
+ # # => JOSE::EncryptedMap[
615
+ # # "tag" => "tSGaAlI2xiMThBZ9XTW2AQ",
616
+ # # "ciphertext" => "c2T5O6WafTCKEX5R_9D-LQ",
617
+ # # "encrypted_key" => "",
618
+ # # "iv" => "U56zV4sW4bOovxeXcz7fUg",
619
+ # # "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"]
620
+ #
621
+ # @see JOSE::JWE.block_decrypt
622
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
623
+ # @param [String, [String, String]] block
624
+ # @param [JOSE::JWE] jwe
625
+ # @param [String] cek
626
+ # @param [String] iv
627
+ # @return [JOSE::EncryptedMap]
115
628
  def self.block_encrypt(key, block, jwe, cek = nil, iv = nil)
116
629
  return from(jwe).block_encrypt(key, block, cek, iv)
117
630
  end
118
631
 
632
+ # Encrypts the `block` binary using the `key`, `cek`, and `iv`.
633
+ # @see JOSE::JWE.block_encrypt
634
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
635
+ # @param [String, [String, String]] block
636
+ # @param [String] cek
637
+ # @param [String] iv
638
+ # @return [JOSE::EncryptedMap]
119
639
  def block_encrypt(key, block, cek = nil, iv = nil)
120
- cek ||= next_cek(key)
121
- iv ||= next_iv
640
+ jwe = self
641
+ if cek.nil?
642
+ cek, jwe = next_cek(key)
643
+ end
644
+ iv ||= jwe.next_iv
122
645
  aad, plain_text = block
123
646
  if plain_text.nil?
124
647
  plain_text = aad
125
648
  aad = nil
126
649
  end
127
- encrypted_key, jwe = key_encrypt(key, cek)
650
+ encrypted_key, jwe = jwe.key_encrypt(key, cek)
128
651
  protected_binary = JOSE.urlsafe_encode64(jwe.to_binary)
129
652
  if aad.nil?
130
653
  cipher_text, cipher_tag = enc.block_encrypt([protected_binary, jwe.compress(plain_text)], cek, iv)
@@ -150,6 +673,21 @@ module JOSE
150
673
  end
151
674
  end
152
675
 
676
+ # Compacts an expanded encrypted map into a binary.
677
+ #
678
+ # !!!ruby
679
+ # JOSE::JWE.compact({
680
+ # "ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA",
681
+ # "encrypted_key" => "",
682
+ # "iv" => "jBt5tTa1Q0N3uFPEkf30MQ",
683
+ # "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
684
+ # "tag" => "gMWOAmhZSq9ksHCZm6VSoA"
685
+ # })
686
+ # # => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA"
687
+ #
688
+ # @see JOSE::JWE.expand
689
+ # @param [JOSE::EncryptedMap, JOSE::Map, Hash] map
690
+ # @return [JOSE::EncryptedBinary]
153
691
  def self.compact(map)
154
692
  if map.is_a?(Hash) or map.is_a?(JOSE::Map)
155
693
  if map.has_key?('aad')
@@ -171,6 +709,23 @@ module JOSE
171
709
  end
172
710
  end
173
711
 
712
+ # Compresses the `plain_text` using the `"zip"` algorithm specified by the `jwe`.
713
+ #
714
+ # !!!ruby
715
+ # JOSE::JWE.compress("{}", { "alg" => "dir", "zip" => "DEF" })
716
+ # # => "x\x9C\xAB\xAE\x05\x00\x01u\x00\xF9"
717
+ #
718
+ # @param [String] plain_text
719
+ # @param [JOSE::JWE] jwe
720
+ # @return [String]
721
+ def self.compress(plain_text, jwe)
722
+ return from(jwe).compress(plain_text)
723
+ end
724
+
725
+ # Compresses the `plain_text` using the `"zip"` algorithm specified by the `jwe`.
726
+ # @see JOSE::JWE.compress
727
+ # @param [String] plain_text
728
+ # @return [String]
174
729
  def compress(plain_text)
175
730
  if zip.nil?
176
731
  return plain_text
@@ -179,6 +734,20 @@ module JOSE
179
734
  end
180
735
  end
181
736
 
737
+ # Expands a compacted encrypted binary into a map.
738
+ #
739
+ # !!!ruby
740
+ # JOSE::JWE.expand("eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..jBt5tTa1Q0N3uFPEkf30MQ.Ei49MvTLLje7bsZ5EZCZMA.gMWOAmhZSq9ksHCZm6VSoA")
741
+ # # => JOSE::EncryptedMap[
742
+ # # "tag" => "gMWOAmhZSq9ksHCZm6VSoA",
743
+ # # "ciphertext" => "Ei49MvTLLje7bsZ5EZCZMA",
744
+ # # "encrypted_key" => "",
745
+ # # "iv" => "jBt5tTa1Q0N3uFPEkf30MQ",
746
+ # # "protected" => "eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0"]
747
+ #
748
+ # @see JOSE::JWE.compact
749
+ # @param [JOSE::EncryptedBinary, String] binary
750
+ # @return [JOSE::EncryptedMap]
182
751
  def self.expand(binary)
183
752
  if binary.is_a?(String)
184
753
  parts = binary.split('.')
@@ -199,18 +768,100 @@ module JOSE
199
768
  end
200
769
  end
201
770
 
202
- def self.generate_key(object, modules = {})
203
- return from(object, modules).generate_key
771
+ # Generates a new {JOSE::JWK JOSE::JWK} based on the algorithms of the specified {JOSE::JWE JOSE::JWE}.
772
+ #
773
+ # !!!ruby
774
+ # JOSE::JWE.generate_key({"alg" => "dir", "enc" => "A128GCM"})
775
+ # # => #<struct JOSE::JWK
776
+ # # keys=nil,
777
+ # # kty=#<struct JOSE::JWK::KTY_oct oct="\xED4\x19\x14\xA1\xDB\xB5\xCF*\xAE\xEE7\xDA\xDE\xA9\xCB">,
778
+ # # fields=JOSE::Map["alg" => "dir", "enc" => "A128GCM", "use" => "enc"]>
779
+ #
780
+ # @param [JOSE::Map, Hash, String, JOSE::JWE, Array<JOSE::Map, Hash, String, JOSE::JWE>] jwe
781
+ # @param [Hash] modules
782
+ # @return [JOSE::JWK, Array<JOSE::JWK>]
783
+ def self.generate_key(jwe, modules = {})
784
+ if jwe.is_a?(Array)
785
+ return from(jwe, modules).map { |obj| obj.generate_key }
786
+ else
787
+ return from(jwe, modules).generate_key
788
+ end
204
789
  end
205
790
 
791
+ # Generates a new {JOSE::JWK JOSE::JWK} based on the algorithms of the specified {JOSE::JWE JOSE::JWE}.
792
+ #
793
+ # @see JOSE::JWE.generate_key
794
+ # @return [JOSE::JWK]
206
795
  def generate_key
207
796
  return alg.generate_key(fields, enc)
208
797
  end
209
798
 
799
+ # Decrypts the `encrypted_key` using the `key` and the `"alg"` and `"enc"` specified by the `jwe`.
800
+ #
801
+ # !!!ruby
802
+ # # let's define our jwk and encrypted_key
803
+ # jwk = JOSE::JWK.from({"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"})
804
+ # enc = [27,123,126,121,56,105,105,81,140,76,30,2,14,92,231,174,203,196,110,204,57,238,248,73].pack('C*')
805
+ #
806
+ # JOSE::JWE.key_decrypt(jwk, enc, { "alg" => "A128KW", "enc" => "A128CBC-HS256" })
807
+ # # => "\x86R\x0F\xB0\xB5s\xAD\x13\r,\xBD\xB9\xBB}\x1C\xF0"
808
+ #
809
+ # @see JOSE::JWE.key_encrypt
810
+ # @param [JOSE::JWK, [JOSE::JWK]] key
811
+ # @param [String] encrypted_key
812
+ # @param [JOSE::JWE] jwe
813
+ # @return [String]
814
+ def self.key_decrypt(key, encrypted_key, jwe)
815
+ return from(jwe).key_decrypt(key, encrypted_key)
816
+ end
817
+
818
+ # Decrypts the `encrypted_key` using the `key` and the `"alg"` and `"enc"` specified by the `jwe`.
819
+ #
820
+ # @param [JOSE::JWK, [JOSE::JWK]] key
821
+ # @param [String] encrypted_key
822
+ # @return [String]
210
823
  def key_decrypt(key, encrypted_key)
211
824
  return alg.key_decrypt(key, enc, encrypted_key)
212
825
  end
213
826
 
827
+ # Encrypts the `decrypted_key` using the `key` and the `"alg"` and `"enc"` specified by the `jwe`.
828
+ #
829
+ # !!!ruby
830
+ # # let's define our jwk and encrypted_key
831
+ # jwk = JOSE::JWK.from({"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"})
832
+ # cek = [134,82,15,176,181,115,173,19,13,44,189,185,187,125,28,240].pack('C*')
833
+ #
834
+ # JOSE::JWE.key_encrypt(jwk, cek, { "alg" => "A128KW", "enc" => "A128CBC-HS256" })
835
+ # # => ["\e{~y8iiQ\x8CL\x1E\x02\x0E\\\xE7\xAE\xCB\xC4n\xCC9\xEE\xF8I",
836
+ # # #<struct JOSE::JWE
837
+ # # alg=#<struct JOSE::JWE::ALG_AES_KW bits=128>,
838
+ # # enc=
839
+ # # #<struct JOSE::JWE::ENC_AES_CBC_HMAC
840
+ # # cipher_name="aes-128-cbc",
841
+ # # bits=256,
842
+ # # cek_len=32,
843
+ # # iv_len=16,
844
+ # # enc_len=16,
845
+ # # mac_len=16,
846
+ # # tag_len=16,
847
+ # # hmac=OpenSSL::Digest::SHA256>,
848
+ # # zip=nil,
849
+ # # fields=JOSE::Map[]>]
850
+ #
851
+ # @see JOSE::JWE.key_decrypt
852
+ # @param [JOSE::JWK, [JOSE::JWK]] key
853
+ # @param [String] decrypted_key
854
+ # @param [JOSE::JWE] jwe
855
+ # @return [[String, JOSE::JWE]]
856
+ def self.key_encrypt(key, decrypted_key, jwe)
857
+ return from(jwe).key_encrypt(key, decrypted_key)
858
+ end
859
+
860
+ # Encrypts the `decrypted_key` using the `key` and the `"alg"` and `"enc"` specified by the `jwe`.
861
+ #
862
+ # @param [JOSE::JWK, [JOSE::JWK]] key
863
+ # @param [String] decrypted_key
864
+ # @return [[String, JOSE::JWE]]
214
865
  def key_encrypt(key, decrypted_key)
215
866
  encrypted_key, new_alg = alg.key_encrypt(key, enc, decrypted_key)
216
867
  new_jwe = JOSE::JWE.from_map(to_map)
@@ -218,10 +869,17 @@ module JOSE
218
869
  return encrypted_key, new_jwe
219
870
  end
220
871
 
872
+ # Merges map on right into map on left.
873
+ # @param [JOSE::Map, Hash, String, JOSE::JWE] left
874
+ # @param [JOSE::Map, Hash, String, JOSE::JWE] right
875
+ # @return [JOSE::JWE]
221
876
  def self.merge(left, right)
222
877
  return from(left).merge(right)
223
878
  end
224
879
 
880
+ # Merges object into current map.
881
+ # @param [JOSE::Map, Hash, String, JOSE::JWE] object
882
+ # @return [JOSE::JWE]
225
883
  def merge(object)
226
884
  object = case object
227
885
  when JOSE::Map, Hash
@@ -236,14 +894,131 @@ module JOSE
236
894
  return JOSE::JWE.from_map(self.to_map.merge(object))
237
895
  end
238
896
 
897
+ # Returns the next `cek` using the `jwk` and the `"alg"` and `"enc"` specified by the `jwe`.
898
+ #
899
+ # !!!ruby
900
+ # # let's define our jwk
901
+ # jwk = JOSE::JWK.from({"k" => "idN_YyeYZqEE7BkpexhA2Q", "kty" => "oct"}) # JOSE::JWK.generate_key([:oct, 16])
902
+ #
903
+ # JOSE::JWE.next_cek(jwk, { "alg" => "A128KW", "enc" => "A128CBC-HS256" })
904
+ # # => ["%S\x8B\xA5,\x17\xA3\xBA\xFF\x9B\xB7\x11\xDC\xD3P\xF7\xEF\x95\xC25\x86)\xFE\xB0\x00\xF7B&\xD9\xFCR\xE9",
905
+ # # #<struct JOSE::JWE
906
+ # # alg=#<struct JOSE::JWE::ALG_AES_KW bits=128>,
907
+ # # enc=
908
+ # # #<struct JOSE::JWE::ENC_AES_CBC_HMAC
909
+ # # cipher_name="aes-128-cbc",
910
+ # # bits=256,
911
+ # # cek_len=32,
912
+ # # iv_len=16,
913
+ # # enc_len=16,
914
+ # # mac_len=16,
915
+ # # tag_len=16,
916
+ # # hmac=OpenSSL::Digest::SHA256>,
917
+ # # zip=nil,
918
+ # # fields=JOSE::Map[]>]
919
+ #
920
+ # # when using the "dir" algorithm, the jwk itself will be used
921
+ # JOSE::JWE.next_cek(jwk, { "alg" => "dir", "enc" => "A128GCM" })
922
+ # # => ["\x89\xD3\x7Fc'\x98f\xA1\x04\xEC\x19){\x18@\xD9",
923
+ # # #<struct JOSE::JWE
924
+ # # alg=#<struct JOSE::JWE::ALG_dir direct=true>,
925
+ # # enc=#<struct JOSE::JWE::ENC_AES_GCM cipher_name="aes-128-gcm", bits=128, cek_len=16, iv_len=12>,
926
+ # # zip=nil,
927
+ # # fields=JOSE::Map[]>]
928
+ #
929
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
930
+ # @param [JOSE::JWE] jwe
931
+ # @return [[String, JOSE::JWE]]
932
+ def self.next_cek(key, jwe)
933
+ return from(jwe).next_cek(key)
934
+ end
935
+
936
+ # Returns the next `cek` using the `jwk` and the `"alg"` and `"enc"` specified by the `jwe`.
937
+ #
938
+ # @param [JOSE::JWK, [JOSE::JWK, JOSE::JWK]] key
939
+ # @return [[String, JOSE::JWE]]
239
940
  def next_cek(key)
240
- return alg.next_cek(key, enc)
941
+ decrypted_key, new_alg = alg.next_cek(key, enc)
942
+ new_jwe = JOSE::JWE.from_map(to_map)
943
+ new_jwe.alg = new_alg
944
+ return decrypted_key, new_jwe
945
+ end
946
+
947
+ # Returns the next `iv` the `"alg"` and `"enc"` specified by the `jwe`.
948
+ #
949
+ # !!!ruby
950
+ # # typically just returns random bytes for the specified "enc" algorithm
951
+ # JOSE::JWE.next_iv({ "alg" => "dir", "enc" => "A128CBC-HS256" }).bytesize * 8
952
+ # # => 128
953
+ # JOSE::JWE.next_iv({ "alg" => "dir", "enc" => "A128GCM" }).bytesize * 8
954
+ # # => 96
955
+ #
956
+ # @param [JOSE::JWE] jwe
957
+ # @return [String]
958
+ def self.next_iv(jwe)
959
+ return from(jwe).next_iv
241
960
  end
242
961
 
962
+ # Returns the next `iv` the `"alg"` and `"enc"` specified by the `jwe`.
963
+ #
964
+ # @return [String]
243
965
  def next_iv
244
966
  return enc.next_iv
245
967
  end
246
968
 
969
+ # Returns the decoded ciphertext portion of a encrypted binary or map without decrypting the ciphertext.
970
+ #
971
+ # !!!ruby
972
+ # JOSE::JWE.peek_ciphertext("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
973
+ # # => "&a"
974
+ #
975
+ # @param [JOSE::EncryptedBinary, String] encrypted
976
+ # @return [String]
977
+ def self.peek_ciphertext(encrypted)
978
+ if encrypted.is_a?(String)
979
+ encrypted = expand(encrypted)
980
+ end
981
+ return JOSE.urlsafe_decode64(encrypted['ciphertext'])
982
+ end
983
+
984
+ # Returns the decoded encrypted key portion of a encrypted binary or map without decrypting the ciphertext.
985
+ #
986
+ # !!!ruby
987
+ # JOSE::JWE.peek_encrypted_key("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
988
+ # # => "\xB7\x8F\xC5o\x89\x02\x97\xA0\\KW\x17\x9D\x1E\x0F\xE1x&\xFA<\x156\xC1e"
989
+ #
990
+ # @param [JOSE::EncryptedBinary, String] encrypted
991
+ # @return [String]
992
+ def self.peek_encrypted_key(encrypted)
993
+ if encrypted.is_a?(String)
994
+ encrypted = expand(encrypted)
995
+ end
996
+ return JOSE.urlsafe_decode64(encrypted['encrypted_key'])
997
+ end
998
+
999
+ # Returns the decoded initialization vector portion of a encrypted binary or map without decrypting the ciphertext.
1000
+ #
1001
+ # !!!ruby
1002
+ # JOSE::JWE.peek_iv("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
1003
+ # # => "E\xEA\xCA}h\xF3\xAA\xABK!J\xF3"
1004
+ #
1005
+ # @param [JOSE::EncryptedBinary, String] encrypted
1006
+ # @return [String]
1007
+ def self.peek_iv(encrypted)
1008
+ if encrypted.is_a?(String)
1009
+ encrypted = expand(encrypted)
1010
+ end
1011
+ return JOSE.urlsafe_decode64(encrypted['iv'])
1012
+ end
1013
+
1014
+ # Returns the decoded protected portion of a encrypted binary or map without decrypting the ciphertext.
1015
+ #
1016
+ # !!!ruby
1017
+ # JOSE::JWE.peek_protected("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
1018
+ # # => JOSE::Map["enc" => "A128GCM", "alg" => "A128KW"]
1019
+ #
1020
+ # @param [JOSE::EncryptedBinary, String] encrypted
1021
+ # @return [JOSE::Map]
247
1022
  def self.peek_protected(encrypted)
248
1023
  if encrypted.is_a?(String)
249
1024
  encrypted = expand(encrypted)
@@ -251,6 +1026,39 @@ module JOSE
251
1026
  return JOSE::Map.new(JOSE.decode(JOSE.urlsafe_decode64(encrypted['protected'])))
252
1027
  end
253
1028
 
1029
+ # Returns the decoded tag portion of a encrypted binary or map without decrypting the ciphertext.
1030
+ #
1031
+ # !!!ruby
1032
+ # JOSE::JWE.peek_tag("eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.t4_Fb4kCl6BcS1cXnR4P4Xgm-jwVNsFl.RerKfWjzqqtLIUrz.JmE.ZDpVlWo-aQYM5la9eshwWw")
1033
+ # # => "d:U\x95j>i\x06\f\xE6V\xBDz\xC8p["
1034
+ #
1035
+ # @param [JOSE::EncryptedBinary, String] encrypted
1036
+ # @return [String]
1037
+ def self.peek_tag(encrypted)
1038
+ if encrypted.is_a?(String)
1039
+ encrypted = expand(encrypted)
1040
+ end
1041
+ return JOSE.urlsafe_decode64(encrypted['tag'])
1042
+ end
1043
+
1044
+ # Uncompresses the `cipher_text` using the `"zip"` algorithm specified by the `jwe`.
1045
+ #
1046
+ # !!!ruby
1047
+ # JOSE::JWE.uncompress([120,156,171,174,5,0,1,117,0,249].pack('C*'), { "alg" => "dir", "zip" => "DEF" })
1048
+ # # => "{}"
1049
+ #
1050
+ # @see JOSE::JWE.compress
1051
+ # @param [String] cipher_text
1052
+ # @param [JOSE::JWE] jwe
1053
+ # @return [String]
1054
+ def self.uncompress(cipher_text, jwe)
1055
+ return from(jwe).uncompress(cipher_text)
1056
+ end
1057
+
1058
+ # Uncompresses the `cipher_text` using the `"zip"` algorithm specified by the `jwe`.
1059
+ #
1060
+ # @param [String] cipher_text
1061
+ # @return [String]
254
1062
  def uncompress(cipher_text)
255
1063
  if zip.nil?
256
1064
  return cipher_text