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.
- checksums.yaml +4 -4
- data/.travis.yml +5 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +7 -0
- data/{LICENSE.txt → LICENSE.md} +0 -0
- data/README.md +10 -3
- data/docs/EncryptionAlgorithms.md +55 -0
- data/docs/GettingStarted.md +137 -0
- data/docs/KeyGeneration.md +263 -0
- data/docs/SignatureAlgorithms.md +35 -0
- data/lib/jose.rb +86 -22
- data/lib/jose/jwa.rb +293 -0
- data/lib/jose/jwa/concat_kdf.rb +2 -0
- data/lib/jose/jwa/curve25519_ruby.rb +1 -1
- data/lib/jose/jwa/curve448_ruby.rb +1 -1
- data/lib/jose/jwa/ed448.rb +8 -8
- data/lib/jose/jwe.rb +819 -11
- data/lib/jose/jwe/alg_aes_gcm_kw.rb +1 -1
- data/lib/jose/jwe/alg_aes_kw.rb +1 -1
- data/lib/jose/jwe/alg_dir.rb +4 -4
- data/lib/jose/jwe/alg_ecdh_es.rb +45 -18
- data/lib/jose/jwe/alg_pbes2.rb +3 -3
- data/lib/jose/jwe/alg_rsa.rb +1 -1
- data/lib/jose/jwk.rb +639 -48
- data/lib/jose/jwk/kty_ec.rb +22 -4
- data/lib/jose/jwk/kty_oct.rb +16 -0
- data/lib/jose/jwk/kty_okp_ed25519.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448.rb +36 -0
- data/lib/jose/jwk/kty_okp_ed448ph.rb +36 -0
- data/lib/jose/jwk/kty_okp_x25519.rb +28 -0
- data/lib/jose/jwk/kty_okp_x448.rb +28 -0
- data/lib/jose/jwk/kty_rsa.rb +8 -0
- data/lib/jose/jwk/openssh_key.rb +278 -0
- data/lib/jose/jws.rb +486 -21
- data/lib/jose/jws/alg_none.rb +2 -2
- data/lib/jose/jwt.rb +208 -14
- data/lib/jose/version.rb +1 -1
- metadata +9 -3
data/lib/jose/jwa/concat_kdf.rb
CHANGED
@@ -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 = [
|
data/lib/jose/jwa/ed448.rb
CHANGED
@@ -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('
|
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('
|
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('
|
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('
|
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('
|
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('
|
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('
|
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('
|
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
|
data/lib/jose/jwe.rb
CHANGED
@@ -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,
|
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
|
-
|
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
|
-
|
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
|
-
|
121
|
-
|
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
|
-
|
203
|
-
|
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
|
-
|
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
|