paperback 0.0.3 → 0.0.4
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/.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
|