jose 1.1.1 → 1.1.2
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/CHANGELOG.md +9 -0
- data/docs/SignatureAlgorithms.md +1 -0
- data/lib/jose/jwa.rb +2 -0
- data/lib/jose/jwe/zip_def.rb +11 -3
- data/lib/jose/jwk/kty_okp_ed25519.rb +6 -6
- data/lib/jose/jwk/kty_okp_ed25519ph.rb +4 -4
- data/lib/jose/jwk/kty_okp_ed448.rb +6 -6
- data/lib/jose/jwk/kty_okp_ed448ph.rb +6 -6
- data/lib/jose/jwk/kty_rsa.rb +65 -1
- data/lib/jose/jws.rb +20 -1
- data/lib/jose/jws/alg_eddsa.rb +4 -1
- data/lib/jose/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aa018b696c9f264acc7691bb2ae92a7e48ec95e7
|
4
|
+
data.tar.gz: c07468825b96248e7e15376711e573913f0d33ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 41a28230b46f5e9ea6ef56c7c8bdc2f353f7472f18faea6923d39326eb8d87f77a3ae247d22181ef5a4babe550cab1db31361e657d066959e8629a6df87c8e6d
|
7
|
+
data.tar.gz: 00c55722c8dc85d65c53a7628abdd9baa4c536d409249dd5de4487a7b3d8e34254035151526cbedf520809401153604f7c301705274d70d02fbc05fe1665a4e2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.1.2 (2016-07-07)
|
4
|
+
|
5
|
+
* Enhancements
|
6
|
+
* Improved handling of RSA private keys in SMF (Straightforward Method) form to CRT (Chinese Remainder Theorem) form, see [potatosalad/erlang-jose#19](https://github.com/potatosalad/erlang-jose/issues/19) This is especially useful for keys produced by Java programs using the `RSAPrivateKeySpec` API as mentioned in [Section 9.3 of RFC 7517](https://tools.ietf.org/html/rfc7517#section-9.3).
|
7
|
+
* Updated EdDSA operations to comply with draft 04 of [draft-ietf-jose-cfrg-curves-04](https://tools.ietf.org/html/draft-ietf-jose-cfrg-curves-04).
|
8
|
+
|
9
|
+
* Fixes
|
10
|
+
* Fixed compression encoding bug for `{"zip":"DEF"}` operations (thanks to [@amadden734](https://github.com/amadden734) see [#3](https://github.com/potatosalad/ruby-jose/pull/3))
|
11
|
+
|
3
12
|
## 1.1.1 (2016-05-27)
|
4
13
|
|
5
14
|
* Enhancements
|
data/docs/SignatureAlgorithms.md
CHANGED
@@ -21,6 +21,7 @@ Here are the supported options for the `alg` parameter, grouped by similar funci
|
|
21
21
|
- [`Ed25519ph`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-25519-group)
|
22
22
|
- [`Ed448`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-448-group)
|
23
23
|
- [`Ed448ph`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-448-group)
|
24
|
+
- [`EdDSA`](http://www.rubydoc.info/gems/jose/JOSE/JWS#EdDSA-group)
|
24
25
|
- HMAC using SHA-2
|
25
26
|
- [`HS256`](http://www.rubydoc.info/gems/jose/JOSE/JWS#HMACSHA2-group)
|
26
27
|
- [`HS384`](http://www.rubydoc.info/gems/jose/JOSE/JWS#HMACSHA2-group)
|
data/lib/jose/jwa.rb
CHANGED
@@ -104,6 +104,7 @@ module JOSE
|
|
104
104
|
# # "Ed25519ph",
|
105
105
|
# # "Ed448",
|
106
106
|
# # "Ed448ph",
|
107
|
+
# # "EdDSA",
|
107
108
|
# # "ES256",
|
108
109
|
# # "ES384",
|
109
110
|
# # "ES512",
|
@@ -161,6 +162,7 @@ module JOSE
|
|
161
162
|
'Ed25519ph',
|
162
163
|
'Ed448',
|
163
164
|
'Ed448ph',
|
165
|
+
'EdDSA',
|
164
166
|
'ES256',
|
165
167
|
'ES384',
|
166
168
|
'ES512',
|
data/lib/jose/jwe/zip_def.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
class JOSE::JWE::ZIP_DEF
|
1
|
+
class JOSE::JWE::ZIP_DEF < Struct.new(nil)
|
2
2
|
|
3
3
|
# JOSE::JWE callbacks
|
4
4
|
|
@@ -18,11 +18,19 @@ class JOSE::JWE::ZIP_DEF
|
|
18
18
|
# JOSE::JWE::ZIP callbacks
|
19
19
|
|
20
20
|
def compress(plain_text)
|
21
|
-
|
21
|
+
zstream = Zlib::Deflate.new(nil, -Zlib::MAX_WBITS)
|
22
|
+
buf = zstream.deflate(plain_text, Zlib::FINISH)
|
23
|
+
zstream.finish
|
24
|
+
zstream.close
|
25
|
+
return buf
|
22
26
|
end
|
23
27
|
|
24
28
|
def uncompress(cipher_text)
|
25
|
-
|
29
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
30
|
+
buf = zstream.inflate(cipher_text)
|
31
|
+
zstream.finish
|
32
|
+
zstream.close
|
33
|
+
return buf
|
26
34
|
end
|
27
35
|
|
28
36
|
end
|
@@ -78,8 +78,8 @@ class JOSE::JWK::KTY_OKP_Ed25519 < Struct.new(:okp)
|
|
78
78
|
return JOSE::JWK::KTY.key_encryptor(self, fields, key)
|
79
79
|
end
|
80
80
|
|
81
|
-
def sign(message,
|
82
|
-
raise ArgumentError, "'
|
81
|
+
def sign(message, sign_type)
|
82
|
+
raise ArgumentError, "'sign_type' must be :Ed25519 or :EdDSA" if sign_type != :Ed25519 and sign_type != :EdDSA
|
83
83
|
raise NotImplementedError, "Ed25519 public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
84
84
|
return JOSE::JWA::Curve25519.ed25519_sign(message, okp)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ class JOSE::JWK::KTY_OKP_Ed25519 < Struct.new(:okp)
|
|
88
88
|
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
89
89
|
return JOSE::Map['alg' => fields['alg']]
|
90
90
|
elsif okp.bytesize == SK_BYTES
|
91
|
-
return JOSE::Map['alg' => '
|
91
|
+
return JOSE::Map['alg' => 'EdDSA']
|
92
92
|
else
|
93
93
|
raise ArgumentError, "signing not supported for public keys"
|
94
94
|
end
|
@@ -98,12 +98,12 @@ class JOSE::JWK::KTY_OKP_Ed25519 < Struct.new(:okp)
|
|
98
98
|
if fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
99
99
|
return [fields['alg']]
|
100
100
|
else
|
101
|
-
return ['Ed25519']
|
101
|
+
return ['Ed25519', 'EdDSA']
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def verify(message,
|
106
|
-
raise ArgumentError, "'
|
105
|
+
def verify(message, sign_type, signature)
|
106
|
+
raise ArgumentError, "'sign_type' must be :Ed25519 or :EdDSA" if sign_type != :Ed25519 and sign_type != :EdDSA
|
107
107
|
pk = okp
|
108
108
|
pk = JOSE::JWA::Curve25519.ed25519_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
109
109
|
return JOSE::JWA::Curve25519.ed25519_verify(signature, message, pk)
|
@@ -79,7 +79,7 @@ class JOSE::JWK::KTY_OKP_Ed25519ph < Struct.new(:okp)
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def sign(message, sign_type)
|
82
|
-
raise ArgumentError, "'sign_type' must be :Ed25519ph" if sign_type != :Ed25519ph
|
82
|
+
raise ArgumentError, "'sign_type' must be :Ed25519ph or :EdDSA" if sign_type != :Ed25519ph and sign_type != :EdDSA
|
83
83
|
raise NotImplementedError, "Ed25519ph public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
84
84
|
return JOSE::JWA::Curve25519.ed25519ph_sign(message, okp)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ class JOSE::JWK::KTY_OKP_Ed25519ph < Struct.new(:okp)
|
|
88
88
|
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
89
89
|
return JOSE::Map['alg' => fields['alg']]
|
90
90
|
elsif okp.bytesize == SK_BYTES
|
91
|
-
return JOSE::Map['alg' => '
|
91
|
+
return JOSE::Map['alg' => 'EdDSA']
|
92
92
|
else
|
93
93
|
raise ArgumentError, "signing not supported for public keys"
|
94
94
|
end
|
@@ -98,12 +98,12 @@ class JOSE::JWK::KTY_OKP_Ed25519ph < Struct.new(:okp)
|
|
98
98
|
if fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
99
99
|
return [fields['alg']]
|
100
100
|
else
|
101
|
-
return ['Ed25519ph']
|
101
|
+
return ['Ed25519ph', 'EdDSA']
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
105
|
def verify(message, sign_type, signature)
|
106
|
-
raise ArgumentError, "'sign_type' must be :Ed25519ph" if sign_type != :Ed25519ph
|
106
|
+
raise ArgumentError, "'sign_type' must be :Ed25519ph or :EdDSA" if sign_type != :Ed25519ph and sign_type != :EdDSA
|
107
107
|
pk = okp
|
108
108
|
pk = JOSE::JWA::Curve25519.ed25519ph_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
109
109
|
return JOSE::JWA::Curve25519.ed25519ph_verify(signature, message, pk)
|
@@ -78,8 +78,8 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
78
78
|
return JOSE::JWK::KTY.key_encryptor(self, fields, key)
|
79
79
|
end
|
80
80
|
|
81
|
-
def sign(message,
|
82
|
-
raise ArgumentError, "'
|
81
|
+
def sign(message, sign_type)
|
82
|
+
raise ArgumentError, "'sign_type' must be :Ed448 or :EdDSA" if sign_type != :Ed448 and sign_type != :EdDSA
|
83
83
|
raise NotImplementedError, "Ed448 public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
84
84
|
return JOSE::JWA::Curve448.ed448_sign(message, okp)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
88
88
|
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
89
89
|
return JOSE::Map['alg' => fields['alg']]
|
90
90
|
elsif okp.bytesize == SK_BYTES
|
91
|
-
return JOSE::Map['alg' => '
|
91
|
+
return JOSE::Map['alg' => 'EdDSA']
|
92
92
|
else
|
93
93
|
raise ArgumentError, "signing not supported for public keys"
|
94
94
|
end
|
@@ -98,12 +98,12 @@ class JOSE::JWK::KTY_OKP_Ed448 < Struct.new(:okp)
|
|
98
98
|
if fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
99
99
|
return [fields['alg']]
|
100
100
|
else
|
101
|
-
return ['Ed448']
|
101
|
+
return ['Ed448', 'EdDSA']
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def verify(message,
|
106
|
-
raise ArgumentError, "'
|
105
|
+
def verify(message, sign_type, signature)
|
106
|
+
raise ArgumentError, "'sign_type' must be :Ed448 or :EdDSA" if sign_type != :Ed448 and sign_type != :EdDSA
|
107
107
|
pk = okp
|
108
108
|
pk = JOSE::JWA::Curve448.ed448_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
109
109
|
return JOSE::JWA::Curve448.ed448_verify(signature, message, pk)
|
@@ -78,8 +78,8 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
78
78
|
return JOSE::JWK::KTY.key_encryptor(self, fields, key)
|
79
79
|
end
|
80
80
|
|
81
|
-
def sign(message,
|
82
|
-
raise ArgumentError, "'
|
81
|
+
def sign(message, sign_type)
|
82
|
+
raise ArgumentError, "'sign_type' must be :Ed448ph or :EdDSA" if sign_type != :Ed448ph and sign_type != :EdDSA
|
83
83
|
raise NotImplementedError, "Ed448ph public key cannot be used for signing" if okp.bytesize != SK_BYTES
|
84
84
|
return JOSE::JWA::Curve448.ed448ph_sign(message, okp)
|
85
85
|
end
|
@@ -88,7 +88,7 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
88
88
|
if okp.bytesize == SK_BYTES and fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
89
89
|
return JOSE::Map['alg' => fields['alg']]
|
90
90
|
elsif okp.bytesize == SK_BYTES
|
91
|
-
return JOSE::Map['alg' => '
|
91
|
+
return JOSE::Map['alg' => 'EdDSA']
|
92
92
|
else
|
93
93
|
raise ArgumentError, "signing not supported for public keys"
|
94
94
|
end
|
@@ -98,12 +98,12 @@ class JOSE::JWK::KTY_OKP_Ed448ph < Struct.new(:okp)
|
|
98
98
|
if fields and fields['use'] == 'sig' and not fields['alg'].nil?
|
99
99
|
return [fields['alg']]
|
100
100
|
else
|
101
|
-
return ['Ed448ph']
|
101
|
+
return ['Ed448ph', 'EdDSA']
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def verify(message,
|
106
|
-
raise ArgumentError, "'
|
105
|
+
def verify(message, sign_type, signature)
|
106
|
+
raise ArgumentError, "'sign_type' must be :Ed448ph or :EdDSA" if sign_type != :Ed448ph and sign_type != :EdDSA
|
107
107
|
pk = okp
|
108
108
|
pk = JOSE::JWA::Curve448.ed448ph_secret_to_public(okp) if okp.bytesize == SK_BYTES
|
109
109
|
return JOSE::JWA::Curve448.ed448ph_verify(signature, message, pk)
|
data/lib/jose/jwk/kty_rsa.rb
CHANGED
@@ -19,7 +19,11 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
19
19
|
rsa.iqmp = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['qi']), 2)
|
20
20
|
return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
|
21
21
|
else
|
22
|
-
|
22
|
+
d = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['d']), 2)
|
23
|
+
e = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['e']), 2)
|
24
|
+
n = OpenSSL::BN.new(JOSE.urlsafe_decode64(fields['n']), 2)
|
25
|
+
rsa = convert_sfm_to_crt(d, e, n)
|
26
|
+
return JOSE::JWK::KTY_RSA.new(JOSE::JWK::PKeyProxy.new(rsa)), fields.except('kty', 'd', 'dp', 'dq', 'e', 'n', 'p', 'q', 'qi')
|
23
27
|
end
|
24
28
|
else
|
25
29
|
rsa = OpenSSL::PKey::RSA.new
|
@@ -200,4 +204,64 @@ class JOSE::JWK::KTY_RSA < Struct.new(:key)
|
|
200
204
|
return JOSE::JWK::PEM.to_binary(key, password)
|
201
205
|
end
|
202
206
|
|
207
|
+
# Internal functions
|
208
|
+
|
209
|
+
private
|
210
|
+
def self.convert_sfm_to_crt(d, e, n)
|
211
|
+
ktot = d * e - 1
|
212
|
+
t = convert_sfm_to_crt_find_t(ktot)
|
213
|
+
a = 2.to_bn
|
214
|
+
k = t
|
215
|
+
p = convert_sfm_to_crt_find_p(ktot, t, k, a, n, d)
|
216
|
+
q, = n / p
|
217
|
+
if q > p
|
218
|
+
p, q = q, p
|
219
|
+
end
|
220
|
+
dp = d % (p - 1)
|
221
|
+
dq = d % (q - 1)
|
222
|
+
qi = q.mod_inverse(p)
|
223
|
+
rsa = OpenSSL::PKey::RSA.new
|
224
|
+
rsa.d = d
|
225
|
+
rsa.dmp1 = dp
|
226
|
+
rsa.dmq1 = dq
|
227
|
+
rsa.e = e
|
228
|
+
rsa.n = n
|
229
|
+
rsa.p = p
|
230
|
+
rsa.q = q
|
231
|
+
rsa.iqmp = qi
|
232
|
+
return rsa
|
233
|
+
end
|
234
|
+
|
235
|
+
def self.convert_sfm_to_crt_find_t(ktot)
|
236
|
+
t = ktot
|
237
|
+
loop do
|
238
|
+
if t > 0 and (t % 2) == 0
|
239
|
+
t, = t / 2
|
240
|
+
else
|
241
|
+
break
|
242
|
+
end
|
243
|
+
end
|
244
|
+
return t
|
245
|
+
end
|
246
|
+
|
247
|
+
def self.convert_sfm_to_crt_find_p(ktot, t, k, a, n, d)
|
248
|
+
p = nil
|
249
|
+
loop do
|
250
|
+
break if not p.nil?
|
251
|
+
loop do
|
252
|
+
break if not p.nil?
|
253
|
+
break if k >= ktot
|
254
|
+
c = a.mod_exp(k, n)
|
255
|
+
if c != 1 and c != (n - 1) and c.mod_exp(2, n) == 1
|
256
|
+
p = (c + 1).gcd(n)
|
257
|
+
else
|
258
|
+
k = k * 2
|
259
|
+
end
|
260
|
+
end
|
261
|
+
break if not p.nil?
|
262
|
+
k = t
|
263
|
+
a = a + 2
|
264
|
+
end
|
265
|
+
return p
|
266
|
+
end
|
203
267
|
end
|
data/lib/jose/jws.rb
CHANGED
@@ -59,6 +59,7 @@ module JOSE
|
|
59
59
|
# * `"Ed25519ph"`
|
60
60
|
# * `"Ed448"`
|
61
61
|
# * `"Ed448ph"`
|
62
|
+
# * `"EdDSA"`
|
62
63
|
# * `"ES256"`
|
63
64
|
# * `"ES384"`
|
64
65
|
# * `"ES512"`
|
@@ -115,6 +116,18 @@ module JOSE
|
|
115
116
|
# JOSE::JWS.verify(jwk_ed448ph, signed_ed448ph).first
|
116
117
|
# # => true
|
117
118
|
#
|
119
|
+
# ### <a name="EdDSA-group">EdDSA</a>
|
120
|
+
#
|
121
|
+
# # EdDSA works with Ed25519, Ed25519ph, Ed448, and Ed448ph keys.
|
122
|
+
# # However, it defaults to Ed25519 for key generation.
|
123
|
+
# jwk_eddsa = JOSE::JWS.generate_key({ "alg" => "EdDSA" })
|
124
|
+
#
|
125
|
+
# # EdDSA
|
126
|
+
# signed_eddsa = JOSE::JWS.sign(jwk_eddsa, "{}", { "alg" => "EdDSA" }).compact
|
127
|
+
# # => "eyJhbGciOiJFZERTQSJ9.e30.rhb5ZY7MllNbW9q-SCn_NglhYtaRGMXEUDj6BvJjltOt19tEI_1wFrVK__jL91i9hO7WtVqRH_OfHiilnO1CAQ"
|
128
|
+
# JOSE::JWS.verify(jwk_eddsa, signed_eddsa).first
|
129
|
+
# # => true
|
130
|
+
#
|
118
131
|
# ### <a name="ECDSA-group">ES256, ES384, and ES512</a>
|
119
132
|
#
|
120
133
|
# !!!ruby
|
@@ -681,7 +694,13 @@ module JOSE
|
|
681
694
|
|
682
695
|
private
|
683
696
|
|
684
|
-
EDDSA_ALG_LIST = [
|
697
|
+
EDDSA_ALG_LIST = [
|
698
|
+
'Ed25519'.freeze,
|
699
|
+
'Ed25519ph'.freeze,
|
700
|
+
'Ed448'.freeze,
|
701
|
+
'Ed448ph'.freeze,
|
702
|
+
'EdDSA'.freeze
|
703
|
+
].freeze
|
685
704
|
|
686
705
|
def self.from_fields(jws, modules)
|
687
706
|
if jws.fields.has_key?('b64')
|
data/lib/jose/jws/alg_eddsa.rb
CHANGED
@@ -12,6 +12,8 @@ class JOSE::JWS::ALG_EDDSA < Struct.new(:sign_type)
|
|
12
12
|
return new(:Ed448), fields.delete('alg')
|
13
13
|
when 'Ed448ph'
|
14
14
|
return new(:Ed448ph), fields.delete('alg')
|
15
|
+
when 'EdDSA'
|
16
|
+
return new(:EdDSA), fields.delete('alg')
|
15
17
|
else
|
16
18
|
raise ArgumentError, "invalid 'alg' for JWS: #{fields['alg'].inspect}"
|
17
19
|
end
|
@@ -25,7 +27,8 @@ class JOSE::JWS::ALG_EDDSA < Struct.new(:sign_type)
|
|
25
27
|
# JOSE::JWS::ALG callbacks
|
26
28
|
|
27
29
|
def generate_key(fields)
|
28
|
-
|
30
|
+
okp_type = sign_type == :EdDSA ? :Ed25519 : sign_type
|
31
|
+
return JOSE::JWS::ALG.generate_key([:okp, okp_type], sign_type.to_s)
|
29
32
|
end
|
30
33
|
|
31
34
|
def sign(jwk, message)
|
data/lib/jose/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jose
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Bennett
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hamster
|