Skip to content

Commit 1027822

Browse files
committed
Allow smaller amounts of padding and smaller records with padding
1 parent 558cfd5 commit 1027822

File tree

3 files changed

+66
-7
lines changed

3 files changed

+66
-7
lines changed

nodejs/ece.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ function unpadLegacy(data, version) {
330330

331331
function unpad(data, last) {
332332
var i = data.length - 1;
333-
while(i > 0) {
333+
while(i >= 0) {
334334
if (data[i]) {
335335
if (last) {
336336
if (data[i] !== 2) {
@@ -433,6 +433,10 @@ function encryptRecord(key, counter, buffer, pad, header, last) {
433433
keylog('padding', padding);
434434
ciphertext.push(gcm.update(padding));
435435
ciphertext.push(gcm.update(buffer));
436+
437+
if (!last && padding.length + buffer.length < header.rs) {
438+
throw new Error('Unable to pad to record size');
439+
}
436440
} else {
437441
ciphertext.push(gcm.update(buffer));
438442
padding.writeUIntBE(last ? 2 : 1, 0, 1);
@@ -520,6 +524,9 @@ function encrypt(buffer, params) {
520524
if (header.version !== 'aes128gcm') {
521525
recordPad = Math.min((1 << (padSize * 8)) - 1, recordPad);
522526
}
527+
if (pad > 0 && recordPad === 0) {
528+
++recordPad; // Deal with perverse case of rs=overhead+1 with padding.
529+
}
523530
pad -= recordPad;
524531

525532
var end = start + header.rs - overhead - recordPad;
@@ -528,18 +535,16 @@ function encrypt(buffer, params) {
528535
// of a buffer.
529536
last = end > buffer.length;
530537
} else {
531-
last = end >= buffer.length && pad <= 0;
538+
last = end >= buffer.length;
532539
}
540+
last = last && pad <= 0;
533541
var block = encryptRecord(key, counter, buffer.slice(start, end),
534542
recordPad, header, last);
535543
result = Buffer.concat([result, block]);
536544

537545
start = end;
538546
++counter;
539547
}
540-
if (pad) {
541-
throw new Error('Unable to pad by requested amount, ' + pad + ' remaining');
542-
}
543548
return result;
544549
}
545550

nodejs/test.js

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function usage() {
1616
console.log(' "dump[=file]" log info to ../encrypt_data.json or the specified file');
1717
}
1818
var args = process.argv.slice(2);
19-
var minLen = 3;
19+
var minLen = 1;
2020
var maxLen = 100;
2121
var plaintext;
2222
var dumpFile;
@@ -65,11 +65,12 @@ function logbuf(msg, buf) {
6565
}
6666
}
6767

68-
function saveDump(data){
68+
function reallySaveDump(data){
6969
if (dumpFile && data.version) {
7070
dumpData.push(data);
7171
}
7272
}
73+
var saveDump = reallySaveDump;
7374

7475
function validate() {
7576
['hello', null, 1, NaN, [], {}].forEach(function(v) {
@@ -181,6 +182,48 @@ function exactlyOneRecord(version) {
181182
encryptDecrypt(input, params, params);
182183
}
183184

185+
// If rs only allows one octet of data in each record AND padding is requested,
186+
// then we need to ensure that padding is added without infinitely looping.
187+
function padTinyRecord(version) {
188+
var input = generateInput(1);
189+
var params = {
190+
version: version,
191+
key: base64.encode(crypto.randomBytes(16)),
192+
rs: rsoverhead(version) + 1,
193+
pad: 2
194+
};
195+
encryptDecrypt(input, params, params);
196+
}
197+
198+
// The earlier versions had a limit to how much padding they could include in
199+
// each record, which means that they could fail to encrypt if too much padding
200+
// was requested with a large record size.
201+
function tooMuchPadding(version) {
202+
if (version === 'aes128gcm') {
203+
return;
204+
}
205+
var padSize = rsoverhead(version);
206+
var rs = Math.pow(256, padSize) + padSize + 1;
207+
var input = generateInput(1);
208+
var params = {
209+
version: version,
210+
key: base64.encode(crypto.randomBytes(16)),
211+
rs: rs,
212+
pad: rs
213+
};
214+
var ok = false;
215+
try {
216+
ece.encrypt(input, params);
217+
} catch (e) {
218+
log('----- OK: ' + e);
219+
ok = true;
220+
}
221+
if (!ok) {
222+
throw new Error('Encryption succeeded, but should not have');
223+
}
224+
}
225+
226+
184227
function detectTruncation(version) {
185228
if (version === 'aes128gcm') {
186229
return;
@@ -334,13 +377,18 @@ filterTests([ 'aesgcm128', 'aesgcm', 'aes128gcm' ])
334377
filterTests([ useExplicitKey,
335378
authenticationSecret,
336379
exactlyOneRecord,
380+
padTinyRecord,
337381
detectTruncation,
338382
useKeyId,
339383
useDH,
340384
checkExamples,
341385
])
342386
.forEach(function(test) {
343387
log(version + ' Test: ' + test.name);
388+
saveDump = data => {
389+
data.test = test.name + ' ' + version;
390+
reallySaveDump(data);
391+
};
344392
test(version);
345393
log('----- OK');
346394
});

python/http_ece/tests/test_ece.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,13 @@ def _run(self, mode):
475475
outp = 'input'
476476

477477
for data in self.legacy_data:
478+
logmsg('%s: %s' % (mode, data['test']))
478479
p = data['params'][mode]
480+
481+
if 'pad' in p and mode == 'encrypt':
482+
# This library doesn't pad in exactly the same way.
483+
continue
484+
479485
if 'keys' in data:
480486
key = None
481487
private_key = pyelliptic.ECC(

0 commit comments

Comments
 (0)