paperback 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop-disables.yml +0 -3
- data/CHANGELOG.md +14 -0
- data/README.md +23 -5
- data/bin/paperback +4 -0
- data/lib/paperback/cli.rb +21 -2
- data/lib/paperback/document.rb +63 -9
- data/lib/paperback/preparer.rb +33 -10
- data/lib/paperback/version.rb +1 -1
- data/sample/aes.key +1 -0
- data/sample/aes.pdf +14413 -0
- data/sample/aes.pdf.passphrase.txt +1 -0
- data/sample/rsa2048.pdf +106803 -0
- data/sample/rsa2048.pdf.passphrase.txt +1 -0
- data/sample/rsa2048.pem +27 -0
- data/spec/functional/paperback/cli_spec.rb +166 -0
- metadata +10 -2
@@ -0,0 +1 @@
|
|
1
|
+
jjdT9GBnpbKu2tXTewindHqXyzkvxfNhvcssnJIPyRK
|
data/sample/rsa2048.pem
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpAIBAAKCAQEAplORJwTNtHcx9V1v/AV6T9EnAmeA2JMVsp3cT0+TOFo4IGwO
|
3
|
+
2BSrC95vdv5CPBS2Jv8OtFKAQtVii2wNsmfcN/YBo7ELds0GgmWnFt+kzj7JIUJd
|
4
|
+
3Igte8RGyjmuM+ZVZ31lvBOqv30ho4zAYVd75Ohli8LTI0i+vSQLAeaGVj3asojb
|
5
|
+
c3AhnOkp+Sr5Pva6GtEsaMAZ+NIpzbwH8/o+mGEBl0q4vdZ+zr58TjPoRJU6PJyG
|
6
|
+
edEID7fQAth+yhdyg4Q0Tmo15CinWRIK0C6Jjs+iq6/FCsFKbHP9lCgC9SgSbSVQ
|
7
|
+
+HoYDVVFDg08+x5sergrBVmjNV9KZG2TS74m+QIDAQABAoIBAFGvVOguyb/JkjWI
|
8
|
+
sAi7v/gXJeteXb7/bXaNZLdmw0y3sjzqFNg2PlLT6TYtZOYQ3eyE8vnIUsl/9qe2
|
9
|
+
Z3uo2lR5cxCrUJAF3R6JQehP6aaI20qER4iZMI1KNeH3NgCzLFppEV+u4PquP6kM
|
10
|
+
5SZpSqV/JO/gvMTlYxeM7gL+7hbH+ugvxx18vcaT3Bw2ezCtIxkBFN9+S81U/019
|
11
|
+
QJtUtXx2nP4hetSm6UVueMOQ3WlcDNuKP3N2HM8HIA2Kl1O1Pznmq4h6xN8Bqfw6
|
12
|
+
5Bz987xWzbaE3bYU+aGNmCE7rQtyQknB+yUAh/6wLywsgGSB+pbjhrLHegf/bCn9
|
13
|
+
1dXCTwECgYEA2u/6jjlE6Fg6HlorZROkd132RhKRxS5+2wR/TzuQKURMXzJ+X6ma
|
14
|
+
hOJfCcvRdfBW9ODeFWyFMc77fo4iCqbgvNU9/hhpTmxLS+4DC2L/0yXG5ioLXXSO
|
15
|
+
5XqopA+ry1U4ZyBJ/JIKKcYJwDvnMxdJbT2VzUMBAyQECsmFlR7g7jECgYEAwnua
|
16
|
+
UUH29RgQ5zE3OmTO/lWM05fOYe7TrTGFyLwQCkd/OBwMn2e8KOlWJ97wBBJ4SLPE
|
17
|
+
dnJoc9LpFtz3MMA3bxsVKExyWJKVx/zsecW1F/gJ4Zuxs5XbelHuA8TxsTaJ1Ml1
|
18
|
+
Vtulg+1J6cyRUIb+IAyVYYrwWEwTVEYOMDEYK0kCgYEA1afjx++zg1q2PPCZ8KXe
|
19
|
+
lm6n63Irt1XMjkgxRU1HPj5cmXpZQVnuZUfUn2a8HnBRK+BWWmnxAc6QN8zVA7gf
|
20
|
+
Eai4OsPv4IxmXs8DQZV3OsNe+zaEqMsauG+fK5vvdxrPVDR6f3S1XlbZJTRQR9et
|
21
|
+
3GGBUzLURhBbsXtAVWow78ECgYEAky7z5NclTOu/rxgCEgFE3hpkj6XtnRQfwo0e
|
22
|
+
EIKzKPRfBVnE+8aIEfY1K8t+nbZUrcDOyE1hW3aczJ7BvHVb/ovigTMJvpqBHG7/
|
23
|
+
kFDd8pvW7pTj8WNSTMpAdT3RJCqAOTeVw/xK0QL90yDsQKNJI3wieUWrC0fXwQmb
|
24
|
+
p0EgZ3ECgYB86T30e+TupkgbPUkjhzyJbg5EadEYQ41+8LyoWfVNWlHwsVj7qLtC
|
25
|
+
CzsBbGvUqAz0DKAS6pEmTLVe1i/f8Lla55zqTvRK/RF8NAKU2yEQl15hzpmv9Nw1
|
26
|
+
pItLJuM6LpVBsUhKKzexuC9vTRN2Qg5tiGYpGiHW1RmFBTuRP2CKDw==
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,166 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest/sha2'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
require 'subprocess'
|
8
|
+
|
9
|
+
RSpec.describe Paperback::CLI do
|
10
|
+
Executable = File.dirname(__FILE__) + '/../../bin/paperback'
|
11
|
+
|
12
|
+
# pdftotext from poppler is a test dependency
|
13
|
+
it 'finds system pdftotext installed (test dependency)' do
|
14
|
+
expect(Subprocess.check_output(%w[which pdftotext])
|
15
|
+
).to match(/pdftotext/)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'end-to-end test:' do
|
19
|
+
before(:each) do
|
20
|
+
@tmpdir = Dir.mktmpdir('paperback-rspec.')
|
21
|
+
@orig_cwd = Dir.pwd
|
22
|
+
Dir.chdir(@tmpdir)
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
Dir.chdir(@orig_cwd) if @orig_cwd
|
27
|
+
FileUtils.rm_r(@tmpdir) if @tmpdir
|
28
|
+
@tmpdir = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'backs up unencrypted content correctly' do
|
32
|
+
secret = "The quick brown fox jumps over the lazy dog.\n"
|
33
|
+
sha = 'b47cc0f104b62d4c7c30bcd68fd8e67613e287dc4ad8c310ef10cbadea9c4380'
|
34
|
+
|
35
|
+
File.write('fox.txt', secret)
|
36
|
+
|
37
|
+
expect(Digest::SHA256.hexdigest(File.read('fox.txt'))).to eq(sha)
|
38
|
+
|
39
|
+
timestamp = Time.now.strftime('%F %T %z')
|
40
|
+
|
41
|
+
expected = <<-EOM
|
42
|
+
This is a paper backup produced by `paperback`. https://github.com/ab/paperback
|
43
|
+
Filename:
|
44
|
+
Backed up:
|
45
|
+
Mtime:
|
46
|
+
Bytes:
|
47
|
+
Comment:
|
48
|
+
SHA256:
|
49
|
+
Encrypted:
|
50
|
+
|
51
|
+
fox.txt
|
52
|
+
#{timestamp}
|
53
|
+
#{timestamp}
|
54
|
+
45
|
55
|
+
Some comment
|
56
|
+
b47cc0f104b62d4c7c30bcd68fd8e67613e287dc4ad8c310ef10cbadea9c4380
|
57
|
+
no
|
58
|
+
|
59
|
+
1 of 2
|
60
|
+
|
61
|
+
\fThis sixword text encodes 45 bytes in 6 six-word sentences. Decode with `sixword -d`.
|
62
|
+
1: beak us ache sour bern lola
|
63
|
+
2: core arc hulk slid drew due
|
64
|
+
3: chub ends bog russ bess mast
|
65
|
+
4: dime arm skew quit kim san
|
66
|
+
5: coat mod hit need knew grim
|
67
|
+
6: cave ella raft fir a act3
|
68
|
+
|
69
|
+
This base64 text encodes 45 bytes in 61 characters. Decode with `base64 --decode`.
|
70
|
+
VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4K
|
71
|
+
|
72
|
+
2 of 2
|
73
|
+
|
74
|
+
\f
|
75
|
+
EOM
|
76
|
+
|
77
|
+
# strip trailing newline
|
78
|
+
expected = expected[0...-1]
|
79
|
+
|
80
|
+
# create the backup file!
|
81
|
+
Paperback::CLI.create_backup(
|
82
|
+
input: 'fox.txt', output: 'fox.pdf', encrypt: false,
|
83
|
+
comment: 'Some comment'
|
84
|
+
)
|
85
|
+
|
86
|
+
# make sure we got a PDF
|
87
|
+
expect(File.size('fox.pdf')).to be > 2000
|
88
|
+
expect(File.read('fox.pdf', 20)).to start_with("%PDF-1.3\n")
|
89
|
+
|
90
|
+
# convert to text to see what's in it
|
91
|
+
Subprocess.check_call(%w[pdftotext fox.pdf fox.pdftotext.txt])
|
92
|
+
|
93
|
+
expect(File.size('fox.pdftotext.txt')).to be > 100
|
94
|
+
|
95
|
+
# This test is likely to break given even minor changes to paperback, so it
|
96
|
+
# may not be particularly useful. However, it's nice to at least have some
|
97
|
+
# kind of end-to-end test.
|
98
|
+
expect(File.read('fox.pdftotext.txt')).to eq(expected)
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'backs up encrypted content correctly' do
|
102
|
+
secret = 'Ey9V03Iuq/gKLH0aVf2aPTAULWydpWt6G/L+O7LJXHeK'
|
103
|
+
sha = '8675e6be1ae84a781b2a81c65b3300ee0611ccb3ad1e0b8e8dad5a94b6d4eef2'
|
104
|
+
|
105
|
+
File.write('2.txt', secret)
|
106
|
+
expect(Digest::SHA256.hexdigest(File.read('2.txt'))).to eq(sha)
|
107
|
+
|
108
|
+
# create the backup file!
|
109
|
+
Paperback::CLI.create_backup(
|
110
|
+
input: '2.txt', output: '2.pdf', encrypt: true,
|
111
|
+
passphrase_file: '2.passphrase.txt'
|
112
|
+
)
|
113
|
+
|
114
|
+
# make sure we got a PDF
|
115
|
+
expect(File.size('2.pdf')).to be > 2000
|
116
|
+
expect(File.read('2.pdf', 20)).to start_with("%PDF-1.3\n")
|
117
|
+
|
118
|
+
# make sure we got a passphrase
|
119
|
+
pass = File.read('2.passphrase.txt')
|
120
|
+
expect(pass.length).to eq 43
|
121
|
+
pass_sha = Digest::SHA256.hexdigest(pass)
|
122
|
+
|
123
|
+
# convert pdf to text
|
124
|
+
Subprocess.check_call(%w[pdftotext 2.pdf 2.pdftotext.txt])
|
125
|
+
|
126
|
+
expect(File.size('2.pdftotext.txt')).to be > 100
|
127
|
+
|
128
|
+
content = File.read('2.pdftotext.txt')
|
129
|
+
|
130
|
+
expect(content).to include('2.txt')
|
131
|
+
expect(content).to include(sha)
|
132
|
+
expect(content).to include(pass_sha[0...16])
|
133
|
+
|
134
|
+
# locate sixword content
|
135
|
+
expect(content).to include('This sixword text')
|
136
|
+
pages = content.split("\f")
|
137
|
+
pg2 = pages.fetch(1)
|
138
|
+
lines = pg2.split("\n")
|
139
|
+
expect(lines.fetch(0)).to start_with('This sixword text')
|
140
|
+
|
141
|
+
# pull sixword lines until blank line
|
142
|
+
sixword_lines = []
|
143
|
+
lines[1..-1].each do |line|
|
144
|
+
break if line.strip.empty?
|
145
|
+
expect(line).to match(/\A\d+: [a-z ]+[0-9]?\z/)
|
146
|
+
sixword_lines << line.split(':').fetch(1)
|
147
|
+
end
|
148
|
+
|
149
|
+
# decode sixword content
|
150
|
+
decoded = Sixword.decode(sixword_lines.join("\n"), padding_ok: true)
|
151
|
+
|
152
|
+
# decrypt data
|
153
|
+
File.write('2.sixword.gpg', decoded)
|
154
|
+
p = Subprocess::Process.new(
|
155
|
+
%w[gpg --batch --passphrase-fd 0 --decrypt 2.sixword.gpg],
|
156
|
+
stdin: Subprocess::PIPE, stdout: Subprocess::PIPE
|
157
|
+
)
|
158
|
+
decrypted, _ = p.communicate(pass)
|
159
|
+
expect(p.wait.exitstatus).to eq 0
|
160
|
+
expect(p.wait.success?).to eq true
|
161
|
+
|
162
|
+
expect(decrypted).to eq(secret)
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paperback
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Brody
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -177,6 +177,13 @@ files:
|
|
177
177
|
- lib/paperback/preparer.rb
|
178
178
|
- lib/paperback/version.rb
|
179
179
|
- paperback.gemspec
|
180
|
+
- sample/aes.key
|
181
|
+
- sample/aes.pdf
|
182
|
+
- sample/aes.pdf.passphrase.txt
|
183
|
+
- sample/rsa2048.pdf
|
184
|
+
- sample/rsa2048.pdf.passphrase.txt
|
185
|
+
- sample/rsa2048.pem
|
186
|
+
- spec/functional/paperback/cli_spec.rb
|
180
187
|
- spec/spec_helper.rb
|
181
188
|
- spec/unit/paperback_spec.rb
|
182
189
|
homepage: https://github.com/ab/paperback
|
@@ -204,5 +211,6 @@ signing_key:
|
|
204
211
|
specification_version: 4
|
205
212
|
summary: Create paper backups of sensitive data
|
206
213
|
test_files:
|
214
|
+
- spec/functional/paperback/cli_spec.rb
|
207
215
|
- spec/spec_helper.rb
|
208
216
|
- spec/unit/paperback_spec.rb
|