jose 0.3.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|