ruby-pgp 0.0.1
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 +7 -0
- data/.gitignore +20 -0
- data/.rbenv-version +1 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +82 -0
- data/Rakefile +12 -0
- data/bin/jrpgp +12 -0
- data/bin/rpgp +12 -0
- data/lib/pgp/cli/runner.rb +78 -0
- data/lib/pgp/cli.rb +160 -0
- data/lib/pgp/decryptor.rb +16 -0
- data/lib/pgp/encryptor.rb +33 -0
- data/lib/pgp/gpg/engine.rb +141 -0
- data/lib/pgp/gpg/runner.rb +189 -0
- data/lib/pgp/gpg/temp_path_helper.rb +25 -0
- data/lib/pgp/keys_importer.rb +11 -0
- data/lib/pgp/log.rb +28 -0
- data/lib/pgp/ruby_decryptor.rb +9 -0
- data/lib/pgp/signer.rb +16 -0
- data/lib/pgp/verifier.rb +14 -0
- data/lib/pgp/version.rb +3 -0
- data/lib/pgp.rb +20 -0
- data/lib/ruby-pgp.rb +1 -0
- data/ruby-pgp.gemspec +24 -0
- data/run_tests.sh +7 -0
- data/spec/helpers/keys_helper.rb +9 -0
- data/spec/helpers/process_helper.rb +13 -0
- data/spec/helpers/temp_helper.rb +28 -0
- data/spec/lib/pgp/cli_spec.rb +41 -0
- data/spec/lib/pgp/decryptor_spec.rb +58 -0
- data/spec/lib/pgp/encryptor_spec.rb +86 -0
- data/spec/lib/pgp/gpg/engine_spec.rb +276 -0
- data/spec/lib/pgp/gpg/runner_integration_spec.rb +38 -0
- data/spec/lib/pgp/gpg/runner_spec.rb +432 -0
- data/spec/lib/pgp/gpg/temp_path_helper_spec.rb +53 -0
- data/spec/lib/pgp/signer_spec.rb +48 -0
- data/spec/lib/pgp/verifier_spec.rb +40 -0
- data/spec/lib/quirks_spec.rb +102 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/fixtures/encrypted_with_passphrase_key.txt +1 -0
- data/spec/support/fixtures/encrypted_with_passphrase_key.txt.asc +13 -0
- data/spec/support/fixtures/private_key.asc +63 -0
- data/spec/support/fixtures/private_key_with_passphrase.asc +59 -0
- data/spec/support/fixtures/public_key.asc +36 -0
- data/spec/support/fixtures/public_key_with_passphrase.asc +30 -0
- data/spec/support/fixtures/signed_file.txt +1 -0
- data/spec/support/fixtures/signed_file.txt.asc +14 -0
- data/spec/support/fixtures/unencrypted_file.txt +1 -0
- data/spec/support/fixtures/unencrypted_file.txt.asc +19 -0
- data/spec/support/fixtures/wrong_private_key_for_signature.asc +63 -0
- data/spec/support/fixtures/wrong_public_key_for_signature.asc +36 -0
- data/spec/verify.sh +124 -0
- metadata +155 -0
@@ -0,0 +1,432 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GPG::Runner do
|
4
|
+
include ProcessHelper
|
5
|
+
|
6
|
+
let(:runner) { GPG::Runner.new }
|
7
|
+
|
8
|
+
describe :version_default do
|
9
|
+
it 'reads gpg version' do
|
10
|
+
setup_process('gpg --version', true, "gpg (GnuPG) 2.0.22\nlibgcrypt 1.5.3\nblah\nblah")
|
11
|
+
expect(runner.version_default).to eq('2.0.22')
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'returns empty when gpg fails' do
|
15
|
+
setup_process('gpg --version', false, nil)
|
16
|
+
expect(runner.version_default).to eq('')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :read_private_key_fingerprints do
|
21
|
+
it 'reads all the private key fingerprints' do
|
22
|
+
fingerprints = [
|
23
|
+
'23AD063A33C2EBE09F9A71ED9539E22A3388EE24',
|
24
|
+
'A99BFCC3B6B952D66AFC1F3C48508D311DD34131'
|
25
|
+
]
|
26
|
+
seeded_output = '''
|
27
|
+
/root/.gnupg/secring.gpg
|
28
|
+
------------------------
|
29
|
+
sec 2048R/3388EE24 2013-03-04
|
30
|
+
Key fingerprint = 23AD 063A 33C2 EBE0 9F9A 71ED 9539 E22A 3388 EE24
|
31
|
+
uid Chris Nelson <superchrisnelson@gmail.com>
|
32
|
+
ssb 2048R/349BAAD3 2013-03-04
|
33
|
+
|
34
|
+
sec 2048R/1DD34131 2012-06-14
|
35
|
+
Key fingerprint = A99B FCC3 B6B9 52D6 6AFC 1F3C 4850 8D31 1DD3 4131
|
36
|
+
uid JRuby BG PGP Bug <foo@bar.com>
|
37
|
+
ssb 2048R/412E5D21 2012-06-14
|
38
|
+
'''
|
39
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', true, seeded_output)
|
40
|
+
|
41
|
+
expect(runner.read_private_key_fingerprints).to eq(fingerprints)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns empty when there are no secret keys' do
|
45
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', true, nil)
|
46
|
+
|
47
|
+
expect(runner.read_private_key_fingerprints).to eq([])
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'returns empty when gpg fails' do
|
51
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', false, nil)
|
52
|
+
|
53
|
+
expect(runner.read_private_key_fingerprints).to eq([])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe :read_public_key_fingerprints do
|
58
|
+
it 'reads all the public key fingerprints' do
|
59
|
+
fingerprints = [
|
60
|
+
'23AD063A33C2EBE09F9A71ED9539E22A3388EE24',
|
61
|
+
'A99BFCC3B6B952D66AFC1F3C48508D311DD34131'
|
62
|
+
]
|
63
|
+
seeded_output = '''
|
64
|
+
/root/.gnupg/pubring.gpg
|
65
|
+
------------------------
|
66
|
+
pub 2048R/3388EE24 2013-03-04
|
67
|
+
Key fingerprint = 23AD 063A 33C2 EBE0 9F9A 71ED 9539 E22A 3388 EE24
|
68
|
+
uid Chris Nelson <superchrisnelson@gmail.com>
|
69
|
+
sub 2048R/349BAAD3 2013-03-04
|
70
|
+
|
71
|
+
pub 2048R/1DD34131 2012-06-14
|
72
|
+
Key fingerprint = A99B FCC3 B6B9 52D6 6AFC 1F3C 4850 8D31 1DD3 4131
|
73
|
+
uid JRuby BG PGP Bug <foo@bar.com>
|
74
|
+
sub 2048R/412E5D21 2012-06-14
|
75
|
+
'''
|
76
|
+
|
77
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', true, seeded_output)
|
78
|
+
|
79
|
+
expect(runner.read_public_key_fingerprints).to eq(fingerprints)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns empty when there are no public keys' do
|
83
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', false, '')
|
84
|
+
|
85
|
+
expect(runner.read_public_key_fingerprints).to eq([])
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'returns empty when gpg fails' do
|
89
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', false, nil)
|
90
|
+
|
91
|
+
expect(runner.read_public_key_fingerprints).to eq([])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe :read_private_key_recipients do
|
96
|
+
it 'reads all the private key recipients' do
|
97
|
+
recipients = [
|
98
|
+
'superchrisnelson@gmail.com',
|
99
|
+
'foo@bar.com'
|
100
|
+
]
|
101
|
+
seeded_output = '''
|
102
|
+
/root/.gnupg/secring.gpg
|
103
|
+
------------------------
|
104
|
+
sec 2048R/3388EE24 2013-03-04
|
105
|
+
Key fingerprint = 23AD 063A 33C2 EBE0 9F9A 71ED 9539 E22A 3388 EE24
|
106
|
+
uid Chris Nelson <superchrisnelson@gmail.com>
|
107
|
+
ssb 2048R/349BAAD3 2013-03-04
|
108
|
+
|
109
|
+
sec 2048R/1DD34131 2012-06-14
|
110
|
+
Key fingerprint = A99B FCC3 B6B9 52D6 6AFC 1F3C 4850 8D31 1DD3 4131
|
111
|
+
uid JRuby BG PGP Bug <foo@bar.com>
|
112
|
+
ssb 2048R/412E5D21 2012-06-14
|
113
|
+
'''
|
114
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', true, seeded_output)
|
115
|
+
|
116
|
+
expect(runner.read_private_key_recipients).to eq(recipients)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns empty when there are no secret keys' do
|
120
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', true, nil)
|
121
|
+
|
122
|
+
expect(runner.read_private_key_recipients).to eq([])
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'returns empty when gpg fails' do
|
126
|
+
setup_process('gpg --quiet --list-secret-keys --fingerprint --keyid-format LONG', false, nil)
|
127
|
+
|
128
|
+
expect(runner.read_private_key_recipients).to eq([])
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe :read_public_key_recipients do
|
133
|
+
it 'reads all the public key recipients' do
|
134
|
+
recipients = [
|
135
|
+
'superchrisnelson@gmail.com',
|
136
|
+
'foo@bar.com'
|
137
|
+
]
|
138
|
+
seeded_output = '''
|
139
|
+
/root/.gnupg/secring.gpg
|
140
|
+
------------------------
|
141
|
+
sec 2048R/3388EE24 2013-03-04
|
142
|
+
Key fingerprint = 23AD 063A 33C2 EBE0 9F9A 71ED 9539 E22A 3388 EE24
|
143
|
+
uid Chris Nelson <superchrisnelson@gmail.com>
|
144
|
+
ssb 2048R/349BAAD3 2013-03-04
|
145
|
+
|
146
|
+
sec 2048R/1DD34131 2012-06-14
|
147
|
+
Key fingerprint = A99B FCC3 B6B9 52D6 6AFC 1F3C 4850 8D31 1DD3 4131
|
148
|
+
uid JRuby BG PGP Bug <foo@bar.com>
|
149
|
+
ssb 2048R/412E5D21 2012-06-14
|
150
|
+
'''
|
151
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', true, seeded_output)
|
152
|
+
|
153
|
+
expect(runner.read_public_key_recipients).to eq(recipients)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'returns empty when there are no secret keys' do
|
157
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', true, nil)
|
158
|
+
|
159
|
+
expect(runner.read_public_key_recipients).to eq([])
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'returns empty when gpg fails' do
|
163
|
+
setup_process('gpg --quiet --list-keys --fingerprint --keyid-format LONG', false, nil)
|
164
|
+
|
165
|
+
expect(runner.read_public_key_recipients).to eq([])
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe :delete_private_key do
|
170
|
+
it 'deletes they key with the specified fingerprint' do
|
171
|
+
setup_process('gpg --quiet --batch --yes --delete-secret-key AAAAAAA', true, '')
|
172
|
+
|
173
|
+
expect(runner.delete_private_key('AAAAAAA')).to eq(true)
|
174
|
+
|
175
|
+
expect(Open3).to have_received(:popen2e)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns false when the deletion fails' do
|
179
|
+
setup_process('gpg --quiet --batch --yes --delete-secret-key AAAAAAA', false, '')
|
180
|
+
|
181
|
+
expect(runner.delete_private_key('AAAAAAA')).to eq(false)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe :delete_public_key do
|
186
|
+
it 'deletes the public key with the specified fingerprint' do
|
187
|
+
setup_process('gpg --quiet --batch --yes --delete-key AAAAAAA', true, '')
|
188
|
+
|
189
|
+
expect(runner.delete_public_key('AAAAAAA')).to eq(true)
|
190
|
+
|
191
|
+
expect(Open3).to have_received(:popen2e)
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'returns false when the deletion fails' do
|
195
|
+
setup_process('gpg --quiet --batch --yes --delete-key AAAAAAA', false, '')
|
196
|
+
|
197
|
+
expect(runner.delete_public_key('AAAAAAA')).to eq(false)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
describe :import_key_from_file do
|
202
|
+
before { allow(File).to receive(:read) }
|
203
|
+
|
204
|
+
it 'imports the key contents from a file and returns the recipients' do
|
205
|
+
output = '''
|
206
|
+
Version: GnuPG v1.4.12 (Darwin)
|
207
|
+
gpg: armor header:
|
208
|
+
Chris Nelson <superchrisnelson@gmail.com>gpg: sec 2048R/3388EE24 2013-03-04
|
209
|
+
gpg: key 3388EE24: secret key imported
|
210
|
+
gpg: pub 2048R/3388EE24 2013-03-04 Chris Nelson <superchrisnelson@gmail.com>
|
211
|
+
gpg: pub 2048R/3388EE24 2013-03-04 John Doe <jdoe@gmail.com>
|
212
|
+
gpg: using PGP trust model
|
213
|
+
gpg: key 3388EE24: public key "Chris Nelson <superchrisnelson@gmail.com>" imported
|
214
|
+
gpg: Total number processed: 1
|
215
|
+
gpg: imported: 1 (RSA: 1)
|
216
|
+
gpg: secret keys read: 1
|
217
|
+
gpg: secret keys imported: 1
|
218
|
+
'''
|
219
|
+
setup_process('gpg --batch -v --import "~/secret.pgp"', true, output)
|
220
|
+
|
221
|
+
expect(runner.import_key_from_file('~/secret.pgp')).to eq([
|
222
|
+
'superchrisnelson@gmail.com',
|
223
|
+
'jdoe@gmail.com'
|
224
|
+
])
|
225
|
+
|
226
|
+
expect(Open3).to have_received(:popen2e)
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'returns the recipients even when the import does not return success' do
|
230
|
+
output = '''
|
231
|
+
gpg: armor header: Version: GnuPG v1.4.12 (Darwin)
|
232
|
+
gpg: sec rsa2048/9539E22A3388EE24 2013-03-04 Chris Nelson <superchrisnelson@gmail.com>
|
233
|
+
gpg: sec rsa2048/9539E22A3388EE24 2013-03-04 John Doe <jdoe@gmail.com>
|
234
|
+
gpg: pub rsa2048/9539E22A3388EE24 2013-03-04 Chris Nelson <superchrisnelson@gmail.com>
|
235
|
+
gpg: key 9539E22A3388EE24: "Chris Nelson <superchrisnelson@gmail.com>" not changed
|
236
|
+
gpg: key 9539E22A3388EE24/9539E22A3388EE24: secret key already exists
|
237
|
+
gpg: key 9539E22A3388EE24/A09B286C349BAAD3: secret key already exists
|
238
|
+
gpg: key 9539E22A3388EE24: secret key imported
|
239
|
+
gpg: Total number processed: 1
|
240
|
+
gpg: unchanged: 1
|
241
|
+
gpg: secret keys read: 1
|
242
|
+
gpg: secret keys unchanged: 1
|
243
|
+
'''
|
244
|
+
setup_process('gpg --batch -v --import "~/secret.pgp"', false, output)
|
245
|
+
|
246
|
+
expect(runner.import_key_from_file('~/secret.pgp')).to eq([
|
247
|
+
'superchrisnelson@gmail.com',
|
248
|
+
'jdoe@gmail.com'
|
249
|
+
])
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'returns empty when the output is empty' do
|
253
|
+
setup_process('gpg --batch -v --import "~/secret.pgp"', false, '')
|
254
|
+
|
255
|
+
expect(runner.import_key_from_file('~/secret.pgp')).to eq([])
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
describe :verify_signature_file do
|
260
|
+
before { allow(File).to receive(:read) }
|
261
|
+
|
262
|
+
it 'verifies the signature contents from a file' do
|
263
|
+
setup_process('gpg --quiet --batch --verify "~/signature.asc"', true, '')
|
264
|
+
|
265
|
+
expect(runner.verify_signature_file('~/signature.asc')).to eq(true)
|
266
|
+
|
267
|
+
expect(Open3).to have_received(:popen2e)
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'returns false when verification fails' do
|
271
|
+
setup_process('gpg --quiet --batch --verify "~/signature.asc"', false, '')
|
272
|
+
|
273
|
+
expect(runner.verify_signature_file('~/signature.asc')).to eq(false)
|
274
|
+
end
|
275
|
+
|
276
|
+
it 'verifies and reads the signature contents from a file' do
|
277
|
+
setup_process('gpg --quiet --batch --output "~/output.txt" "~/signature.asc"', true, '')
|
278
|
+
|
279
|
+
expect(runner.verify_signature_file('~/signature.asc', '~/output.txt')).to eq(true)
|
280
|
+
|
281
|
+
expect(Open3).to have_received(:popen2e)
|
282
|
+
end
|
283
|
+
|
284
|
+
it 'returns false when signature data read fails' do
|
285
|
+
setup_process('gpg --quiet --batch --output "~/output.txt" "~/signature.asc"', false, '')
|
286
|
+
|
287
|
+
expect(runner.verify_signature_file('~/signature.asc', '~/output.txt')).to eq(false)
|
288
|
+
|
289
|
+
expect(Open3).to have_received(:popen2e)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
describe :decrypt_file do
|
294
|
+
context 'without passphrase ' do
|
295
|
+
it 'decrypts a file' do
|
296
|
+
setup_process('gpg --quiet --batch --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', true, '')
|
297
|
+
|
298
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt')).to eq(true)
|
299
|
+
|
300
|
+
expect(Open3).to have_received(:popen2e)
|
301
|
+
end
|
302
|
+
|
303
|
+
it 'returns false when decryption failed' do
|
304
|
+
setup_process('gpg --quiet --batch --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', false, '')
|
305
|
+
|
306
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt')).to eq(false)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
context 'with passphrase' do
|
311
|
+
context 'gpg 2.0' do
|
312
|
+
before {
|
313
|
+
allow(runner).to receive(:version_default).and_return('2.0.4')
|
314
|
+
}
|
315
|
+
|
316
|
+
it 'decrypts a file' do
|
317
|
+
setup_process('gpg --quiet --batch --passphrase "supersecret111" --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', true, '')
|
318
|
+
|
319
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt', 'supersecret111')).to eq(true)
|
320
|
+
|
321
|
+
expect(Open3).to have_received(:popen2e)
|
322
|
+
end
|
323
|
+
|
324
|
+
it 'returns false when decryption failed' do
|
325
|
+
setup_process('gpg --quiet --batch --passphrase "supersecret111" --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', false, '')
|
326
|
+
|
327
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt', 'supersecret111')).to eq(false)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
context 'gpg >= 2.1' do
|
332
|
+
before {
|
333
|
+
allow(runner).to receive(:version_default).and_return('2.1.23')
|
334
|
+
}
|
335
|
+
|
336
|
+
it 'decrypts a file' do
|
337
|
+
setup_process('gpg --quiet --batch --pinentry-mode loopback --passphrase "supersecret123" --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', true, '')
|
338
|
+
|
339
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt', 'supersecret123')).to eq(true)
|
340
|
+
|
341
|
+
expect(Open3).to have_received(:popen2e)
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'returns false when decryption failed' do
|
345
|
+
setup_process('gpg --quiet --batch --pinentry-mode loopback --passphrase "supersecret123" --yes --ignore-mdc-error --output "/tmp/plaintext.txt" --decrypt "~/encrypted_text.txt"', false, '')
|
346
|
+
|
347
|
+
expect(runner.decrypt_file('~/encrypted_text.txt', '/tmp/plaintext.txt', 'supersecret123')).to eq(false)
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
describe :sign_file do
|
354
|
+
context 'without passphrase' do
|
355
|
+
it 'signs a file' do
|
356
|
+
setup_process('gpg --quiet --batch --yes --ignore-mdc-error --output "/tmp/signed.txt" --sign "~/plaintext.txt"', true, '')
|
357
|
+
|
358
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/signed.txt')).to eq(true)
|
359
|
+
|
360
|
+
expect(Open3).to have_received(:popen2e)
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'returns false when signing failed' do
|
364
|
+
setup_process('gpg --quiet --batch --yes --ignore-mdc-error --output "/tmp/signed.txt" --sign "~/plaintext.txt"', false, '')
|
365
|
+
|
366
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/signed.txt')).to eq(false)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
context 'with passphrase' do
|
371
|
+
context 'gpg 2.0' do
|
372
|
+
before {
|
373
|
+
allow(runner).to receive(:version_default).and_return('2.0.4')
|
374
|
+
}
|
375
|
+
|
376
|
+
it 'signs a file' do
|
377
|
+
setup_process('gpg --quiet --batch --passphrase "supersecret111" --yes --ignore-mdc-error --output "/tmp/signed.txt" --sign "~/plaintext.txt"', true, '')
|
378
|
+
|
379
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/signed.txt', 'supersecret111')).to eq(true)
|
380
|
+
|
381
|
+
expect(Open3).to have_received(:popen2e)
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'returns false when signing failed' do
|
385
|
+
setup_process('gpg --quiet --batch --passphrase "supersecret111" --yes --ignore-mdc-error --output "/tmp/signed.txt" --sign "~/plaintext.txt"', false, '')
|
386
|
+
|
387
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/signed.txt', 'supersecret111')).to eq(false)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
|
391
|
+
context 'gpg >= 2.1' do
|
392
|
+
before {
|
393
|
+
allow(runner).to receive(:version_default).and_return('2.1.23')
|
394
|
+
}
|
395
|
+
|
396
|
+
it 'signs a file' do
|
397
|
+
setup_process('gpg --quiet --batch --pinentry-mode loopback --passphrase "supersecret123" --yes --ignore-mdc-error --output "/tmp/output.txt" --sign "~/plaintext.txt"', true, '')
|
398
|
+
|
399
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/output.txt', 'supersecret123')).to eq(true)
|
400
|
+
|
401
|
+
expect(Open3).to have_received(:popen2e)
|
402
|
+
end
|
403
|
+
|
404
|
+
it 'returns false when signing failed' do
|
405
|
+
setup_process('gpg --quiet --batch --pinentry-mode loopback --passphrase "supersecret123" --yes --ignore-mdc-error --output "/tmp/output.txt" --sign "~/plaintext.txt"', false, '')
|
406
|
+
|
407
|
+
expect(runner.sign_file('~/plaintext.txt', '/tmp/output.txt', 'supersecret123')).to eq(false)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
describe :encrypt_file do
|
414
|
+
it 'encrypts a file' do
|
415
|
+
setup_process('gpg --quiet --batch --yes --output "/tmp/out.txt" --recipient "foo@bar.com" --trust-model always --encrypt "/tmp/in.txt"', true, '')
|
416
|
+
|
417
|
+
expect(runner.encrypt_file('/tmp/in.txt', '/tmp/out.txt', ['foo@bar.com'])).to eq(true)
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'encrypts a file for multiple recipients' do
|
421
|
+
setup_process('gpg --quiet --batch --yes --output "/tmp/out.txt" --recipient "foo@bar.com" --recipient "aaaa@yahoo.com" --trust-model always --encrypt "/tmp/in.txt"', true, '')
|
422
|
+
|
423
|
+
expect(runner.encrypt_file('/tmp/in.txt', '/tmp/out.txt', ['foo@bar.com', 'aaaa@yahoo.com'])).to eq(true)
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'returns false when encryption failed' do
|
427
|
+
setup_process('gpg --quiet --batch --yes --output "/tmp/out.txt" --recipient "foo@bar.com" --trust-model always --encrypt "/tmp/in.txt"', false, '')
|
428
|
+
|
429
|
+
expect(runner.encrypt_file('/tmp/in.txt', '/tmp/out.txt', ['foo@bar.com'])).to eq(false)
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe GPG::TempPathHelper do
|
4
|
+
it 'creates a temporary file path' do
|
5
|
+
path = GPG::TempPathHelper.create
|
6
|
+
|
7
|
+
expect(path.start_with?(Dir.tmpdir)).to eq(true)
|
8
|
+
expect(File.exists?(path)).to eq(false)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'creates unique paths every time' do
|
12
|
+
paths = (1..100).map { GPG::TempPathHelper.create }
|
13
|
+
|
14
|
+
expect(paths.uniq).to eq(paths)
|
15
|
+
expect(paths.length).to eq(100)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'can receive an execution block' do
|
19
|
+
path2 = nil
|
20
|
+
path1 = GPG::TempPathHelper.create do |p|
|
21
|
+
path2 = p
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(path2).to eq(path1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'deletes the temporary file if it exists' do
|
28
|
+
path = GPG::TempPathHelper.create do |p|
|
29
|
+
File.write(p, 'test')
|
30
|
+
expect(File.exists?(p)).to eq(true)
|
31
|
+
end
|
32
|
+
|
33
|
+
expect(File.exists?(path)).to eq(false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'deletes the temporary file even when there is an exception in the block' do
|
37
|
+
exception_raised = false
|
38
|
+
path = nil
|
39
|
+
begin
|
40
|
+
GPG::TempPathHelper.create do |p|
|
41
|
+
path = p
|
42
|
+
File.write(p, 'test')
|
43
|
+
expect(File.exists?(p)).to eq(true)
|
44
|
+
value = 45 / 0
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
exception_raised = true
|
48
|
+
end
|
49
|
+
|
50
|
+
expect(exception_raised).to eq(true)
|
51
|
+
expect(File.exists?(path)).to eq(false)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PGP::Signer do
|
4
|
+
include KeysHelper
|
5
|
+
|
6
|
+
let(:private_key_path) { Fixtures_Path.join('private_key_with_passphrase.asc').to_s }
|
7
|
+
let(:public_key_path) { Fixtures_Path.join('public_key_with_passphrase.asc').to_s }
|
8
|
+
|
9
|
+
let(:signer) do
|
10
|
+
signer = PGP::Signer.new
|
11
|
+
signer.passphrase = "testingpgp"
|
12
|
+
signer.add_keys_from_file(private_key_path)
|
13
|
+
signer
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:unsigned_file) { Fixtures_Path.join('signed_file.txt') }
|
17
|
+
let(:unsigned_data) { File.read(unsigned_file)}
|
18
|
+
let(:signed_file) { Fixtures_Path.join('signed_file.txt.asc') }
|
19
|
+
let(:verifier) do
|
20
|
+
verifier = PGP::Verifier.new
|
21
|
+
verifier.add_keys_from_file(public_key_path)
|
22
|
+
verifier
|
23
|
+
end
|
24
|
+
|
25
|
+
before { remove_all_keys }
|
26
|
+
|
27
|
+
describe '#sign' do
|
28
|
+
|
29
|
+
it "signs" do
|
30
|
+
expect(verifier.verify(signer.sign(unsigned_data))).to eq(unsigned_data)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "encrypting and signing" do
|
36
|
+
let(:encryptor) { PGP::Encryptor.new(File.read public_key_path) }
|
37
|
+
let(:decryptor) do
|
38
|
+
decryptor = PGP::Decryptor.new
|
39
|
+
decryptor.passphrase = "testingpgp"
|
40
|
+
decryptor.add_keys_from_file(private_key_path)
|
41
|
+
decryptor
|
42
|
+
end
|
43
|
+
it "can decrypt and verify something that has been signed and encrypted" do
|
44
|
+
expect(verifier.verify(decryptor.decrypt(encryptor.encrypt(signer.sign("something fabulous"))))).to eq("something fabulous")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PGP::Verifier do
|
4
|
+
include KeysHelper
|
5
|
+
|
6
|
+
let(:verifier) { PGP::Verifier.new }
|
7
|
+
let(:unsigned_file) { Fixtures_Path.join('signed_file.txt') }
|
8
|
+
let(:signed_file) { Fixtures_Path.join('signed_file.txt.asc') }
|
9
|
+
|
10
|
+
before { remove_all_keys }
|
11
|
+
|
12
|
+
describe '#verify' do
|
13
|
+
context 'When the Public Key is from a file' do
|
14
|
+
let(:public_key_path) { Fixtures_Path.join('public_key_with_passphrase.asc').to_s }
|
15
|
+
|
16
|
+
before do
|
17
|
+
verifier.add_keys_from_file(public_key_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "verifies" do
|
21
|
+
expect(verifier.verify(File.read(signed_file))).to eq(File.read(unsigned_file))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'When the public key cannot verify a signature' do
|
26
|
+
let(:public_key_path_wrong_public_key) { Fixtures_Path.join('wrong_public_key_for_signature.asc').to_s }
|
27
|
+
|
28
|
+
before do
|
29
|
+
verifier.add_keys_from_file(public_key_path_wrong_public_key)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should raise an exception" do
|
33
|
+
expect {
|
34
|
+
verifier.verify(File.read(signed_file))
|
35
|
+
}.to raise_exception 'Signature could not be verified'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'RSpec multiple yields quirks' do
|
4
|
+
##
|
5
|
+
## I wrote this test to capture my lack of understanding on how to mock a method that yields multiple times
|
6
|
+
##
|
7
|
+
#it 'this should work' do
|
8
|
+
# stub1 = double
|
9
|
+
# allow(stub1).to receive(:path).and_return('path1')
|
10
|
+
#
|
11
|
+
# stub2 = double
|
12
|
+
# allow(stub2).to receive(:path).and_return('path2')
|
13
|
+
#
|
14
|
+
# allow(Tempfile).to receive(:open).and_return(stub1, stub2)
|
15
|
+
#
|
16
|
+
# paths = []
|
17
|
+
# Tempfile.open do |f1|
|
18
|
+
# Tempfile.open do |f2|
|
19
|
+
# paths = [f1.path, f2.path]
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# expect(paths).to eq(['path1', 'path2'])
|
23
|
+
#end
|
24
|
+
|
25
|
+
it 'workaround' do
|
26
|
+
stub1 = double
|
27
|
+
allow(stub1).to receive(:path).and_return('path1', 'path2')
|
28
|
+
|
29
|
+
allow(Tempfile).to receive(:open).and_yield(stub1)
|
30
|
+
|
31
|
+
paths = []
|
32
|
+
Tempfile.open do |f1|
|
33
|
+
Tempfile.open do |f2|
|
34
|
+
paths = [f1.path, f2.path]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
expect(paths).to eq(['path1', 'path2'])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'Nested Temp files' do
|
42
|
+
it 'can be read externally' do
|
43
|
+
file_contents1 = "3" * 300000
|
44
|
+
actual = ''
|
45
|
+
|
46
|
+
Tempfile.open do |f1|
|
47
|
+
Tempfile.open do |f2|
|
48
|
+
f1.write(file_contents1)
|
49
|
+
f1.rewind
|
50
|
+
|
51
|
+
buffer = File.read(f1.path)
|
52
|
+
f2.write(buffer)
|
53
|
+
f2.rewind
|
54
|
+
|
55
|
+
actual = File.read(f2.path)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
expect(actual).to eq(file_contents1)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can be read with its handler' do
|
63
|
+
file_contents1 = "3" * 300000
|
64
|
+
actual = ''
|
65
|
+
|
66
|
+
Tempfile.open do |f1|
|
67
|
+
Tempfile.open do |f2|
|
68
|
+
f1.write(file_contents1)
|
69
|
+
f1.rewind
|
70
|
+
|
71
|
+
buffer = f1.read
|
72
|
+
f2.write(buffer)
|
73
|
+
f2.rewind
|
74
|
+
|
75
|
+
buffer = f2.read
|
76
|
+
actual << buffer
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
expect(actual).to eq(file_contents1)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'can be read mixing approaches' do
|
84
|
+
file_contents1 = "3" * 300000
|
85
|
+
actual = ''
|
86
|
+
|
87
|
+
Tempfile.open do |f1|
|
88
|
+
Tempfile.open do |f2|
|
89
|
+
f1.write(file_contents1)
|
90
|
+
f1.rewind
|
91
|
+
|
92
|
+
buffer = File.read(f1.path)
|
93
|
+
File.write(f2.path, buffer)
|
94
|
+
|
95
|
+
f2.rewind
|
96
|
+
actual = f2.read
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
expect(actual).to eq(file_contents1)
|
101
|
+
end
|
102
|
+
end
|