Skip to content

Commit 6edd8c5

Browse files
close input stream
1 parent 897420e commit 6edd8c5

File tree

2 files changed

+158
-108
lines changed

2 files changed

+158
-108
lines changed

src/test/java/org/cryptomator/siv/EncryptionTestCase.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,9 @@ public byte[][] getAssociatedData() {
8282
public byte[] getCiphertext() {
8383
return Arrays.copyOf(ciphertext, ciphertext.length);
8484
}
85+
86+
@Override
87+
public String toString() {
88+
return "TestCase #" + testCaseNumber;
89+
}
8590
}

src/test/java/org/cryptomator/siv/SivEngineTest.java

Lines changed: 153 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package org.cryptomator.siv;
22

33
import org.junit.jupiter.api.Assertions;
4+
import org.junit.jupiter.api.Assumptions;
5+
import org.junit.jupiter.api.BeforeAll;
6+
import org.junit.jupiter.api.DisplayName;
47
import org.junit.jupiter.api.DynamicContainer;
58
import org.junit.jupiter.api.DynamicTest;
69
import org.junit.jupiter.api.Nested;
710
import org.junit.jupiter.api.Test;
811
import org.junit.jupiter.api.TestFactory;
12+
import org.junit.jupiter.api.TestInstance;
913
import org.junit.jupiter.params.ParameterizedTest;
14+
import org.junit.jupiter.params.provider.FieldSource;
1015
import org.junit.jupiter.params.provider.ValueSource;
1116

1217
import javax.crypto.AEADBadTagException;
@@ -16,9 +21,10 @@
1621
import java.io.InputStream;
1722
import java.io.InputStreamReader;
1823
import java.io.Reader;
19-
import java.io.UncheckedIOException;
2024
import java.nio.charset.StandardCharsets;
2125
import java.util.Arrays;
26+
import java.util.List;
27+
import java.util.stream.Collectors;
2228
import java.util.stream.Stream;
2329

2430
/**
@@ -293,115 +299,154 @@ public void testSivDecrypt() throws AEADBadTagException, IllegalBlockSizeExcepti
293299

294300
}
295301

296-
@TestFactory
297-
public Stream<DynamicContainer> testGeneratedTestCases() {
298-
InputStream in = EncryptionTestCase.class.getResourceAsStream("/testcases.txt");
299-
Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII);
300-
BufferedReader bufferedReader = new BufferedReader(reader);
301-
Stream<String> lines = bufferedReader.lines().onClose(() -> {
302-
try {
303-
bufferedReader.close();
304-
} catch (IOException e) {
305-
throw new UncheckedIOException(e);
302+
@Nested
303+
@DisplayName("Generated Test Cases")
304+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
305+
public class GeneratedTestCases {
306+
307+
private List<EncryptionTestCase> testCases;
308+
309+
@BeforeAll
310+
public void loadTestCases() throws IOException {
311+
try (InputStream in = EncryptionTestCase.class.getResourceAsStream("/testcases.txt");
312+
Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII);
313+
BufferedReader bufferedReader = new BufferedReader(reader)) {
314+
this.testCases = bufferedReader.lines().map(EncryptionTestCase::fromLine).collect(Collectors.toList());
306315
}
307-
});
308-
return lines.map(EncryptionTestCase::fromLine).map(testCase -> {
309-
int testIdx = testCase.getTestCaseNumber();
316+
}
317+
318+
@DisplayName("decrypt")
319+
@ParameterizedTest(name = "{0}")
320+
@FieldSource("testCases")
321+
public void testDecrypt(EncryptionTestCase testCase) throws AEADBadTagException, IllegalBlockSizeException {
322+
SivEngine siv = new SivEngine(testCase.getKey());
323+
byte[] actualPlaintext = siv.decrypt(testCase.getCiphertext(), testCase.getAssociatedData());
324+
Assertions.assertArrayEquals(testCase.getPlaintext(), actualPlaintext);
325+
}
326+
327+
@DisplayName("encrypt")
328+
@ParameterizedTest(name = "{0}")
329+
@FieldSource("testCases")
330+
public void testEncrypt(EncryptionTestCase testCase) {
331+
SivEngine siv = new SivEngine(testCase.getKey());
332+
byte[] actualCiphertext = siv.encrypt(testCase.getPlaintext(), testCase.getAssociatedData());
333+
Assertions.assertArrayEquals(testCase.getCiphertext(), actualCiphertext);
334+
}
335+
336+
@DisplayName("decrypt fails due to tampered mac key")
337+
@ParameterizedTest(name = "{0}")
338+
@FieldSource("testCases")
339+
public void testDecryptFailsDueToTamperedMacKey(EncryptionTestCase testCase) {
340+
byte[] key = testCase.getKey();
341+
342+
// Pick some arbitrary byte from first half of key (i.e. the MAC key) to tamper with
343+
int halfKeyLen = key.length / 2;
344+
int tamperedByteIndex = testCase.getTestCaseNumber() % halfKeyLen;
345+
346+
// Flip a single bit
347+
key[tamperedByteIndex] ^= 0x10;
348+
349+
SivEngine sivWithTamperedKey = new SivEngine(key);
350+
351+
Assertions.assertThrows(AEADBadTagException.class, () -> {
352+
sivWithTamperedKey.decrypt(testCase.getCiphertext(), testCase.getAssociatedData());
353+
});
354+
}
355+
356+
@DisplayName("decrypt fails due to tampered ciphertext")
357+
@ParameterizedTest(name = "{0}")
358+
@FieldSource("testCases")
359+
public void testDecryptFailsDueToTamperedCiphertext(EncryptionTestCase testCase) {
360+
SivEngine siv = new SivEngine(testCase.getKey());
361+
byte[] ciphertext = testCase.getCiphertext();
362+
363+
// Pick some arbitrary key byte to tamper with
364+
int tamperedByteIndex = testCase.getTestCaseNumber() % ciphertext.length;
365+
366+
// Flip a single bit
367+
ciphertext[tamperedByteIndex] ^= 0x10;
368+
369+
Assertions.assertThrows(AEADBadTagException.class, () -> {
370+
siv.decrypt(ciphertext, testCase.getAssociatedData());
371+
});
372+
}
373+
374+
@DisplayName("decrypt fails due to tampered associated data")
375+
@ParameterizedTest(name = "{0}")
376+
@FieldSource("testCases")
377+
public void testDecryptFailsDueToTamperedAAD(EncryptionTestCase testCase) {
378+
// Skip if there is no AD
379+
if (testCase.getAssociatedData().length == 0) {
380+
return;
381+
}
382+
310383
SivEngine siv = new SivEngine(testCase.getKey());
311-
return DynamicContainer.dynamicContainer("test case " + testIdx, Arrays.asList(
312-
DynamicTest.dynamicTest("decrypt", () -> {
313-
byte[] actualPlaintext = siv.decrypt(testCase.getCiphertext(), testCase.getAssociatedData());
314-
Assertions.assertArrayEquals(testCase.getPlaintext(), actualPlaintext);
315-
}),
316-
DynamicTest.dynamicTest("encrypt", () -> {
317-
byte[] actualCiphertext = siv.encrypt(testCase.getPlaintext(), testCase.getAssociatedData());
318-
Assertions.assertArrayEquals(testCase.getCiphertext(), actualCiphertext);
319-
}),
320-
DynamicTest.dynamicTest("decrypt fails due to tampered mac key", () -> {
321-
byte[] key = testCase.getKey();
322-
323-
// Pick some arbitrary byte from first half of key (i.e. the MAC key) to tamper with
324-
int halfKeyLen = key.length / 2;
325-
int tamperedByteIndex = testIdx % halfKeyLen;
326-
327-
// Flip a single bit
328-
key[tamperedByteIndex] ^= 0x10;
329-
330-
SivEngine sivWithTamperedKey = new SivEngine(key);
331-
332-
Assertions.assertThrows(AEADBadTagException.class, () -> {
333-
sivWithTamperedKey.decrypt(testCase.getCiphertext(), testCase.getAssociatedData());
334-
});
335-
}),
336-
DynamicTest.dynamicTest("decrypt fails due to tampered ciphertext", () -> {
337-
byte[] ciphertext = testCase.getCiphertext();
338-
339-
// Pick some arbitrary key byte to tamper with
340-
int tamperedByteIndex = testIdx % ciphertext.length;
341-
342-
// Flip a single bit
343-
ciphertext[tamperedByteIndex] ^= 0x10;
344-
345-
Assertions.assertThrows(AEADBadTagException.class, () -> {
346-
siv.decrypt(ciphertext, testCase.getAssociatedData());
347-
});
348-
}),
349-
DynamicTest.dynamicTest("decrypt fails due to tampered associated data", () -> {
350-
byte[][] ad = testCase.getAssociatedData();
351-
352-
// Try flipping bits in the associated data elements
353-
for (int adIdx = 0; adIdx < ad.length; adIdx++) {
354-
// Skip if this ad element is empty
355-
if (ad[adIdx].length == 0) {
356-
continue;
357-
}
358-
359-
// Pick some arbitrary byte to tamper with
360-
int tamperedByteIndex = testIdx % ad[adIdx].length;
361-
362-
// Flip a single bit
363-
ad[adIdx][tamperedByteIndex] ^= 0x04;
364-
365-
Assertions.assertThrows(AEADBadTagException.class, () -> {
366-
siv.decrypt(testCase.getCiphertext(), ad);
367-
});
368-
369-
// Restore ad to original value
370-
ad[adIdx][tamperedByteIndex] ^= 0x04;
371-
}
372-
}),
373-
DynamicTest.dynamicTest("decrypt fails due to prepended associated data", () -> {
374-
// Skip if there is no more room for additional AD
375-
if (testCase.getAssociatedData().length > 125) {
376-
return;
377-
}
378-
379-
byte[][] ad = testCase.getAssociatedData();
380-
byte[][] prependedAd = new byte[ad.length + 1][];
381-
prependedAd[0] = new byte[testIdx % 16];
382-
System.arraycopy(ad, 0, prependedAd, 1, ad.length);
383-
384-
Assertions.assertThrows(AEADBadTagException.class, () -> {
385-
siv.decrypt(testCase.getCiphertext(), prependedAd);
386-
});
387-
}),
388-
DynamicTest.dynamicTest("decrypt fails due to appended associated data", () -> {
389-
// Skip if there is no more room for additional AD
390-
if (testCase.getAssociatedData().length > 125) {
391-
return;
392-
}
393-
394-
byte[][] ad = testCase.getAssociatedData();
395-
byte[][] appendedAd = new byte[ad.length + 1][];
396-
appendedAd[ad.length] = new byte[testIdx % 16];
397-
System.arraycopy(ad, 0, appendedAd, 0, ad.length);
398-
399-
Assertions.assertThrows(AEADBadTagException.class, () -> {
400-
siv.decrypt(testCase.getCiphertext(), appendedAd);
401-
});
402-
})
403-
));
404-
});
384+
byte[][] ad = testCase.getAssociatedData();
385+
386+
// Try flipping bits in the associated data elements
387+
for (int adIdx = 0; adIdx < ad.length; adIdx++) {
388+
// Skip if this ad element is empty
389+
if (ad[adIdx].length == 0) {
390+
continue;
391+
}
392+
393+
// Pick some arbitrary byte to tamper with
394+
int tamperedByteIndex = testCase.getTestCaseNumber() % ad[adIdx].length;
395+
396+
// Flip a single bit
397+
ad[adIdx][tamperedByteIndex] ^= 0x04;
398+
399+
Assertions.assertThrows(AEADBadTagException.class, () -> {
400+
siv.decrypt(testCase.getCiphertext(), ad);
401+
});
402+
403+
// Restore ad to original value
404+
ad[adIdx][tamperedByteIndex] ^= 0x04;
405+
}
406+
}
407+
408+
@DisplayName("decrypt fails due to prepended associated data")
409+
@ParameterizedTest(name = "{0}")
410+
@FieldSource("testCases")
411+
public void testDecryptFailsDueToPrependedAAD(EncryptionTestCase testCase) {
412+
// Skip if there is no more room for additional AD
413+
if (testCase.getAssociatedData().length > 125) {
414+
return;
415+
}
416+
417+
SivEngine siv = new SivEngine(testCase.getKey());
418+
419+
byte[][] ad = testCase.getAssociatedData();
420+
byte[][] prependedAd = new byte[ad.length + 1][];
421+
prependedAd[0] = new byte[testCase.getTestCaseNumber() % 16];
422+
System.arraycopy(ad, 0, prependedAd, 1, ad.length);
423+
424+
Assertions.assertThrows(AEADBadTagException.class, () -> {
425+
siv.decrypt(testCase.getCiphertext(), prependedAd);
426+
});
427+
}
428+
429+
@DisplayName("decrypt fails due to appended associated data")
430+
@ParameterizedTest(name = "{0}")
431+
@FieldSource("testCases")
432+
public void testDecryptFailsDueToAppendedAAD(EncryptionTestCase testCase) {
433+
// Skip if there is no more room for additional AD
434+
if (testCase.getAssociatedData().length > 125) {
435+
return;
436+
}
437+
438+
SivEngine siv = new SivEngine(testCase.getKey());
439+
440+
byte[][] ad = testCase.getAssociatedData();
441+
byte[][] appendedAd = new byte[ad.length + 1][];
442+
appendedAd[ad.length] = new byte[testCase.getTestCaseNumber() % 16];
443+
System.arraycopy(ad, 0, appendedAd, 0, ad.length);
444+
445+
Assertions.assertThrows(AEADBadTagException.class, () -> {
446+
siv.decrypt(testCase.getCiphertext(), appendedAd);
447+
});
448+
}
449+
405450
}
406451

407452
}

0 commit comments

Comments
 (0)