fix-ceb-nulls.lua

Mon, 01 Aug 2022 11:29:27 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Mon, 01 Aug 2022 11:29:27 +0100
changeset 0
ed346ec34e2a
permissions
-rwxr-xr-x

Initial commit

0
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
1 #!/usr/bin/env lua5.3
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
2
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
3 if not _VERSION:match("^Lua 5%.[34]") then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
4 print("This utility requires Lua 5.3/5.4");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
5 return 1;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
6 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
7
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
8 local have_ciphers, ciphers = pcall(require, "openssl.cipher");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
9 if not have_ciphers then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
10 print("openssl.ciphers module not found.");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
11 print("On Debian, install lua-luaossl, or install luarocks and run");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
12 print(" luarocks install luaossl");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
13 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
14 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
15
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
16 local have_kdf, kdf = pcall(require, "openssl.kdf");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
17 if not have_kdf then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
18 print("openssl.kdf module not found.");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
19 print("On Debian, install lua-luaossl, or install luarocks and run");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
20 print(" luarocks install luaossl");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
21 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
22 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
23
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
24 local have_zlib, zlib = pcall(require, "zlib");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
25 if not have_zlib then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
26 print("zlib module not found.");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
27 print("On Debian, install lua-zlib, or install luarocks and run");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
28 print(" luarocks install lua-zlib");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
29 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
30 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
31
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
32 if #arg == 0 then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
33 print("Fix Conversations backup files that contain NUL bytes");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
34 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
35 print("Usage:");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
36 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
37 print("", arg[0].." INPUT_FILE PASSWORD");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
38 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
39 print("On success, a new backup file will be created with the suffix '-fixed.ceb'.");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
40 print("");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
41 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
42
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
43 if #arg == 0 or not have_zlib or not have_kdf or not have_ciphers then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
44 return 1;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
45 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
46
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
47 local input_filename = assert(arg[1], "no ceb file specified");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
48
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
49 local file_password = assert(arg[2], "no password specified");
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
50
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
51 local function read_header(f)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
52 local function read_int()
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
53 return (">i4"):unpack(f:read(4));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
54 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
55 local function read_short()
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
56 return (">i2"):unpack(f:read(2));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
57 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
58 local function read_long()
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
59 return (">i8"):unpack(f:read(8));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
60 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
61 local function read_string()
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
62 local n = read_short();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
63 return f:read(n);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
64 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
65
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
66 return {
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
67 version = read_int();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
68 app_id = read_string();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
69 jid = read_string();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
70 timestamp = math.floor(read_long()/1000);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
71 iv = f:read(12);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
72 salt = f:read(16);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
73 };
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
74 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
75
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
76 local function write_header(f, header)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
77 local function write_int(f, n)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
78 f:write((">i4"):pack(n));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
79 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
80 local function write_short(f, n)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
81 f:write((">i2"):pack(n));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
82 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
83 local function write_long(f, n)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
84 f:write((">i8"):pack(n));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
85 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
86 local function write_string(f, s)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
87 write_short(f, #s);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
88 f:write(s);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
89 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
90
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
91 write_int(f, header.version);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
92 write_string(f, header.app_id);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
93 write_string(f, header.jid);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
94 write_long(f, header.timestamp*1000);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
95 assert(#header.iv == 12);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
96 f:write(header.iv);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
97 assert(#header.salt == 16);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
98 f:write(header.salt);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
99 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
100
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
101 local f = io.open(input_filename);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
102
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
103 local header = read_header(f);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
104
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
105 print("version", header.version);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
106 print("app", header.app_id);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
107 print("jid", header.jid);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
108 print("timestamp", os.date("%c", header.timestamp));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
109
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
110
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
111 local function generate_key(password, salt)
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
112 return kdf.derive({
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
113 type = "PBKDF2";
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
114 md = "sha1";
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
115 pass = password;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
116 salt = salt;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
117 iter = 1024;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
118 outlen = 128/8;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
119 });
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
120 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
121
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
122 print("k", #(generate_key(file_password, header.salt)));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
123
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
124 local decryption_key = generate_key(file_password, header.salt);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
125
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
126 local cipher = ciphers.new("AES-128-GCM"):decrypt(decryption_key, header.iv);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
127
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
128 local decompress = zlib.inflate();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
129
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
130 local compress = zlib.deflate();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
131 local cipher_out = ciphers.new("AES-128-GCM"):encrypt(decryption_key, header.iv);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
132
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
133 local null_replacement = string.char(0xE2, 0x90, 0x80); -- U+2400 SYMBOL FOR NULL
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
134
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
135 local outfile = assert(io.open(input_filename:gsub("%.ceb$", "-fixed.ceb"), "w+"));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
136 write_header(outfile, header);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
137
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
138 repeat
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
139 local enc_data = f:read(4096);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
140 if not enc_data then break; end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
141
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
142 local gz_data = assert(cipher:update(enc_data));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
143
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
144 local status, data, eof = pcall(decompress, gz_data);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
145 if not status then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
146 print("EE: Failed to decompress: "..tostring(data));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
147 return 1;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
148 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
149
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
150 local status, recompressed = pcall(compress, ((data:gsub("\000", null_replacement))));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
151 if not status then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
152 print("EE: Failed to compress: "..tostring(data));
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
153 return 1;
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
154 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
155 local reencrypted = cipher_out:update(recompressed);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
156 outfile:write(reencrypted);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
157 until eof
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
158
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
159 local final_data = cipher_out:final();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
160 if final_data and final_data ~= "" then
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
161 outfile:write(final_data);
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
162 end
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
163 outfile:close();
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
164
ed346ec34e2a Initial commit
Matthew Wild <mwild1@gmail.com>
parents:
diff changeset
165 print("Done");

mercurial